@stacksjs/actions 0.75.177 → 0.75.179
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 +14 -1
- package/dist/build/android-config.js +1 -1
- package/dist/build/android.js +1 -1
- package/dist/build/ios-config.d.ts +13 -1
- package/dist/build/ios-config.js +1 -1
- package/dist/build/ios-shell.d.ts +14 -2
- package/dist/build/ios-shell.js +26 -2
- package/dist/build/ios.js +1 -1
- package/dist/github-release.js +2 -2
- package/package.json +40 -39
|
@@ -5,6 +5,15 @@ export declare function androidCraftFeatures(config: AndroidMobileConfig, option
|
|
|
5
5
|
export declare function assertCraftSupportsAndroid(config: AndroidMobileConfig, version: string, source: string, options?: { appBundle?: boolean }): void;
|
|
6
6
|
export declare function toCraftAndroidConfig(config: AndroidMobileConfig): CraftAndroidConfig;
|
|
7
7
|
export declare function validateAndroidMobileConfig(config: AndroidMobileConfig): void;
|
|
8
|
+
/** The release an Android build's events name, and so the one its R8 mapping is stored under. */
|
|
9
|
+
export declare function androidRelease(config: Pick<AndroidMobileConfig, 'version' | 'versionCode' | 'crashReporting'>): string;
|
|
10
|
+
/**
|
|
11
|
+
* Upload a release build's R8 mapping to bughq, so its obfuscated crash
|
|
12
|
+
* frames retrace. Needs the secret `BUGHQ_UPLOAD_TOKEN` in the build's
|
|
13
|
+
* environment. Answers what happened, and never throws: a missing upload must
|
|
14
|
+
* not fail a release build.
|
|
15
|
+
*/
|
|
16
|
+
export declare function uploadAndroidMapping(mappingPath: string, host: string, release: string, token: string | undefined, send?: typeof fetch): Promise<{ ok: boolean, message: string }>;
|
|
8
17
|
/**
|
|
9
18
|
* The first craft-native release whose Android template honours a feature
|
|
10
19
|
* of `config/mobile.ts`'s android section, the Android sibling of
|
|
@@ -16,12 +25,15 @@ export declare function validateAndroidMobileConfig(config: AndroidMobileConfig)
|
|
|
16
25
|
export declare const CRAFT_ANDROID_FLOORS: {
|
|
17
26
|
/** The native app shell: tabs, paths to screens, the app script, synchronous stores. */
|
|
18
27
|
nativeShell: '0.0.169';
|
|
28
|
+
bluetooth: '0.0.178';
|
|
19
29
|
/** Health Connect for native screens (workouts, sleep, heart rate, mindful, nutrition). */
|
|
20
30
|
healthConnect: '0.0.169';
|
|
21
31
|
/** WorkManager background refresh for the app script. */
|
|
22
32
|
backgroundRefresh: '0.0.169';
|
|
23
33
|
/** `bundleRelease` (an AAB for Google Play) and release signing from the environment. */
|
|
24
|
-
appBundle: '0.0.169'
|
|
34
|
+
appBundle: '0.0.169';
|
|
35
|
+
/** CraftCrashReporter.kt: uncaught exceptions, ANRs, JavaScript errors, the outbox. */
|
|
36
|
+
crashReporting: '0.0.176'
|
|
25
37
|
};
|
|
26
38
|
export declare interface CraftAndroidConfig {
|
|
27
39
|
[key: string]: unknown
|
|
@@ -40,6 +52,7 @@ export declare interface CraftAndroidConfig {
|
|
|
40
52
|
urlSchemes?: string[]
|
|
41
53
|
appIconPath?: string
|
|
42
54
|
googleServicesFile?: string
|
|
55
|
+
enableBluetooth?: boolean
|
|
43
56
|
enableHealthConnect?: boolean
|
|
44
57
|
renderer?: AndroidMobileConfig['renderer']
|
|
45
58
|
nativeScreens?: Record<string, string>
|
|
@@ -1 +1 @@
|
|
|
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}`)}
|
|
1
|
+
import{NATIVE_APP_ROUTE,normalizeMobileUrl,toCraftCrashReporting}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",bluetooth:"enableBluetooth",localNetwork:"enableLocalNetwork"};export const CRAFT_ANDROID_FLOORS={nativeShell:"0.0.169",bluetooth:"0.0.178",healthConnect:"0.0.169",backgroundRefresh:"0.0.169",appBundle:"0.0.169",crashReporting:"0.0.176"};export function androidCraftFeatures(config,options={}){const features=[];if(config.renderer==="native"&&Object.keys(config.nativeScreens??{}).length)features.push("nativeShell");if(config.capabilities?.bluetooth)features.push("bluetooth");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");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,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.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}`)}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)}`}}}
|
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,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()}`),
|
|
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{androidRelease,assertCraftSupportsAndroid,toCraftAndroidConfig,uploadAndroidMapping,validateAndroidMobileConfig}from"./android-config";import{resolveCrashReporting,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()}`),androidSection=configModule.default.android;if(!androidSection)throw Error("config/mobile.ts must define an android section");const config={...androidSection,crashReporting:resolveCrashReporting(configModule.default.crashReporting,androidSection.crashReporting)};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"});if(release&&config.crashReporting?.key&&config.crashReporting.uploadSymbols!==!1&&process.env.STACKS_ANDROID_SKIP_GRADLE!=="1"){const upload=await uploadAndroidMapping(`${output}/app/build/outputs/mapping/release/mapping.txt`,config.crashReporting.host??"https://bughq.org",androidRelease(config),process.env.BUGHQ_UPLOAD_TOKEN);if(upload.ok)log.info(upload.message);else log.warn(upload.message)}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;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IosMobileConfig, MobileConfig } from '@stacksjs/types';
|
|
1
|
+
import type { IosMobileConfig, MobileConfig, MobileCrashReportingConfig } from '@stacksjs/types';
|
|
2
2
|
/** The capabilities `config/mobile.ts` turns on that a Craft of `version` would leave out of the app. */
|
|
3
3
|
export declare function unsupportedCraftCapabilities(capabilities: IosMobileConfig['capabilities'], version: string): UnsupportedCraftCapability[];
|
|
4
4
|
/**
|
|
@@ -9,6 +9,16 @@ export declare function unsupportedCraftCapabilities(capabilities: IosMobileConf
|
|
|
9
9
|
* lockfile.
|
|
10
10
|
*/
|
|
11
11
|
export declare function assertCraftSupportsCapabilities(capabilities: IosMobileConfig['capabilities'], version: string, source: string): void;
|
|
12
|
+
/**
|
|
13
|
+
* The crash reporting a platform builds with: the shared `crashReporting`,
|
|
14
|
+
* overridden field by field by the platform's own. Undefined, and so off,
|
|
15
|
+
* without a key.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveCrashReporting(shared: MobileCrashReportingConfig | undefined, platform: MobileCrashReportingConfig | undefined): MobileCrashReportingConfig | undefined;
|
|
18
|
+
/** What craft.config.json carries: the runtime settings, not the build-only ones. */
|
|
19
|
+
export declare function toCraftCrashReporting(config: MobileCrashReportingConfig | undefined): Record<string, unknown> | undefined;
|
|
20
|
+
/** Refuse a build whose Craft would silently leave crash reporting out. */
|
|
21
|
+
export declare function assertCraftSupportsCrashReporting(config: MobileCrashReportingConfig | undefined, version: string, source: string): void;
|
|
12
22
|
export declare function normalizeMobileUrl(value: string | undefined): string | undefined;
|
|
13
23
|
/**
|
|
14
24
|
* The generated project's records (craft.config.json, stacks-mobile.json)
|
|
@@ -97,6 +107,8 @@ declare const CAPABILITY_KEYS: {
|
|
|
97
107
|
* accepted are not listed.
|
|
98
108
|
*/
|
|
99
109
|
export declare const CRAFT_CAPABILITY_FLOORS: Partial<Record<keyof typeof CAPABILITY_KEYS, string>>;
|
|
110
|
+
/** The first craft-native whose iOS and Android shells report crashes (CraftCrashReporter). */
|
|
111
|
+
export declare const CRAFT_CRASH_REPORTING_FLOOR: '0.0.176';
|
|
100
112
|
export declare interface CraftIosConfig {
|
|
101
113
|
[key: string]: unknown
|
|
102
114
|
appName: string
|
package/dist/build/ios-config.js
CHANGED
|
@@ -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";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 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)$/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={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,trustedOrigins:[...trustedOrigins],appBoundDomains:config.appBoundDomains,limitNavigationsToAppBoundDomains:config.limitNavigationsToAppBoundDomains??(declaresAppBoundDomains||void 0),associatedDomains:config.associatedDomains,renderer:config.renderer,nativeScreens:config.nativeScreens,nativeApp:config.nativeApp?NATIVE_APP_ROUTE:void 0,tabs:config.tabs,shareStorage:config.shareStorage,appGroups:config.appGroups,appIconPath:config.appIcon,privacy:config.privacy,usageDescriptions:config.usageDescriptions,orientations:config.orientations,deviceFamilies:config.deviceFamilies};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";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.176";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)$/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={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,trustedOrigins:[...trustedOrigins],appBoundDomains:config.appBoundDomains,limitNavigationsToAppBoundDomains:config.limitNavigationsToAppBoundDomains??(declaresAppBoundDomains||void 0),associatedDomains:config.associatedDomains,renderer:config.renderer,nativeScreens:config.nativeScreens,nativeApp:config.nativeApp?NATIVE_APP_ROUTE:void 0,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}
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
-
import type { IosMobileConfig } from '@stacksjs/types';
|
|
1
|
+
import type { IosMobileConfig, MobileCrashReportingConfig } from '@stacksjs/types';
|
|
2
2
|
/** Keep native source package-owned while retaining Xcode metadata for cloud project discovery. */
|
|
3
|
-
export declare function writeIosShellSupport(output: string, config: IosMobileConfig, bunVersion?: unknown): void;
|
|
3
|
+
export declare function writeIosShellSupport(output: string, config: IosMobileConfig, bunVersion?: unknown, crashReporting?: MobileCrashReportingConfig): void;
|
|
4
|
+
/**
|
|
5
|
+
* `ci_post_xcodebuild.sh`: after Xcode Cloud archives the app, upload its
|
|
6
|
+
* dSYMs to bughq so the build's crashes symbolicate.
|
|
7
|
+
*
|
|
8
|
+
* bughq runs on Linux, which can neither read a dSYM's DWARF in Apple's
|
|
9
|
+
* layout nor demangle Swift, so the Mac that built the app reduces each dSYM
|
|
10
|
+
* with `/usr/bin/symbols` (every function and line range, demangled) and
|
|
11
|
+
* uploads that, gzipped. The upload token is a secret: an Xcode Cloud
|
|
12
|
+
* environment variable, never the app's config. Without one the script says
|
|
13
|
+
* so and exits 0, because a missing upload must not fail a release.
|
|
14
|
+
*/
|
|
15
|
+
export declare function renderDsymUpload(host: string): string;
|
package/dist/build/ios-shell.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{chmodSync,mkdirSync,writeFileSync}from"node:fs";import{join}from"node:path";export function writeIosShellSupport(output,config,bunVersion=Bun.version){const version=config.shell?.bunVersion??bunVersion;if(!/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/.test(version))throw Error("ios.shell.bunVersion must be a Bun version such as 1.3.14");writeFileSync(join(output,".gitignore"),`# Native sources are restored from the locked Craft package.
|
|
1
|
+
import{chmodSync,mkdirSync,rmSync,writeFileSync}from"node:fs";import{join}from"node:path";export function writeIosShellSupport(output,config,bunVersion=Bun.version,crashReporting){const version=config.shell?.bunVersion??bunVersion;if(!/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/.test(version))throw Error("ios.shell.bunVersion must be a Bun version such as 1.3.14");writeFileSync(join(output,".gitignore"),`# Native sources are restored from the locked Craft package.
|
|
2
2
|
*.swift
|
|
3
3
|
`);const scripts=join(output,"ci_scripts");mkdirSync(scripts,{recursive:!0});writeFileSync(join(scripts,"bunfig.toml"),`preload = []
|
|
4
4
|
`);const path=join(scripts,"ci_post_clone.sh");writeFileSync(path,`#!/bin/sh
|
|
@@ -13,4 +13,28 @@ fi
|
|
|
13
13
|
cd "$ROOT"
|
|
14
14
|
bun install --frozen-lockfile
|
|
15
15
|
bun --config="$PROJECT_DIR/ci_scripts/bunfig.toml" -e 'import { restoreIosShell } from "@stacksjs/mobile/testing"; restoreIosShell(process.argv[1])' "$PROJECT_DIR"
|
|
16
|
-
`);chmodSync(path,493)}
|
|
16
|
+
`);chmodSync(path,493);writeDsymUpload(scripts,crashReporting)}export function renderDsymUpload(host){return`#!/bin/sh
|
|
17
|
+
# Generated by buddy build:ios from config/mobile.ts crashReporting.
|
|
18
|
+
set -u
|
|
19
|
+
[ "\${CI_XCODEBUILD_ACTION:-}" = "archive" ] || exit 0
|
|
20
|
+
if [ -z "\${BUGHQ_UPLOAD_TOKEN:-}" ]; then
|
|
21
|
+
echo "bughq: BUGHQ_UPLOAD_TOKEN is not set in this workflow, so no dSYMs were uploaded and this build's crashes will show addresses."
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
HOST="\${BUGHQ_HOST:-${host}}"
|
|
25
|
+
for DSYM in "\${CI_ARCHIVE_PATH}"/dSYMs/*.dSYM; do
|
|
26
|
+
[ -d "$DSYM" ] || continue
|
|
27
|
+
OUT="\${TMPDIR:-/tmp}/bughq-$(basename "$DSYM").symbols.gz"
|
|
28
|
+
if symbols -arch arm64 "$DSYM" 2>/dev/null | gzip -c > "$OUT"; then
|
|
29
|
+
if curl -fsS --retry 3 -X POST "$HOST/api/debug-files?kind=apple" \\
|
|
30
|
+
-H "Authorization: Bearer $BUGHQ_UPLOAD_TOKEN" -H "Content-Type: application/octet-stream" \\
|
|
31
|
+
--data-binary @"$OUT"; then
|
|
32
|
+
echo "bughq: uploaded $(basename "$DSYM")"
|
|
33
|
+
else
|
|
34
|
+
echo "bughq: could not upload $(basename "$DSYM")"
|
|
35
|
+
fi
|
|
36
|
+
fi
|
|
37
|
+
rm -f "$OUT"
|
|
38
|
+
done
|
|
39
|
+
exit 0
|
|
40
|
+
`}function writeDsymUpload(scripts,crashReporting){const path=join(scripts,"ci_post_xcodebuild.sh");if(!crashReporting?.key||crashReporting.uploadSymbols===!1){rmSync(path,{force:!0});return}writeFileSync(path,renderDsymUpload(crashReporting.host??"https://bughq.org"));chmodSync(path,493)}
|
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{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)}
|
|
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,assertCraftSupportsCrashReporting,portablePaths,resolveCrashReporting,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),crashReporting=resolveCrashReporting(mobile.crashReporting,config.crashReporting),craftConfig=toCraftIosConfig({...config,crashReporting});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);assertCraftSupportsCrashReporting(crashReporting,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,void 0,crashReporting);if(crashReporting)log.info(`Crash reporting to ${crashReporting.host??"https://bughq.org"}${crashReporting.uploadSymbols===!1?"":": dSYMs upload from Xcode Cloud when BUGHQ_UPLOAD_TOKEN is set in the workflow"}`);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;
|
package/dist/github-release.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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
|
|
1
|
+
import{githubHeaders}from"@stacksjs/github";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
2
|
host=github.com
|
|
3
3
|
|
|
4
4
|
`);if(credential.exitCode===0){const password=credential.stdout.split(`
|
|
@@ -6,6 +6,6 @@ host=github.com
|
|
|
6
6
|
|
|
7
7
|
\u2026the notes were longer than GitHub allows${compareUrl?`; see [every change](${compareUrl})`:""}.
|
|
8
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=
|
|
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=githubHeaders(options.token),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
10
|
|
|
11
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/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.179",
|
|
6
6
|
"description": "The Stacks actions.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -66,53 +66,54 @@
|
|
|
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.179",
|
|
70
|
+
"@stacksjs/ai": "0.75.179",
|
|
71
|
+
"@stacksjs/analytics": "0.75.179",
|
|
72
|
+
"@stacksjs/api": "0.75.179",
|
|
73
|
+
"@stacksjs/auth": "0.75.179",
|
|
74
|
+
"@stacksjs/build": "0.75.179",
|
|
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.179",
|
|
78
|
+
"@stacksjs/cloud": "0.75.179",
|
|
79
|
+
"@stacksjs/cms": "0.75.179",
|
|
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.179",
|
|
82
|
+
"@stacksjs/database": "0.75.179",
|
|
83
|
+
"@stacksjs/desktop-build": "0.75.179",
|
|
84
|
+
"@stacksjs/dns": "0.75.179",
|
|
85
|
+
"@stacksjs/enums": "0.75.179",
|
|
86
|
+
"@stacksjs/env": "0.75.179",
|
|
87
|
+
"@stacksjs/error-handling": "0.75.179",
|
|
88
|
+
"@stacksjs/github": "0.75.179",
|
|
88
89
|
"@stacksjs/gitit": "^0.2.5",
|
|
89
|
-
"@stacksjs/i18n": "0.75.
|
|
90
|
-
"@stacksjs/image": "0.75.
|
|
91
|
-
"@stacksjs/logging": "0.75.
|
|
90
|
+
"@stacksjs/i18n": "0.75.179",
|
|
91
|
+
"@stacksjs/image": "0.75.179",
|
|
92
|
+
"@stacksjs/logging": "0.75.179",
|
|
92
93
|
"@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.
|
|
94
|
+
"@stacksjs/mobile": "0.75.179",
|
|
95
|
+
"@stacksjs/orm": "0.75.179",
|
|
96
|
+
"@stacksjs/path": "0.75.179",
|
|
97
|
+
"@stacksjs/payments": "0.75.179",
|
|
98
|
+
"@stacksjs/queue": "0.75.179",
|
|
99
|
+
"@stacksjs/registry": "0.75.179",
|
|
100
|
+
"@stacksjs/router": "0.75.179",
|
|
100
101
|
"@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.
|
|
102
|
+
"@stacksjs/scheduler": "0.75.179",
|
|
103
|
+
"@stacksjs/search-engine": "0.75.179",
|
|
104
|
+
"@stacksjs/security": "0.75.179",
|
|
105
|
+
"@stacksjs/server": "0.75.179",
|
|
106
|
+
"@stacksjs/sites": "0.75.179",
|
|
107
|
+
"@stacksjs/storage": "0.75.179",
|
|
108
|
+
"@stacksjs/strings": "0.75.179",
|
|
108
109
|
"@stacksjs/stx": "^0.2.483",
|
|
109
110
|
"@stacksjs/tlsx": "^0.13.19",
|
|
110
111
|
"@stacksjs/ts-cloud": "^0.16.52",
|
|
111
112
|
"@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.
|
|
113
|
+
"@stacksjs/tunnel": "0.75.179",
|
|
114
|
+
"@stacksjs/types": "0.75.179",
|
|
115
|
+
"@stacksjs/utils": "0.75.179",
|
|
116
|
+
"@stacksjs/whois": "0.75.179",
|
|
116
117
|
"bun-plugin-stx": "^0.2.483"
|
|
117
118
|
},
|
|
118
119
|
"peerDependencies": {
|
|
@@ -128,7 +129,7 @@
|
|
|
128
129
|
}
|
|
129
130
|
},
|
|
130
131
|
"devDependencies": {
|
|
131
|
-
"@stacksjs/validation": "0.75.
|
|
132
|
+
"@stacksjs/validation": "0.75.179",
|
|
132
133
|
"better-dx": "^0.2.24"
|
|
133
134
|
}
|
|
134
135
|
}
|