@absolutejs/absolute 0.20.0-beta.43 → 0.20.0-beta.44
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/angular/browser.js +378 -361
- package/dist/angular/browser.js.map +2 -2
- package/dist/angular/components/core/streamingSlotRegistrar.js +7 -7
- package/dist/angular/components/core/streamingSlotRegistry.js +4 -4
- package/dist/angular/index.js +300 -294
- package/dist/angular/index.js.map +2 -2
- package/dist/angular/server.js +54 -50
- package/dist/angular/server.js.map +2 -2
- package/dist/build.js +199 -203
- package/dist/build.js.map +2 -2
- package/dist/cli/config/client.js +12 -12
- package/dist/cli/config/server.js +1096 -1090
- package/dist/cli/index.js +275 -161
- package/dist/client/index.js +29 -25
- package/dist/client/index.js.map +2 -2
- package/dist/core/streamingSlotRegistrar.js +15 -13
- package/dist/core/streamingSlotRegistrar.js.map +2 -2
- package/dist/core/streamingSlotRegistry.js +11 -9
- package/dist/core/streamingSlotRegistry.js.map +2 -2
- package/dist/image-client/imageClient.js +7 -7
- package/dist/index.js +782 -732
- package/dist/index.js.map +6 -5
- package/dist/islands/browser.js +11 -9
- package/dist/islands/browser.js.map +2 -2
- package/dist/islands/index.js +39 -35
- package/dist/islands/index.js.map +2 -2
- package/dist/mobile/browser.js +23 -22
- package/dist/mobile/browser.js.map +2 -2
- package/dist/mobile/index.js +552 -423
- package/dist/mobile/index.js.map +9 -9
- package/dist/mobile/remoteMacAgentEntry.js +115 -37
- package/dist/mobile/shellBootstrap.js +0 -1
- package/dist/mobile/shellExpoAuth.js +184 -0
- package/dist/mobile/shellExpoDevices.js +4 -5
- package/dist/mobile/shellSync.js +4 -4
- package/dist/react/browser.js +17 -15
- package/dist/react/browser.js.map +2 -2
- package/dist/react/components/browser/index.js +107 -107
- package/dist/react/components/index.js +118 -116
- package/dist/react/components/index.js.map +2 -2
- package/dist/react/hooks/index.js +11 -9
- package/dist/react/hooks/index.js.map +2 -2
- package/dist/react/index.js +53 -49
- package/dist/react/index.js.map +2 -2
- package/dist/react/jsxDevRuntimeCompat.js +11 -9
- package/dist/react/jsxDevRuntimeCompat.js.map +2 -2
- package/dist/react/jsxRuntimeCompat.js +11 -9
- package/dist/react/jsxRuntimeCompat.js.map +2 -2
- package/dist/react/router/browser.js +9 -7
- package/dist/react/router/browser.js.map +2 -2
- package/dist/react/router/index.js +9 -7
- package/dist/react/router/index.js.map +2 -2
- package/dist/react/server.js +12 -10
- package/dist/react/server.js.map +2 -2
- package/dist/src/mobile/devDeviceAdapter.d.ts +1 -1
- package/dist/src/mobile/expoBridge.d.ts +12 -12
- package/dist/src/mobile/nativeAuth.d.ts +3 -0
- package/dist/src/mobile/shellExpoAuth.d.ts +12 -0
- package/dist/svelte/browser.js +12 -10
- package/dist/svelte/browser.js.map +2 -2
- package/dist/svelte/index.js +47 -43
- package/dist/svelte/index.js.map +2 -2
- package/dist/svelte/server.js +29 -27
- package/dist/svelte/server.js.map +2 -2
- package/dist/vue/browser.js +14 -12
- package/dist/vue/browser.js.map +2 -2
- package/dist/vue/components/Image.js +9 -7
- package/dist/vue/components/Image.js.map +2 -2
- package/dist/vue/components/index.js +11 -9
- package/dist/vue/components/index.js.map +2 -2
- package/dist/vue/index.js +52 -48
- package/dist/vue/index.js.map +2 -2
- package/dist/vue/server.js +14 -12
- package/dist/vue/server.js.map +2 -2
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{mkdir as
|
|
3
|
-
`);while(
|
|
4
|
-
`)}),
|
|
5
|
-
`;var
|
|
2
|
+
import{mkdir as Jo,readFile as Vo,rm as Te,writeFile as zo}from"fs/promises";import{join as ue}from"path";import{createInterface as _t}from"readline";import{resolve as Me}from"path";var Tt=/^[A-Za-z][\w]*(?:\.[A-Za-z][\w]*)+$/,Pt=/^[a-z][a-z0-9+.-]*$/,Dt=/^[A-Z0-9]{10}$/,Ct=/^[0-9A-F]{64}$/,Rt=/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))*$/,te=(e,t,r)=>{let o=Me(e),n=Me(o,t);if(n!==o&&!n.startsWith(`${o}/`))throw TypeError(`${r} must remain inside the project root.`);return n},R=(e,t)=>{let r=e.trim();if(!r)throw TypeError(`${t} must not be empty.`);return r},Le=(e)=>{let t=R(e??"/","mobile.entry");if(!t.startsWith("/")||t.startsWith("//"))throw TypeError("mobile.entry must be an absolute application path.");return t},Nt=(e)=>{let t=new URL(R(e,"mobile.server.productionOrigin")),r=t.protocol==="http:"&&(t.hostname==="localhost"||t.hostname==="127.0.0.1"||t.hostname==="[::1]");if(t.protocol!=="https:"&&!r)throw TypeError("mobile.server.productionOrigin must use HTTPS, except for a loopback development origin.");if(t.username||t.password||t.pathname!=="/"||t.search||t.hash)throw TypeError("mobile.server.productionOrigin must be an origin without credentials, path, query, or hash.");return t.origin},Mt=(e)=>{let t=[...new Set(e??["ios","android"])];if(t.length===0)throw TypeError("mobile.platforms must contain at least one platform.");return t},Lt=(e,t)=>{let r=(s)=>{let i=R(s,"mobile.deepLinks.hosts").toLowerCase();if(!Rt.test(i))throw TypeError("mobile.deepLinks.hosts entries must be valid hostnames without ports, wildcards, or paths.");return i},o=new URL(t).hostname,n=new Set(o==="[::1]"?[]:[r(o)]);for(let s of e??[])n.add(r(s));return[...n].sort()},Ut=(e)=>{if(e===void 0)return;let t=R(e,"mobile.deepLinks.apple.appIdPrefix").toUpperCase();if(!Dt.test(t))throw TypeError("mobile.deepLinks.apple.appIdPrefix must contain ten letters or digits.");return t},$t=(e)=>{if(e===void 0)return;let t=R(e,"mobile.ios.version");if(!/^\d+(?:\.\d+){0,2}$/u.test(t))throw TypeError("mobile.ios.version must contain one to three dot-separated integer components, for example 1.4.0.");return t},kt=(e)=>[...new Set((e??[]).map((t)=>R(t,"mobile.deepLinks.android.sha256CertificateFingerprints").replaceAll(":","").toUpperCase()).map((t)=>{if(!Ct.test(t))throw TypeError("mobile.deepLinks.android.sha256CertificateFingerprints entries must be SHA-256 certificate fingerprints.");return t.match(/.{2}/g)?.join(":")??t}))].sort(),jt=(e,t)=>{if(e.engine!=="expo")return{};let r=e.routes?.native??{},o={};for(let[n,s]of Object.entries(r)){let i=Le(n);if(i.includes("?")||i.includes("#")||i!=="/"&&i.endsWith("/"))throw TypeError(`mobile.routes.native route ${i} must be a canonical path without a query, fragment, or trailing slash.`);if(i==="/__absolute/native")throw TypeError("mobile.routes.native reserves /__absolute/native for the Expo diagnostic screen.");if(i.includes("*")||i.includes(":"))throw TypeError(`mobile.routes.native route ${i} must be static during the Expo experiment; parameters and wildcards are not supported yet.`);o[i]=te(t,R(s,`mobile.routes.native[${i}]`),`mobile.routes.native[${i}]`)}return Object.fromEntries(Object.entries(o).sort(([n],[s])=>n.localeCompare(s)))},Ue=(e,t)=>{let r=R(e.appId,"mobile.appId");if(!Tt.test(r))throw TypeError("mobile.appId must use reverse-domain notation, for example com.example.app.");let o=Nt(e.server.productionOrigin),n=(e.deepLinks?.scheme??r).trim().toLowerCase();if(n&&!Pt.test(n))throw TypeError("mobile.deepLinks.scheme is not a valid URL scheme.");return{androidCertificateFingerprints:kt(e.deepLinks?.android?.sha256CertificateFingerprints),appId:r,appleAppIdPrefix:Ut(e.deepLinks?.apple?.appIdPrefix),appName:R(e.appName,"mobile.appName"),bundleDirectory:te(t,e.bundleDirectory??".absolutejs/mobile/web","mobile.bundleDirectory"),deepLinkHosts:Lt(e.deepLinks?.hosts,o),deepLinkScheme:n,engine:e.engine??"capacitor",entry:Le(e.entry),expoNativeRoutes:jt(e,t),...e.engine==="expo"?{expoSdkVersion:e.expo?.sdkVersion??57}:{},iosVersion:$t(e.ios?.version),nativeProjectDirectory:te(t,e.nativeProject?.directory??(e.engine==="expo"?".absolutejs/mobile/expo":"mobile"),"mobile.nativeProject.directory"),platforms:Mt(e.platforms),productionOrigin:o,pushAndroidGoogleServicesFile:te(t,e.pushNotifications?.android?.googleServicesFile??"google-services.json","mobile.pushNotifications.android.googleServicesFile")}};import{spawn as Xt}from"child_process";import{access as qt}from"fs/promises";import{join as Kt}from"path";import{randomUUID as Bt,X509Certificate as Ht}from"crypto";import{createServer as Ft}from"http";import{connect as Wt,createServer as $e,isIP as Gt}from"net";import{readFile as Jt}from"fs/promises";var ge=(e)=>new Promise((t,r)=>{e.close((o)=>{if(o)r(o);else t()})}),ke=(e,t)=>new Promise((r,o)=>{e.once("error",o),e.listen(t,"0.0.0.0",()=>{e.off("error",o);let n=e.address();if(!n||typeof n==="string"){o(Error("Could not determine the iOS device helper port."));return}r(n.port)})}),Vt=async()=>{let e=$e(),t=await new Promise((r,o)=>{e.once("error",o),e.listen(0,"127.0.0.1",()=>{let n=e.address();if(!n||typeof n==="string"){o(Error("Could not allocate the iOS CA enrollment port."));return}r(n.port)})});return await ge(e),t},fe=(e)=>{let t=e.trim();if(!t||t.length>253||/[\0\s/?#]/u.test(t))throw TypeError("Physical iOS development requires a valid LAN host.");return t},je=(e)=>{let t=e.trim();if(!t||t.length>256||/[\0\r\n]/u.test(t))throw TypeError("--ios-device requires a valid Xcode device identifier or name.");return t},zt=(e,t,r)=>{let o=new URL(`${e}://localhost:${r}`),n=fe(t);return o.hostname=Gt(n)===6?`[${n}]`:n,o},re=async(e)=>{let r=new Ht(await Jt(e.certificateAuthorityPath)).raw,n=`/${Bt().replaceAll("-","")}/absolutejs-development-ca.cer`,s=Ft((a,c)=>{if(a.method!=="GET"||a.url!==n){c.writeHead(404,{"Cache-Control":"no-store","Content-Type":"text/plain; charset=utf-8"}),c.end("Not found.");return}c.writeHead(200,{"Cache-Control":"no-store","Content-Disposition":'attachment; filename="absolutejs-development-ca.cer"',"Content-Length":String(r.byteLength),"Content-Type":"application/x-x509-ca-cert","X-Content-Type-Options":"nosniff"}),c.end(r)}),i=await Vt();await ke(s,i);let l=zt("http",e.displayHost,i);return l.pathname=n,{url:l.href,close:()=>ge(s)}},oe=async(e)=>{let t=$e((r)=>{let o=Wt({host:"127.0.0.1",port:e.targetPort});r.pipe(o),o.pipe(r);let n=()=>{r.destroy(),o.destroy()};r.once("error",n),o.once("error",n)});return await ke(t,e.listenPort),{close:()=>ge(t)}};var Yt=60000,Zt=2000,Qt=(e)=>({ABSOLUTE_EXPO_DEVELOPMENT:"1",...e.certificateAuthorityPath?{ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH:e.certificateAuthorityPath}:{},...e.androidOrigin?{EXPO_PUBLIC_ABSOLUTE_DEV_ANDROID_ORIGIN:e.androidOrigin}:{},...e.iosOrigin?{EXPO_PUBLIC_ABSOLUTE_DEV_IOS_ORIGIN:e.iosOrigin}:{},...e.metroHost?{REACT_NATIVE_PACKAGER_HOSTNAME:e.metroHost}:{}}),Ae=async(e)=>{let t=Kt(e,"node_modules",".bin","expo");try{return await qt(t),t}catch{throw TypeError("Expo dependencies are not installed in the generated shell. Run `absolute mobile init --yes`.")}},er=(e,t)=>{if(e.engine!=="expo")throw TypeError("The Expo development controller requires Expo.");if(t.platforms.length===0&&t.metro==="external")throw TypeError("External Expo execution requires a target platform.");if([t.androidOrigin,t.iosOrigin].some((l)=>l&&new URL(l).protocol==="https:")&&!t.certificateAuthorityPath)throw TypeError("Expo HTTPS development requires the AbsoluteJS development CA certificate.");let o=Qt(t),n={args:["start","--dev-client","--host","localhost","--port",String(t.metroPort)],env:o,role:"metro"},s=t.platforms.length===0?void 0:{args:["prebuild","--clean","--no-install","--platform",t.platforms.length===2?"all":t.platforms[0]??"all"],env:o,role:"native-prepare"},i=t.platforms.map((l)=>{let a=l==="android"?t.androidDevice:t.iosDevice;return{args:[`run:${l}`,"--no-bundler","--port",String(t.metroPort),...a?["--device",a]:[]],env:o,platform:l,role:"native-build"}});return{commands:[...s?[s]:[],...t.metro==="external"?[]:[n],...i],metroPort:t.metroPort,project:e.nativeProjectDirectory}},V=()=>new DOMException("Expo development aborted.","AbortError"),ne=(e,t)=>{let r=(o)=>{if(!o)return;let n="";o.on("data",(s)=>{n+=s.toString("utf8");let i=n.indexOf(`
|
|
3
|
+
`);while(i>=0)t(n.slice(0,i).replace(/\r$/u,"")),n=n.slice(i+1),i=n.indexOf(`
|
|
4
|
+
`)}),o.on("end",()=>{if(n)t(n)})};r(e.stdout),r(e.stderr)},F=async(e)=>{if(e.exitCode!==null||e.killed)return;if(e.kill("SIGTERM"),await Promise.race([new Promise((t)=>e.once("exit",()=>t())),new Promise((t)=>setTimeout(t,Zt))]),e.exitCode===null)e.kill("SIGKILL")},he=(e)=>new Promise((t)=>{if(e.exitCode!==null){t(e.exitCode);return}e.once("exit",(r)=>t(r??1))}),be=async(e,t,r,o)=>{let n=e(t,r,{cwd:o.cwd,env:process.env,stdio:["ignore","pipe","pipe"]});ne(n,o.log);let s=()=>void F(n);o.signal?.addEventListener("abort",s,{once:!0});let i=await he(n);if(o.signal?.removeEventListener("abort",s),o.signal?.aborted)throw V();return i},Be=async(e)=>{let t=er(e.config,e),r=e.executable??await Ae(t.project),o=e.spawnProcess??Xt,n=e.log??(()=>{return}),s={},i=null,l=async()=>{let d=i;i=null,await d?.close()},a=(d,w)=>{s[d]=(s[d]??0)+w,e.onPhaseTiming?.({durationMs:w,phase:d})},c=(d)=>{e.onStateChange?.(d)};if(e.signal?.aborted)throw V();let g=t.commands.find((d)=>d.role==="native-prepare"),m=t.commands.find((d)=>d.role==="metro"),b=t.commands.filter((d)=>d.role==="native-build"),x=async(d)=>{c("preparing-native");let w=performance.now(),y=o(r,d.args,{cwd:t.project,env:{...process.env,...d.env},stdio:["ignore","pipe","pipe"]});ne(y,(N)=>{if(N)n(`[prebuild] ${N}`)});let T=()=>void F(y);e.signal?.addEventListener("abort",T,{once:!0});let O=await he(y);if(e.signal?.removeEventListener("abort",T),e.signal?.aborted)throw V();if(O!==0)throw c("failed"),Error(`Expo native preparation exited with status ${O}.`);a("preparing-native",performance.now()-w)};if(g)await x(g);let u=m!==void 0;if(u)c("starting-metro");let p=performance.now(),f=m?o(r,m.args,{cwd:t.project,env:{...process.env,...m.env},stdio:["ignore","pipe","pipe"]}):void 0,_=!1,A,h=new Promise((d,w)=>{if(!f){d();return}let y=setTimeout(()=>{w(Error("Expo Metro did not become ready within 60 seconds."))},Yt);A=()=>{clearTimeout(y),d()},f.once("exit",(T)=>{if(!_)clearTimeout(y),w(Error(`Expo Metro exited with status ${T??1}.`))})});if(f)ne(f,(d)=>{if(d)n(`[metro] ${d}`);if(!_&&/(?:Waiting on|Metro waiting on|Dev server ready)/iu.test(d))_=!0,A?.()});let C=()=>{if(f)F(f)};e.signal?.addEventListener("abort",C,{once:!0});let H=async(d)=>{if(e.signal?.aborted)throw V();let{platform:w}=d;if(!w)throw TypeError("Expo native build command is missing a platform.");let y=w==="android"?"building-android":"building-ios";c(y);let T=performance.now(),O=o(r,d.args,{cwd:t.project,env:{...process.env,...d.env},stdio:["ignore","pipe","pipe"]});ne(O,(ee)=>{if(ee)n(`[${w}] ${ee}`)});let N=()=>void F(O);e.signal?.addEventListener("abort",N,{once:!0});let M=await he(O);if(e.signal?.removeEventListener("abort",N),e.signal?.aborted)throw V();if(M!==0)throw Error(`Expo ${w} development build exited with status ${M}.`);if(w==="ios"&&e.certificateAuthorityPath&&e.iosOrigin&&new URL(e.iosOrigin).protocol==="https:"&&!e.iosDevice){c("enrolling-trust");let ee=performance.now(),me={cwd:t.project,signal:e.signal,log:(Ne)=>Ne&&n(`[ios-trust] ${Ne}`)},Ce=await be(o,"xcrun",["simctl","keychain","booted","add-root-cert",e.certificateAuthorityPath],me);if(Ce!==0)throw Error(`Expo iOS Simulator development CA trust exited with status ${Ce}.`);await be(o,"xcrun",["simctl","terminate","booted",e.config.appId],me);let Re=await be(o,"xcrun",["simctl","launch","booted",e.config.appId],me);if(Re!==0)throw Error(`Expo iOS Simulator relaunch exited with status ${Re}.`);n("Installed the AbsoluteJS development CA into the Expo iOS Simulator and relaunched the app."),a("enrolling-trust",performance.now()-ee)}let U=performance.now()-T;s[y]=U,e.onPhaseTiming?.({durationMs:U,phase:y})},Q=async(d)=>{let[w,...y]=d;if(!w)return;await H(w),await Q(y)},pe=async()=>{if(!e.iosDevice||!e.certificateAuthorityPath||!e.iosOrigin||new URL(e.iosOrigin).protocol!=="https:")return;c("enrolling-trust");let d=performance.now();i=await(e.startCaEnrollmentServer??re)({certificateAuthorityPath:e.certificateAuthorityPath,displayHost:new URL(e.iosOrigin).hostname}),n(`On the iOS device, open ${i.url}, install the AbsoluteJS development CA profile, then enable it under Settings > General > About > Certificate Trust Settings. This public CA endpoint exists only for this dev session.`),a("enrolling-trust",performance.now()-d)};try{if(await pe(),await h,u)a("starting-metro",performance.now()-p);return await Q(b),c("ready"),{metroPort:t.metroPort,platforms:e.platforms,timings:s,close:async()=>{if(e.signal?.removeEventListener("abort",C),f)await F(f);await l(),c("closed")}}}catch(d){if(e.signal?.removeEventListener("abort",C),f)await F(f);throw await l(),c("failed"),d}};import{access as lr,cp as bn,mkdir as Je,mkdtemp as hn,readdir as ur,readFile as dr,rename as pr,rm as An,writeFile as Ve}from"fs/promises";import{createHash as mr}from"crypto";import{basename as vn,dirname as ze,join as v,relative as gr,resolve as fr}from"path";import{readFileSync as tr}from"fs";import{join as rr}from"path";var or="@absolutejs/auth",He="0.75.6",Fe="@absolutejs/auth-expo",We="0.0.2";var nr=["openid","profile"];var sr=(e)=>{try{return JSON.parse(tr(rr(e,"package.json"),"utf8"))}catch{return}},ir=(e,t)=>{if(typeof e!=="object"||e===null)return!1;return[Reflect.get(e,"dependencies"),Reflect.get(e,"devDependencies"),Reflect.get(e,"optionalDependencies"),Reflect.get(e,"peerDependencies")].some((r)=>typeof r==="object"&&r!==null&&Object.hasOwn(r,t))},ar=(e)=>{let t=e.deepLinkScheme??e.appId.toLowerCase();return{clientId:`absolutejs-native:${e.appId}`,issuer:e.productionOrigin,redirectUri:`${t}://auth/callback`,scopes:[...nr]}};var cr=(e)=>ir(sr(e),or);var Ge=(e,t)=>cr(e)?ar(t):void 0;var I=`// Generated by AbsoluteJS. Edit absolute.config.ts, then run absolute mobile sync.
|
|
5
|
+
`;var br=".absolutejs-expo-project",z=async(e)=>{try{return await lr(e),!0}catch{return!1}},Xe=(e,t)=>{let r=gr(e,t).replaceAll("\\","/");return r.startsWith(".")?r:`./${r}`},hr=(e)=>e.split("/").filter(Boolean).map((t)=>{if(t.startsWith(":"))return`[${t.slice(1)}]`;if(t==="."||t===".."||!/^[A-Za-z0-9._~-]+$/u.test(t))throw TypeError(`Expo native route ${e} contains an unsupported segment ${t}.`);return t}),Ar=(e,t)=>v(e,"app",...hr(t),"index.tsx"),wr=(e)=>({dependencies:{...e?{"@absolutejs/auth":He,[Fe]:We}:{},expo:"~57.0.9","expo-asset":"~57.0.15","expo-constants":"~57.0.16","expo-dev-client":"~57.0.16","expo-file-system":"~57.0.6","expo-haptics":"~57.0.2","expo-linking":"~57.0.8","expo-router":"~57.0.17",...e?{"expo-secure-store":"~57.0.2","expo-web-browser":"~57.0.2"}:{},react:"19.2.3","react-native":"0.86.3","react-native-safe-area-context":"~5.7.0","react-native-screens":"4.26.0","react-native-webview":"13.16.1"},devDependencies:{"@types/react":"~19.2.2",typescript:"~6.0.3"},main:"expo-router/entry",name:"absolutejs-expo-shell",private:!0,scripts:{android:"expo run:android",ios:"expo run:ios",start:"expo start --dev-client"},version:"0.0.0"}),Er=(e,t)=>({expo:{android:{intentFilters:e.deepLinkHosts.map((r)=>({action:"VIEW",autoVerify:!0,category:["BROWSABLE","DEFAULT"],data:[{host:r,pathPrefix:"/",scheme:"https"}]})),package:e.appId},experiments:{typedRoutes:!0},ios:{associatedDomains:e.deepLinkHosts.map((r)=>`applinks:${r}`),bundleIdentifier:e.appId,...e.iosVersion?{buildNumber:e.iosVersion}:{}},name:e.appName,plugins:["expo-router",["expo-dev-client",{launchMode:"most-recent"}],...t?["expo-secure-store"]:[]],runtimeVersion:{policy:"appVersion"},scheme:e.deepLinkScheme,slug:e.appId.toLowerCase().replaceAll(".","-"),version:e.iosVersion??"0.1.0"}}),vr=`${I}const config = require('./app.json');
|
|
6
6
|
|
|
7
7
|
if (process.env.ABSOLUTE_EXPO_DEVELOPMENT === '1' && process.env.ABSOLUTE_EXPO_DEVELOPMENT_CA_PATH) {
|
|
8
8
|
config.expo.plugins = [
|
|
@@ -12,7 +12,7 @@ if (process.env.ABSOLUTE_EXPO_DEVELOPMENT === '1' && process.env.ABSOLUTE_EXPO_D
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
module.exports = config;
|
|
15
|
-
`,
|
|
15
|
+
`,Sr=`${I}const { AndroidConfig, withAndroidManifest, withDangerousMod } = require('expo/config-plugins');
|
|
16
16
|
const { copyFile, mkdir } = require('node:fs/promises');
|
|
17
17
|
const path = require('node:path');
|
|
18
18
|
|
|
@@ -51,7 +51,7 @@ const withAbsoluteDevelopmentCa = config => {
|
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
module.exports = withAbsoluteDevelopmentCa;
|
|
54
|
-
`,
|
|
54
|
+
`,yr=(e)=>`${I}const { getDefaultConfig } = require('expo/metro-config');
|
|
55
55
|
const path = require('node:path');
|
|
56
56
|
|
|
57
57
|
const projectRoot = __dirname;
|
|
@@ -65,12 +65,17 @@ config.resolver.nodeModulesPaths = [
|
|
|
65
65
|
config.watchFolders = [appRoot];
|
|
66
66
|
|
|
67
67
|
module.exports = config;
|
|
68
|
-
`,
|
|
68
|
+
`,xr=(e)=>`${I}import { Stack } from 'expo-router';
|
|
69
|
+
${e?`import { useEffect } from 'react';
|
|
70
|
+
import { startAbsoluteExpoAuth } from '../src/generated/AbsoluteAuth';`:""}
|
|
69
71
|
|
|
70
72
|
export default function AbsoluteLayout() {
|
|
73
|
+
${e?`useEffect(() => {
|
|
74
|
+
void startAbsoluteExpoAuth();
|
|
75
|
+
}, []);`:""}
|
|
71
76
|
return <Stack screenOptions={{ headerShown: false }} />;
|
|
72
77
|
}
|
|
73
|
-
`,
|
|
78
|
+
`,Ir=`${I}import * as Haptics from 'expo-haptics';
|
|
74
79
|
import { Link } from 'expo-router';
|
|
75
80
|
import { Pressable, SafeAreaView, StyleSheet, Text, View } from 'react-native';
|
|
76
81
|
|
|
@@ -100,15 +105,44 @@ const styles = StyleSheet.create({
|
|
|
100
105
|
page: { alignItems: 'center', backgroundColor: '#020617', flex: 1, justifyContent: 'center', padding: 20 },
|
|
101
106
|
title: { color: '#f8fafc', fontSize: 32, fontWeight: '800' }
|
|
102
107
|
});
|
|
103
|
-
`,
|
|
108
|
+
`,_r=(e,t)=>{let r=mr("sha256").update(t).digest("hex").slice(0,24);return`${I}import { createAbsoluteExpoAuthClient } from '@absolutejs/auth-expo';
|
|
109
|
+
import { createMobileAuthTransport, installAuthClientRuntimeTransport } from '@absolutejs/auth/client/mobile';
|
|
110
|
+
|
|
111
|
+
export const absoluteExpoAuth = createAbsoluteExpoAuthClient({
|
|
112
|
+
allowedOrigins: [${JSON.stringify(e.issuer)}],
|
|
113
|
+
clientId: ${JSON.stringify(e.clientId)},
|
|
114
|
+
issuer: ${JSON.stringify(e.issuer)},
|
|
115
|
+
redirectUri: ${JSON.stringify(e.redirectUri)},
|
|
116
|
+
resource: ${JSON.stringify(e.issuer)},
|
|
117
|
+
scopes: ${JSON.stringify(e.scopes)},
|
|
118
|
+
storagePrefix: ${JSON.stringify(`absolutejs.auth.${r}`)}
|
|
119
|
+
});
|
|
120
|
+
installAuthClientRuntimeTransport(createMobileAuthTransport(absoluteExpoAuth, { baseUrl: ${JSON.stringify(e.issuer)} }));
|
|
121
|
+
|
|
122
|
+
let currentPrincipal: Awaited<ReturnType<typeof absoluteExpoAuth.principal>> = null;
|
|
123
|
+
absoluteExpoAuth.onPrincipalChange(principal => {
|
|
124
|
+
currentPrincipal = principal;
|
|
125
|
+
});
|
|
126
|
+
export const getAbsoluteExpoAuthPrincipal = () => currentPrincipal;
|
|
127
|
+
|
|
128
|
+
let startPromise: Promise<void> | undefined;
|
|
129
|
+
export const startAbsoluteExpoAuth = () => {
|
|
130
|
+
startPromise ??= absoluteExpoAuth.start().then(async () => {
|
|
131
|
+
await absoluteExpoAuth.principal();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return startPromise;
|
|
135
|
+
};
|
|
136
|
+
`},Or=(e,t)=>{let r=["/__absolute/native",...Object.keys(e.expoNativeRoutes)];return`${I}import * as Haptics from 'expo-haptics';
|
|
104
137
|
import * as Linking from 'expo-linking';
|
|
105
138
|
import { router, usePathname } from 'expo-router';
|
|
106
139
|
import { useEffect, useRef, useState } from 'react';
|
|
107
140
|
import { ActivityIndicator, BackHandler, Platform, StyleSheet, View } from 'react-native';
|
|
108
141
|
import { WebView, type WebViewMessageEvent } from 'react-native-webview';
|
|
109
142
|
import { materializeAbsoluteWebBundle } from './webAssets';
|
|
143
|
+
${t?"import { absoluteExpoAuth, getAbsoluteExpoAuthPrincipal, startAbsoluteExpoAuth } from './AbsoluteAuth';":""}
|
|
110
144
|
|
|
111
|
-
const BRIDGE_FORMAT =
|
|
145
|
+
const BRIDGE_FORMAT = 2;
|
|
112
146
|
const MAX_MESSAGE_BYTES = 64 * 1024;
|
|
113
147
|
const MAX_HTTP_BODY_BYTES = 48 * 1024;
|
|
114
148
|
const NATIVE_ROUTES = new Set(${JSON.stringify(r)});
|
|
@@ -117,6 +151,7 @@ const DEV_ORIGIN = Platform.OS === 'android'
|
|
|
117
151
|
? process.env.EXPO_PUBLIC_ABSOLUTE_DEV_ANDROID_ORIGIN
|
|
118
152
|
: process.env.EXPO_PUBLIC_ABSOLUTE_DEV_IOS_ORIGIN;
|
|
119
153
|
const HMR_TARGET = Platform.OS === 'android' ? 'expo-android' : 'expo-ios';
|
|
154
|
+
const AUTH_ENABLED = ${t?"true":"false"};
|
|
120
155
|
|
|
121
156
|
const bridgeBootstrap = (path: string) => {
|
|
122
157
|
const initialPath = DEV_ORIGIN
|
|
@@ -124,6 +159,7 @@ const bridgeBootstrap = (path: string) => {
|
|
|
124
159
|
: JSON.stringify(path);
|
|
125
160
|
return \`(() => {
|
|
126
161
|
const pending = new Map();
|
|
162
|
+
const listeners = new Map();
|
|
127
163
|
let sequence = 0;
|
|
128
164
|
let currentPath = \${initialPath};
|
|
129
165
|
const send = value => {
|
|
@@ -133,6 +169,10 @@ const bridgeBootstrap = (path: string) => {
|
|
|
133
169
|
};
|
|
134
170
|
globalThis.__absoluteExpoReceive = source => {
|
|
135
171
|
const message = JSON.parse(source);
|
|
172
|
+
if (message.kind === 'event') {
|
|
173
|
+
for (const listener of listeners.get(message.event) || []) listener(message.payload || {});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
136
176
|
const operation = pending.get(message.id);
|
|
137
177
|
if (!operation) return;
|
|
138
178
|
pending.delete(message.id);
|
|
@@ -140,9 +180,18 @@ const bridgeBootstrap = (path: string) => {
|
|
|
140
180
|
message.error ? operation.reject(new Error(message.error.message)) : operation.resolve(message.result);
|
|
141
181
|
};
|
|
142
182
|
globalThis.__absoluteExpoBridge = {
|
|
183
|
+
on(event, listener) {
|
|
184
|
+
const values = listeners.get(event) || new Set();
|
|
185
|
+
values.add(listener);
|
|
186
|
+
listeners.set(event, values);
|
|
187
|
+
return () => {
|
|
188
|
+
values.delete(listener);
|
|
189
|
+
if (values.size === 0) listeners.delete(event);
|
|
190
|
+
};
|
|
191
|
+
},
|
|
143
192
|
request(method, params) {
|
|
144
193
|
const id = 'web_' + Date.now().toString(36) + '_' + (++sequence).toString(36);
|
|
145
|
-
send({ format:
|
|
194
|
+
send({ format: 2, id, kind: 'request', method, params, path: currentPath });
|
|
146
195
|
return new Promise((resolve, reject) => {
|
|
147
196
|
const timer = setTimeout(() => {
|
|
148
197
|
pending.delete(id);
|
|
@@ -153,7 +202,7 @@ const bridgeBootstrap = (path: string) => {
|
|
|
153
202
|
},
|
|
154
203
|
setPath(path) {
|
|
155
204
|
currentPath = path;
|
|
156
|
-
send({ format:
|
|
205
|
+
send({ format: 2, kind: 'event', event: 'navigation', path });
|
|
157
206
|
}
|
|
158
207
|
};
|
|
159
208
|
if (\${DEV_ORIGIN ? 'true' : 'false'}) {
|
|
@@ -161,7 +210,7 @@ const bridgeBootstrap = (path: string) => {
|
|
|
161
210
|
const path = location.pathname + location.search + location.hash;
|
|
162
211
|
if (path === currentPath) return;
|
|
163
212
|
currentPath = path;
|
|
164
|
-
send({ format:
|
|
213
|
+
send({ format: 2, kind: 'event', event: 'navigation', path });
|
|
165
214
|
};
|
|
166
215
|
for (const method of ['pushState', 'replaceState']) {
|
|
167
216
|
const original = history[method];
|
|
@@ -180,9 +229,9 @@ const bridgeBootstrap = (path: string) => {
|
|
|
180
229
|
const url = new URL(anchor.href, location.href);
|
|
181
230
|
if (!${JSON.stringify(r)}.includes(url.pathname)) return;
|
|
182
231
|
event.preventDefault();
|
|
183
|
-
send({ format:
|
|
232
|
+
send({ format: 2, kind: 'event', event: 'navigation', path: url.pathname + url.search + url.hash });
|
|
184
233
|
}, true);
|
|
185
|
-
send({ format:
|
|
234
|
+
send({ format: 2, kind: 'event', event: 'ready', path: currentPath });
|
|
186
235
|
})(); true;\`;
|
|
187
236
|
};
|
|
188
237
|
|
|
@@ -198,16 +247,16 @@ const impact = async (params: Record<string, unknown>) => {
|
|
|
198
247
|
};
|
|
199
248
|
|
|
200
249
|
const bridgeFetch = async (params: Record<string, unknown>) => {
|
|
201
|
-
if (params.method !== 'GET' || typeof params.url !== 'string' || typeof params.headers !== 'object' || params.headers === null || Array.isArray(params.headers)) throw new Error('Expo bridge HTTP request is invalid.');
|
|
250
|
+
if (typeof params.method !== 'string' || !['DELETE', 'GET', 'PATCH', 'POST', 'PUT'].includes(params.method) || typeof params.url !== 'string' || typeof params.headers !== 'object' || params.headers === null || Array.isArray(params.headers) || params.body !== undefined && typeof params.body !== 'string') throw new Error('Expo bridge HTTP request is invalid.');
|
|
202
251
|
const url = new URL(params.url);
|
|
203
252
|
if (url.origin !== PRODUCTION_ORIGIN || url.username || url.password) throw new Error('Expo bridge HTTP request left productionOrigin.');
|
|
204
253
|
const headers = new Headers();
|
|
205
254
|
for (const [name, value] of Object.entries(params.headers as Record<string, unknown>)) {
|
|
206
255
|
const normalized = name.toLowerCase();
|
|
207
|
-
if (typeof value !== 'string' || (normalized !== 'accept' && !normalized.startsWith('x-absolute-mobile-'))) throw new Error('Expo bridge HTTP header is not allowed.');
|
|
256
|
+
if (typeof value !== 'string' || (normalized !== 'accept' && normalized !== 'content-type' && !normalized.startsWith('x-absolute-mobile-'))) throw new Error('Expo bridge HTTP header is not allowed.');
|
|
208
257
|
headers.set(normalized, value);
|
|
209
258
|
}
|
|
210
|
-
const response = await fetch(url, { headers, method:
|
|
259
|
+
const response = await ${t?"absoluteExpoAuth.fetchOptional":"fetch"}(url, { ...(typeof params.body === 'string' ? { body: params.body } : {}), headers, method: params.method, redirect: 'manual' });
|
|
211
260
|
if (new URL(response.url || url.href).origin !== PRODUCTION_ORIGIN || response.status >= 300 && response.status < 400) throw new Error('Expo bridge HTTP redirects are not allowed.');
|
|
212
261
|
const body = await response.text();
|
|
213
262
|
if (new TextEncoder().encode(body).byteLength > MAX_HTTP_BODY_BYTES) throw new Error('Expo bridge HTTP response exceeds 48 KiB.');
|
|
@@ -219,13 +268,32 @@ const bridgeFetch = async (params: Record<string, unknown>) => {
|
|
|
219
268
|
return { body, headers: responseHeaders, status: response.status };
|
|
220
269
|
};
|
|
221
270
|
|
|
271
|
+
const authStatus = async () => {
|
|
272
|
+
${t?`const user = await absoluteExpoAuth.status();
|
|
273
|
+
const principal = getAbsoluteExpoAuthPrincipal();
|
|
274
|
+
return { principal: principal ? { namespace: principal.namespace } : null, user };`:"throw new Error('Expo Auth is not configured.');"}
|
|
275
|
+
};
|
|
276
|
+
|
|
222
277
|
export function AbsoluteWebHost() {
|
|
223
278
|
const pathname = usePathname() || '/';
|
|
224
279
|
const webView = useRef<WebView>(null);
|
|
225
280
|
const [indexUri, setIndexUri] = useState<string>();
|
|
226
281
|
const [canGoBack, setCanGoBack] = useState(false);
|
|
282
|
+
const [authReady, setAuthReady] = useState(!AUTH_ENABLED);
|
|
227
283
|
const activeWebPath = useRef(pathname);
|
|
228
284
|
|
|
285
|
+
useEffect(() => {
|
|
286
|
+
if (!AUTH_ENABLED) return;
|
|
287
|
+
let active = true;
|
|
288
|
+
${t?`const stop = absoluteExpoAuth.onPrincipalChange(principal => {
|
|
289
|
+
if (!active) return;
|
|
290
|
+
const source = JSON.stringify({ event: 'auth.principal', format: BRIDGE_FORMAT, kind: 'event', path: activeWebPath.current, payload: { principal: principal ? { namespace: principal.namespace } : null } });
|
|
291
|
+
webView.current?.injectJavaScript(\`globalThis.__absoluteExpoReceive(\${JSON.stringify(source)}); true;\`);
|
|
292
|
+
});
|
|
293
|
+
void startAbsoluteExpoAuth().then(() => { if (active) setAuthReady(true); });
|
|
294
|
+
return () => { active = false; stop(); };`:""}
|
|
295
|
+
}, []);
|
|
296
|
+
|
|
229
297
|
useEffect(() => {
|
|
230
298
|
if (DEV_ORIGIN) {
|
|
231
299
|
const target = new URL(pathname, DEV_ORIGIN);
|
|
@@ -275,6 +343,16 @@ export function AbsoluteWebHost() {
|
|
|
275
343
|
respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: null });
|
|
276
344
|
} else if (message.method === 'http.fetch') {
|
|
277
345
|
respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: await bridgeFetch(message.params as Record<string, unknown>) });
|
|
346
|
+
} else if (message.method === 'auth.signIn') {
|
|
347
|
+
${t?`const params = message.params as Record<string, unknown>;
|
|
348
|
+
if (typeof params.email !== 'string' || typeof params.signup !== 'boolean') throw new Error('Expo Auth sign-in params are invalid.');
|
|
349
|
+
await absoluteExpoAuth.signIn({ authorizationParameters: { login_hint: params.email, ...(params.signup ? { screen_hint: 'signup' } : {}) } });
|
|
350
|
+
respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: await authStatus() });`:"throw new Error('Expo Auth is not configured.');"}
|
|
351
|
+
} else if (message.method === 'auth.signOut') {
|
|
352
|
+
${t?`await absoluteExpoAuth.signOut();
|
|
353
|
+
respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: null });`:"throw new Error('Expo Auth is not configured.');"}
|
|
354
|
+
} else if (message.method === 'auth.status') {
|
|
355
|
+
respond({ format: BRIDGE_FORMAT, id: message.id, kind: 'response', result: await authStatus() });
|
|
278
356
|
} else {
|
|
279
357
|
throw new Error('Expo bridge method is not allowed.');
|
|
280
358
|
}
|
|
@@ -283,7 +361,7 @@ export function AbsoluteWebHost() {
|
|
|
283
361
|
}
|
|
284
362
|
};
|
|
285
363
|
|
|
286
|
-
if (!indexUri) return <View style={styles.loading}><ActivityIndicator /></View>;
|
|
364
|
+
if (!indexUri || !authReady) return <View style={styles.loading}><ActivityIndicator /></View>;
|
|
287
365
|
return <WebView
|
|
288
366
|
allowFileAccess
|
|
289
367
|
allowFileAccessFromFileURLs
|
|
@@ -304,47 +382,47 @@ export function AbsoluteWebHost() {
|
|
|
304
382
|
}
|
|
305
383
|
|
|
306
384
|
const styles = StyleSheet.create({ loading: { alignItems: 'center', flex: 1, justifyContent: 'center' }, web: { flex: 1 } });
|
|
307
|
-
`},
|
|
385
|
+
`},Tr=`${I}import { AbsoluteWebHost } from '../src/generated/AbsoluteWebHost';
|
|
308
386
|
|
|
309
387
|
export default AbsoluteWebHost;
|
|
310
|
-
`,
|
|
388
|
+
`,Pr=`${I}import { AbsoluteWebHost } from '../src/generated/AbsoluteWebHost';
|
|
311
389
|
|
|
312
390
|
export default AbsoluteWebHost;
|
|
313
|
-
`,
|
|
314
|
-
`,
|
|
315
|
-
`,
|
|
391
|
+
`,Dr=(e,t)=>`${I}export { default } from ${JSON.stringify(Xe(ze(e),t).replace(/\.(?:[cm]?[jt]sx?)$/u,""))};
|
|
392
|
+
`,Cr=(e,t,r)=>({compilerOptions:{paths:{...r?{"@absolutejs/auth":["./node_modules/@absolutejs/auth/dist/index.d.ts"],"@absolutejs/auth/*":["./node_modules/@absolutejs/auth/dist/*"]}:{},"*":["./node_modules/*",`${Xe(t,e)}/node_modules/*`],react:["./node_modules/@types/react/index.d.ts"],"react/*":["./node_modules/@types/react/*"]},strict:!0},extends:"expo/tsconfig.base"}),Rr=async(e,t,r)=>{if(await Je(ze(e),{recursive:!0}),await z(e)){let n=await dr(e,"utf8");if(n===t)return!1;if(!r&&!n.startsWith(I))throw TypeError(`Expo project file ${e} is not AbsoluteJS-managed; rerun with --force only after reviewing it.`)}let o=`${e}.${crypto.randomUUID()}.tmp`;return await Ve(o,t,{flag:"wx"}),await pr(o,e),!0},we=(e)=>`${JSON.stringify(e,null,"\t")}
|
|
393
|
+
`,Nr=`${I}export const materializeAbsoluteWebBundle = async () => {
|
|
316
394
|
throw new Error('The embedded AbsoluteJS bundle is unavailable. Run absolute prepare before a production Expo build.');
|
|
317
395
|
};
|
|
318
|
-
`,
|
|
319
|
-
`);let
|
|
396
|
+
`,qe=async(e,t)=>{if(e.engine!=="expo")throw TypeError("Expo project generation requires mobile.engine: expo.");let r=fr(t.projectRoot),o=e.nativeProjectDirectory,n=Ge(r,e),s=Object.entries(e.expoNativeRoutes),l=(await Promise.all(s.map(async([u,p])=>({exists:await z(p),module:p,route:u})))).find((u)=>!u.exists);if(l)throw TypeError(`Expo native route ${l.route} references missing module ${l.module}.`);let a=v(o,br);if(await z(o)&&!await z(a)){if((await ur(o)).length>0&&!t.force)throw TypeError(`Expo project directory ${o} is not AbsoluteJS-managed; rerun with --force only after reviewing it.`)}await Je(o,{recursive:!0}),await Ve(a,`format=1
|
|
397
|
+
`);let c=Boolean(n),g=new Map([[v(o,".gitignore"),`.expo/
|
|
320
398
|
android/
|
|
321
399
|
ios/
|
|
322
400
|
node_modules/
|
|
323
|
-
`],[
|
|
401
|
+
`],[v(o,"app.json"),we(Er(e,c))],[v(o,"app.config.js"),vr],[v(o,"package.json"),we(wr(c))],[v(o,"metro.config.js"),yr(r)],[v(o,"plugins","withAbsoluteDevelopmentCa.js"),Sr],[v(o,"tsconfig.json"),we(Cr(r,o,c))],[v(o,"app","_layout.tsx"),xr(c)],[v(o,"app","__absolute","native","index.tsx"),Ir],[v(o,"src","generated","AbsoluteWebHost.tsx"),Or(e,n)]]);if(n)g.set(v(o,"src","generated","AbsoluteAuth.ts"),_r(n,e.appId));let m=v(o,"src","generated","webAssets.ts");if(!await z(m))g.set(m,Nr);if(!e.expoNativeRoutes["/"])g.set(v(o,"app","index.tsx"),Tr);g.set(v(o,"app","[...absolute].tsx"),Pr);for(let[u,p]of s){let f=u==="/"?v(o,"app","index.tsx"):Ar(o,u);g.set(f,Dr(f,p))}return{changed:(await Promise.all([...g].map(([u,p])=>Rr(u,p,!0)))).filter(Boolean).length,path:o,written:[...g.keys()]}};import{createHash as ft,randomUUID as to}from"crypto";import{access as ro,copyFile as at,mkdir as G,readFile as ae,rename as oo,rm as ye,writeFile as B}from"fs/promises";import{isIP as no}from"net";import{dirname as bt,isAbsolute as so,join as S,relative as io,resolve as ce,sep as ao}from"path";import{access as Lr}from"fs/promises";import{homedir as Ze}from"os";import{join as K}from"path";import{existsSync as Nn,readFileSync as Mr}from"fs";var X=1000;var Ke=60000;var Ee=2;var q=(e)=>{let t;if(e<X)t=`${e.toFixed(Ee)}ms`;else if(e<Ke)t=`${(e/X).toFixed(Ee)}s`;else{let r=Math.round(e/X),o=Math.floor(r/60),n=r%60;t=n===0?`${o}m`:`${o}m ${n}s`}return t};var Ye=()=>{try{let e=Mr("/proc/version","utf-8");return/microsoft|wsl/i.test(e)}catch{return!1}};var Qe="AbsoluteJS_API_36",Ur=(e)=>{try{let t=Bun.spawnSync(e,{stderr:"ignore",stdout:"pipe"});return{exitCode:t.exitCode,stdout:t.stdout.toString()}}catch{return{exitCode:1,stdout:""}}},$r=(e)=>{try{let t=JSON.parse(e);if(typeof t!=="object"||t===null)return!1;let r=Reflect.get(t,"runtimes");if(!Array.isArray(r))return!1;return r.some((o)=>typeof o==="object"&&o!==null&&Reflect.get(o,"isAvailable")===!0&&typeof Reflect.get(o,"identifier")==="string"&&String(Reflect.get(o,"identifier")).includes("iOS"))}catch{return!1}},kr=(e)=>{let t=/^([a-z]):[\\/](.*)$/i.exec(e.trim());if(!t)return e.trim().replaceAll("\\","/");return`/mnt/${t[1]?.toLowerCase()}/${t[2]?.replaceAll("\\","/")}`},jr=()=>{try{let e=Bun.spawnSync(["cmd.exe","/d","/c","echo","%LOCALAPPDATA%"],{stderr:"ignore",stdout:"pipe"});if(e.exitCode!==0)return;let t=e.stdout.toString().trim();if(!t||t==="%LOCALAPPDATA%")return;return kr(t)}catch{return}},Br=(e,t=process.env)=>{if(e==="windows")return K(t.LOCALAPPDATA??K(Ze(),"AppData","Local"),"AbsoluteJS","Android","Sdk");if(e==="wsl"){let r=jr();if(r)return K(r,"AbsoluteJS","Android","Sdk")}return K(Ze(),".absolutejs","android-sdk")},Hr=async(e)=>{try{return await Lr(e),!0}catch{return!1}},se=(e=process.platform,t=Ye())=>{if(e==="darwin")return"macos";if(e==="win32")return"windows";if(e==="linux"&&t)return"wsl";return"linux"},Fr=(e,t)=>{if(e==="wsl")return[`${t}.exe`,`${t}.bat`,t];if(e==="windows")return[t,`${t}.exe`,`${t}.bat`];return[t]},k=async(e,t,r,o)=>{let s=(await Promise.all(t.map(async(i)=>await r.exists(i)?i:void 0))).find((i)=>i!==void 0);if(s)return s;for(let i of Fr(o,e)){let l=r.which(i);if(l)return l}return},j=(e,t,r,o,n)=>o?{id:e,label:t,path:o,platform:r,status:"pass"}:{id:e,label:t,platform:r,remediation:n,status:"fail"},et=async(e={})=>{let t=e.env??process.env,r=e.host??se(),o=e.exists??Hr,n=e.which??((A)=>Bun.which(A)),s=e.capture??Ur,i=e.androidRoot===null?void 0:e.androidRoot??t.ANDROID_HOME??t.ANDROID_SDK_ROOT??Br(r,t),l=r==="windows"||r==="wsl",a=(A)=>i?K(i,...A):void 0,c=(A)=>A.filter((h)=>Boolean(h)),g=await k("adb",c([a(["platform-tools",l?"adb.exe":"adb"])]),{exists:o,which:n},r),m=await k("emulator",c([a(["emulator",l?"emulator.exe":"emulator"])]),{exists:o,which:n},r),b=await k("sdkmanager",c([a(["cmdline-tools","latest","bin",l?"sdkmanager.bat":"sdkmanager"])]),{exists:o,which:n},r),x=await k("avdmanager",c([a(["cmdline-tools","latest","bin",l?"avdmanager.bat":"avdmanager"])]),{exists:o,which:n},r),u=await k("java",[],{exists:o,which:n},r),p=[{id:"host",label:`Development host: ${r}`,platform:"host",status:"pass"},j("android.adb","Android Debug Bridge","android",g,"Install Android SDK Platform Tools or expose adb on PATH."),j("android.emulator","Android Emulator","android",m,"Install the Android Emulator from Android Studio SDK Manager."),j("android.sdkmanager","Android SDK Manager","android",b,"Install Android SDK Command-line Tools (latest)."),j("android.avdmanager","Android Virtual Device Manager","android",x,"Install Android SDK Command-line Tools (latest)."),j("android.java","Java runtime","android",u,"Install the JDK required by the configured Android Gradle plugin.")];if(m){let A=s([m,"-list-avds"]),h=A.exitCode===0&&A.stdout.split(/\r?\n/).includes(Qe);p.push({id:"android.avd",label:`AbsoluteJS Android emulator (${Qe})`,platform:"android",remediation:h?void 0:"Run absolute mobile doctor android --fix to provision the managed emulator.",status:h?"pass":"fail"})}if(r==="wsl")p.push({id:"android.virtualization",label:g?.endsWith(".exe")?"Windows-host Android bridge available to WSL":"WSL requires a Windows-host emulator bridge or Linux KVM",platform:"android",remediation:g?.endsWith(".exe")?void 0:"Expose the Windows Android SDK adb.exe to WSL, or enable /dev/kvm for a Linux SDK.",status:g?.endsWith(".exe")?"pass":"warn"});else if(r==="linux"){let A=await o("/dev/kvm");p.push({id:"android.virtualization",label:"Linux KVM acceleration",platform:"android",remediation:A?void 0:"Enable KVM and grant the current user access to /dev/kvm.",status:A?"pass":"warn"})}if(r!=="macos")return p.push({id:"ios.simulator",label:"iOS Simulator requires macOS and Xcode",platform:"ios",status:"skip"}),p;let f=await k("xcrun",[],{exists:o,which:n},r),_=await k("xcodebuild",[],{exists:o,which:n},r);if(p.push(j("ios.xcrun","Xcode command runner","ios",f,"Install Xcode and select it with xcode-select."),j("ios.xcodebuild","Xcode build system","ios",_,"Install Xcode and select it with xcode-select.")),f){let A=s([f,"simctl","list","runtimes","--json"]),h=A.exitCode===0&&$r(A.stdout);p.push({id:"ios.runtime",label:"iOS Simulator runtime",platform:"ios",remediation:h?void 0:"Run absolute mobile doctor ios --fix to download an iOS Simulator runtime.",status:h?"pass":"fail"})}return p};import{access as Wr,readFile as Gr,rename as Jr,writeFile as Vr}from"fs/promises";import{relative as zr,resolve as tt}from"path";var rt="capacitor.config.ts",ve=(e,t)=>zr(e,t).replaceAll("\\","/"),Xr=(e,t)=>`import type { CapacitorConfig } from '@capacitor/cli';
|
|
324
402
|
|
|
325
403
|
const config: CapacitorConfig = {
|
|
326
404
|
appId: ${JSON.stringify(e.appId)},
|
|
327
405
|
appName: ${JSON.stringify(e.appName)},
|
|
328
|
-
webDir: ${JSON.stringify(
|
|
406
|
+
webDir: ${JSON.stringify(ve(t,e.bundleDirectory))},
|
|
329
407
|
android: {
|
|
330
|
-
path: ${JSON.stringify(
|
|
408
|
+
path: ${JSON.stringify(ve(t,`${e.nativeProjectDirectory}/android`))}
|
|
331
409
|
},
|
|
332
410
|
ios: {
|
|
333
|
-
path: ${JSON.stringify(
|
|
411
|
+
path: ${JSON.stringify(ve(t,`${e.nativeProjectDirectory}/ios`))}
|
|
334
412
|
}
|
|
335
413
|
};
|
|
336
414
|
|
|
337
415
|
export default config;
|
|
338
|
-
`,
|
|
416
|
+
`,qr=async(e)=>{try{return await Wr(e),!0}catch{return!1}},ot=async(e,t)=>{let r=tt(t.projectRoot),o=tt(r,rt),n=Xr(e,r);if(await qr(o)){if(await Gr(o,"utf8")===n)return{changed:!1,path:o};if(!t.force)throw TypeError(`${rt} already exists and differs; rerun with --force only after reviewing it.`)}let s=`${o}.${crypto.randomUUID()}.tmp`;return await Vr(s,n,{flag:"wx"}),await Jr(s,o),{changed:!0,path:o}};import{createHash as Kr}from"crypto";import{access as zn,copyFile as Xn,mkdir as qn,mkdtemp as Kn,readdir as Yr,readFile as Zr,rename as Yn,rm as Zn,stat as Qn,writeFile as es}from"fs/promises";import{dirname as rs,isAbsolute as os,join as Qr,relative as nt,resolve as ns,sep as ss}from"path";var eo=new Set(["Pods","DerivedData","build","xcuserdata"]),st=async(e,t=e,r={})=>{let o=await Yr(t,{withFileTypes:!0});return(await Promise.all(o.sort((s,i)=>s.name.localeCompare(i.name)).map(async(s)=>{let i=Qr(t,s.name),l=nt(e,i).replaceAll("\\","/");if(s.isDirectory()&&(eo.has(s.name)||l==="App/App/public"&&r.includePublicBundle!==!0))return[];if(s.isDirectory())return st(e,i,r);return s.isFile()?[i]:[]}))).flat()},it=async(e,t={})=>{let r=Kr("sha256"),o=await st(e,e,t),n=await Promise.all(o.map((s)=>Zr(s)));return o.forEach((s,i)=>{r.update(nt(e,s).replaceAll("\\","/")),r.update("\x00"),r.update(n[i]??new Uint8Array),r.update("\x00")}),r.digest("hex")};var Se="AbsoluteJS iPhone",ct=180000,co=1000,xe=1,Ie=1,D=(e)=>typeof e==="object"&&e!==null&&!Array.isArray(e),L=async(e)=>{try{return await ro(e),!0}catch{return!1}},W=(e)=>e?.throwIfAborted(),lo=(e,t={})=>{try{let r=Bun.spawnSync(e,{cwd:t.cwd,env:t.env,stderr:"pipe",stdin:"ignore",stdout:"pipe"});return{exitCode:r.exitCode,stderr:r.stderr.toString(),stdout:r.stdout.toString()}}catch(r){return{exitCode:1,stderr:r instanceof Error?r.message:String(r),stdout:""}}},ht=async(e,t={})=>Bun.spawn(e,{cwd:t.cwd,env:t.env,signal:t.signal,stderr:"inherit",stdin:"inherit",stdout:"inherit"}).exited,uo=(e,t={})=>{Bun.spawn(e,{cwd:t.cwd,env:t.env,signal:t.signal,stderr:"ignore",stdin:"ignore",stdout:"ignore"})},lt=async(e,t)=>{let r=e.getReader(),o=new TextDecoder,n="",s=async()=>{let{done:i,value:l}=await r.read();if(i)return;n+=o.decode(l,{stream:!0});let a=n.split(/\r?\n/u);n=a.pop()??"",a.forEach(t),await s()};try{if(await s(),n+=o.decode(),n)t(n)}finally{r.releaseLock()}},po=(e,t,r)=>{let o=Bun.spawn(e,{cwd:t.cwd,env:t.env,signal:t.signal,stderr:"pipe",stdin:"ignore",stdout:"pipe"});return lt(o.stdout,r),lt(o.stderr,r),{close:async()=>{try{o.kill()}catch{}await o.exited.catch(()=>{return})}}},P=async(e,t,r,o)=>{let n=await r(e,o);if(n!==0)throw Error(`${t} failed with status ${n}.`)},mo=async(e,t,r,o)=>{if(!e.https)return;if(!e.certificateAuthorityPath)throw Error("iOS Simulator HTTPS requires the AbsoluteJS development CA certificate.");await P([e.project.xcrun,"simctl","keychain",t,"add-root-cert",e.certificateAuthorityPath],"iOS Simulator development CA trust",r,{signal:e.signal}),o("Installed the AbsoluteJS development CA into this iOS Simulator trust store.")},ut=(e,t,r)=>r?[e.xcrun,"devicectl","device","process","launch","--terminate-existing","--device",t,e.config.appId]:[e.xcrun,"simctl","launch","--terminate-running-process",t,e.config.appId],Z=(e,t)=>{if(e.exitCode!==0)throw Error(`${t} failed: ${e.stderr.trim()||e.stdout.trim()||`status ${e.exitCode}`}`);return e.stdout.trim()},_e=(e,t)=>{try{let r=JSON.parse(e);if(D(r))return r}catch{}throw Error(`Invalid ${t} JSON from simctl.`)},go=(e)=>{let r=_e(e,"device type").devicetypes;if(!Array.isArray(r))return[];return r.flatMap((o)=>{if(!D(o))return[];let{identifier:n}=o,{name:s}=o;return typeof n==="string"&&typeof s==="string"?[{identifier:n,name:s}]:[]})},fo=(e)=>{let t=_e(e,"runtime"),{runtimes:r}=t;if(!Array.isArray(r))return[];return r.flatMap((o)=>{if(!D(o))return[];let{identifier:n}=o,{name:s}=o,{version:i}=o;if(typeof n!=="string"||typeof s!=="string"||typeof i!=="string")return[];return[{identifier:n,isAvailable:o.isAvailable!==!1,name:s,version:i}]})},bo=(e)=>{let t=_e(e,"device"),{devices:r}=t;if(!D(r))return[];return Object.entries(r).flatMap(([o,n])=>{if(!Array.isArray(n))return[];return n.flatMap((s)=>{if(!D(s))return[];let{name:i}=s,{state:l}=s,{udid:a}=s;if(typeof i!=="string"||typeof l!=="string"||typeof a!=="string")return[];return[{isAvailable:s.isAvailable!==!1,name:i,runtime:o,state:l,udid:a}]})})},dt=(e)=>e.split(".").map((t)=>Number(t)),ho=(e,t)=>{let r=dt(e),o=dt(t),n=Math.max(r.length,o.length);for(let s=0;s<n;s++){let i=(r[s]??0)-(o[s]??0);if(i!==0)return i}return 0},Ao=(e)=>e.filter((t)=>t.isAvailable&&t.identifier.includes("SimRuntime.iOS-")).sort((t,r)=>ho(r.version,t.version))[0],pt=(e)=>Number(/iPhone\s+(\d+)/u.exec(e)?.[1]??0),wo=(e)=>e.filter((t)=>t.name.startsWith("iPhone")).sort((t,r)=>{let o=pt(r.name)-pt(t.name);if(o!==0)return o;let n=r.name.includes("Pro")?1:0,s=t.name.includes("Pro")?1:0;return n-s})[0],At=(e)=>{let t=S(e,".absolutejs","mobile","ios-dev-session");return{configBackup:S(t,"capacitor-config.backup"),infoBackup:S(t,"Info.plist.backup"),journal:S(t,"journal.json"),root:t}},wt=(e)=>S(e,".absolutejs","mobile","ios-native-cache.json"),Y=(e,t)=>{let r=io(ce(e),ce(t));return r===""||!r.startsWith(`..${ao}`)&&r!==".."&&!so(r)},Eo=(e)=>{if(!D(e)||e.format!==xe)return null;let{configBackupPath:t}=e,{infoBackupPath:r}=e,{infoPath:o}=e,{nativeConfigPath:n}=e;if(typeof t!=="string"||typeof r!=="string"||typeof o!=="string"||typeof n!=="string")return null;return{configBackupPath:t,format:xe,infoBackupPath:r,infoPath:o,nativeConfigPath:n}},ie=async(e)=>{let t=At(e);if(!await L(t.journal))return await ye(t.root,{force:!0,recursive:!0}),!1;let r=await ae(t.journal,"utf8").then((o)=>Eo(JSON.parse(o))).catch(()=>null);if(!r||!Y(e,r.nativeConfigPath)||!Y(e,r.infoPath)||!Y(t.root,r.configBackupPath)||!Y(t.root,r.infoBackupPath))throw Error(`Refusing unsafe or invalid iOS dev journal at ${t.journal}.`);if(await L(r.configBackupPath))await at(r.configBackupPath,r.nativeConfigPath);if(await L(r.infoBackupPath))await at(r.infoBackupPath,r.infoPath);return await ye(t.root,{force:!0,recursive:!0}),!0},vo=(e,t)=>{if(!t)return e;let r=/(<key>NSAllowsArbitraryLoads<\/key>\s*)<false\s*\/>/u;if(r.test(e))return e.replace(r,"$1<true/>");if(/<key>NSAllowsArbitraryLoads<\/key>\s*<true\s*\/>/u.test(e))return e;let o=/(<key>NSAppTransportSecurity<\/key>\s*<dict>)/u;if(o.test(e))return e.replace(o,`$1
|
|
339
417
|
<key>NSAllowsArbitraryLoads</key>
|
|
340
418
|
<true/>`);return e.replace(/<dict>/u,`<dict>
|
|
341
419
|
<key>NSAppTransportSecurity</key>
|
|
342
420
|
<dict>
|
|
343
421
|
<key>NSAllowsArbitraryLoads</key>
|
|
344
422
|
<true/>
|
|
345
|
-
</dict>`)},
|
|
346
|
-
`,{flag:"wx"});let
|
|
347
|
-
`),
|
|
348
|
-
`,{flag:"wx"}),await
|
|
349
|
-
`);let
|
|
350
|
-
`),de=(e)=>e instanceof Error?e.message:String(e),
|
|
423
|
+
</dict>`)},So=async(e,t,r,o="localhost")=>{let n=At(e.projectRoot);await ie(e.projectRoot);let s=S(e.nativeDirectory,"App","App","capacitor.config.json"),i=S(e.nativeDirectory,"App","App","Info.plist"),[l,a]=await Promise.all([ae(s,"utf8"),ae(i,"utf8")]),c=JSON.parse(l);if(!D(c))throw Error(`Invalid Capacitor native config at ${s}.`);await G(n.root,{recursive:!0}),await Promise.all([B(n.configBackup,l,{flag:"wx"}),B(n.infoBackup,a,{flag:"wx"})]);let g={configBackupPath:n.configBackup,format:xe,infoBackupPath:n.infoBackup,infoPath:i,nativeConfigPath:s};await B(n.journal,`${JSON.stringify(g,null,"\t")}
|
|
424
|
+
`,{flag:"wx"});let m=new URL(`${r?"https":"http"}://localhost:${t}${e.config.entry}`);m.hostname=no(o)===6?`[${o}]`:o,m.searchParams.set("__absolute_target","capacitor-ios");let b=c.server;c.server={...D(b)?b:{},cleartext:!r,url:m.href},await Promise.all([B(s,`${JSON.stringify(c,null,"\t")}
|
|
425
|
+
`),B(i,vo(a,!r))])},yo=(e)=>{if(!D(e))return null;let{appId:t,fingerprint:r,format:o,installations:n}=e;if(o!==Ie||typeof t!=="string"||typeof r!=="string"||!D(n)||!Object.values(n).every((s)=>typeof s==="string"))return null;return{appId:t,fingerprint:r,format:o,installations:Object.fromEntries(Object.entries(n).map(([s,i])=>[s,String(i)]))}},xo=(e)=>ae(wt(e),"utf8").then((t)=>yo(JSON.parse(t))).catch(()=>null),Et=async(e,t)=>{let r=wt(e),o=`${r}.${process.pid}.${to()}.tmp`;await G(bt(r),{recursive:!0});try{await B(o,`${JSON.stringify(t,null,"\t")}
|
|
426
|
+
`,{flag:"wx"}),await oo(o,r)}finally{await ye(o,{force:!0}).catch(()=>{return})}},Io=async(e,t={})=>it(e.nativeDirectory,t),vt=(e,t)=>{let r=t([e,"simctl","list","devices","available","-j"]);return bo(Z(r,"iOS simulator discovery"))},_o=async(e,t)=>{let r=fo(Z(t([e.xcrun,"simctl","list","runtimes","-j"]),"iOS runtime discovery")),o=Ao(r);if(!o)throw Error("No available iOS Simulator runtime. Run absolute mobile doctor ios --fix.");let[n]=vt(e.xcrun,t).filter((a)=>a.isAvailable&&a.name===Se&&a.runtime===o.identifier).sort((a,c)=>Number(c.state==="Booted")-Number(a.state==="Booted"));if(n)return{created:!1,device:n};let s=go(Z(t([e.xcrun,"simctl","list","devicetypes","-j"]),"iOS device-type discovery")),i=wo(s);if(!i)throw Error("Xcode did not report an iPhone simulator type.");let[l]=Z(t([e.xcrun,"simctl","create",Se,i.identifier,o.identifier]),"iOS simulator creation").split(/\s/u);if(!l)throw Error("simctl did not return the created simulator UDID.");return{created:!0,device:{isAvailable:!0,name:Se,runtime:o.identifier,state:"Shutdown",udid:l}}},Oo=async(e,t,r,o,n)=>{let s=Date.now()+ct,i=async()=>{if(W(n),vt(e.xcrun,r).find((a)=>a.udid===t)?.state==="Booted")return;if(Date.now()>s)throw Error(`iOS simulator ${t} did not finish booting within ${ct/1000}s.`);await o(co),await i()};return i()},To=(e,t,r)=>{if(t.state==="Booted")return;Z(r([e.xcrun,"simctl","boot",t.udid]),"iOS simulator boot")},mt=(e,t,r)=>{let o=r([e.xcrun,"simctl","get_app_container",t,e.config.appId,"app"]);return o.exitCode===0&&o.stdout.trim()?o.stdout.trim():void 0},Po=(e,t,r)=>{let o=r([e.xcrun,"devicectl","device","info","details","--device",t]);if(o.exitCode!==0)throw Error(`Physical iOS device ${JSON.stringify(t)} is unavailable: ${o.stderr.trim()||o.stdout.trim()||"pair it in Xcode Device Hub, trust this Mac, unlock it, and enable Developer Mode."}`)},Do=(e,t,r)=>{let o=r([e.xcrun,"devicectl","device","info","apps","--device",t]);return o.exitCode===0&&o.stdout.includes(e.config.appId)},Co=async(e,t,r,o,n)=>{let s=S(e.projectRoot,".absolutejs","mobile","ios-derived-data",ft("sha256").update(e.config.appId).digest("hex").slice(0,16));await G(s,{recursive:!0}),await P([e.xcodebuild,"-workspace",S(e.nativeDirectory,"App","App.xcworkspace"),"-scheme","App","-configuration","Debug","-destination",`platform=iOS Simulator,id=${t}`,"-derivedDataPath",s,"build"],"iOS simulator build",o,{cwd:e.nativeDirectory,signal:n});let i=S(s,"Build","Products","Debug-iphonesimulator","App.app");if(!await L(i))throw Error(`Xcode did not produce the simulator app at ${i}.`);return i},Ro=async(e,t,r,o)=>{let n=S(e.projectRoot,".absolutejs","mobile","ios-derived-data",ft("sha256").update(e.config.appId).digest("hex").slice(0,16));await G(n,{recursive:!0}),await P([e.xcodebuild,"-workspace",S(e.nativeDirectory,"App","App.xcworkspace"),"-scheme","App","-configuration","Debug","-destination",`platform=iOS,id=${t}`,"-derivedDataPath",n,"-allowProvisioningUpdates","build"],"iOS physical-device build (configure automatic signing and a Development Team in Xcode if this is the first run)",r,{cwd:e.nativeDirectory,signal:o});let s=S(n,"Build","Products","Debug-iphoneos","App.app");if(!await L(s))throw Error(`Xcode did not produce the physical-device app at ${s}.`);return s},No=async(e)=>{if(e.cache?.appId===e.project.config.appId&&e.cache.fingerprint===e.fingerprint&&e.cache.installations[e.identifier]===e.fingerprint&&Do(e.project,e.identifier,e.capture))return e.log("iOS native app is unchanged on the selected physical device; skipped Xcode build and install."),!0;e.log("iOS native inputs changed or the physical-device install is stale; rebuilding."),e.transition("building");let r=await Ro(e.project,e.identifier,e.run,e.signal);return W(e.signal),e.transition("installing"),await P([e.project.xcrun,"devicectl","device","install","app","--device",e.identifier,r],"iOS physical-device app installation",e.run,{signal:e.signal}),await Et(e.project.projectRoot,{appId:e.project.config.appId,fingerprint:e.fingerprint,format:Ie,installations:{...e.cache?.appId===e.project.config.appId?e.cache.installations:{},[e.identifier]:e.fingerprint}}).catch((o)=>e.log(`iOS native cache could not be saved: ${o instanceof Error?o.message:String(o)}`)),!1},Mo=async(e)=>{let t=mt(e.project,e.udid,e.capture);if(e.cache?.appId===e.project.config.appId&&e.cache.fingerprint===e.fingerprint&&t!==void 0&&e.cache.installations[e.udid]===t)return e.log(`iOS native app is unchanged on ${e.udid}; skipped Xcode build and install.`),!0;e.log("iOS native inputs changed or the installed app is stale; rebuilding."),e.transition("building");let o=await Co(e.project,e.udid,e.fingerprint,e.run,e.signal);W(e.signal),e.transition("installing"),await P([e.project.xcrun,"simctl","install",e.udid,o],"iOS simulator app installation",e.run,{signal:e.signal});let n=mt(e.project,e.udid,e.capture);if(n)await Et(e.project.projectRoot,{appId:e.project.config.appId,fingerprint:e.fingerprint,format:Ie,installations:{[e.udid]:n}}).catch((s)=>e.log(`iOS native cache could not be saved: ${s instanceof Error?s.message:String(s)}`));return!1},Lo=/((?:authorization|cookie|password|secret|token|oauth[_-]?code)\s*[:=]\s*)([^\s,;]+)/giu,Uo=new RegExp(String.raw`\bBearer\s+[A-Za-z0-9._~+/-]+=*`,"giu"),$o=/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu,ko=new RegExp(String.raw`\s(Debug|Info|Notice|Error|Fault)\s+.*?\[[^\]]+\]\s+\[([^\]]+)\]\s*(.*)$`,"iu"),jo=(e)=>{let t=Bo(e).trim();if(!t)return null;let r=ko.exec(t),o=r?.[1]?.toLowerCase();return{level:o==="debug"||o==="error"||o==="fault"||o==="notice"?o:"info",message:r?.[3]?.trim()||t,tag:r?.[2]?.trim()||"App"}},Bo=(e)=>e.replaceAll(Uo,"Bearer [REDACTED]").replaceAll($o,"[REDACTED_JWT]").replaceAll(Lo,"$1[REDACTED]").replaceAll(/\p{C}/gu,""),gt=(e,t,r)=>{if(!r.nativeLog)return null;let o=r.startNativeLogs??po,n=r.deviceIdentifier?[e.xcrun,"devicectl","device","process","launch","--console","--terminate-existing","--device",t,e.config.appId]:[e.xcrun,"simctl","spawn",t,"log","stream","--style","compact","--level","debug","--predicate",'process == "App"'];return o(n,{signal:r.signal},(s)=>{let i=jo(s);if(i)r.nativeLog?.(i)})},Ho=[["syncing","Capacitor sync"],["configuring","dev config"],["fingerprinting","fingerprint"],["booting","simulator"],["connecting","device ready"],["enrolling-trust","HTTPS trust"],["checking-native","app check"],["building","Xcode"],["installing","install"],["launching","launch"],["streaming-logs","logs"]],Fo=(e)=>Ho.map(([t,r])=>{let o=e[t];return o===void 0?null:`${r} ${q(o)}`}).filter((t)=>t!==null).join(", "),St=async(e,t)=>{if(se()!=="macos")throw Error("iOS development requires macOS and Xcode.");let r=t.target??"simulator",o=ce(t.projectRoot),n=await et({host:"macos"}),s=n.filter((m)=>m.platform==="ios"&&!(r==="device"&&m.id==="ios.runtime")&&(m.status==="fail"||m.status==="warn"));if(s.length>0)throw Error(`iOS ${r} development is not ready: ${s.map(({label:m})=>m).join(", ")}.`);let i=n.find((m)=>m.id==="ios.xcrun")?.path,l=n.find((m)=>m.id==="ios.xcodebuild")?.path;if(!i||!l)throw Error("Xcode tools disappeared after readiness checks.");let a=S(o,"node_modules",".bin","cap");if(!await L(a))throw Error("Capacitor CLI is not installed. Run absolute mobile init first.");await ot(e,{projectRoot:o}),await G(e.bundleDirectory,{recursive:!0});let c=S(e.bundleDirectory,"index.html");if(!await L(c))await B(c,`<!doctype html><title>AbsoluteJS mobile development</title>
|
|
427
|
+
`);let g=S(e.nativeProjectDirectory,"ios");if(!await L(g)){if(!t.createNativeProject)throw Error("iOS native project has not been created.");if(await(t.run??ht)([a,"add","ios"],{cwd:o})!==0)throw Error("Capacitor iOS project creation failed.")}return{cap:a,config:e,nativeDirectory:g,projectRoot:o,xcodebuild:l,xcrun:i}},Wo=async(e)=>{if(e.transition("connecting"),Po(e.project,e.deviceIdentifier,e.capture),!e.startOptions.https)return{caEnrollmentServer:null,startedSimulator:!1,udid:e.deviceIdentifier};if(!e.startOptions.certificateAuthorityPath)throw Error("Physical iOS HTTPS development requires the AbsoluteJS development CA certificate.");e.transition("enrolling-trust");let r=await(e.startOptions.startCaEnrollmentServer??re)({certificateAuthorityPath:e.startOptions.certificateAuthorityPath,displayHost:e.serverHost});return e.log(`On the iOS device, open ${r.url}, install the AbsoluteJS development CA profile, then enable it under Settings > General > About > Certificate Trust Settings. This public CA endpoint exists only for this dev session.`),{caEnrollmentServer:r,startedSimulator:!1,udid:e.deviceIdentifier}},Go=async(e)=>{e.transition("booting");let t=await _o(e.project,e.capture),{device:r}=t,o=t.created||r.state!=="Booted";return To(e.project,r,e.capture),e.spawn(["open","-a","Simulator","--args","-CurrentDeviceUDID",r.udid]),e.transition("connecting"),await Oo(e.project,r.udid,e.capture,e.sleep,e.startOptions.signal),await P([e.project.xcrun,"simctl","bootstatus",r.udid,"-b"],"iOS simulator boot readiness",e.run,{signal:e.startOptions.signal}),await mo(e.startOptions,r.udid,e.run,e.log),{caEnrollmentServer:null,startedSimulator:o,udid:r.udid}},Oe=async(e)=>{let{project:t}=e,r=e.deviceIdentifier?je(e.deviceIdentifier):void 0,o=r?"device":"simulator",n=r?fe(e.serverHost??""):"localhost",s=e.capture??lo,i=e.run??ht,l=e.sleep??Bun.sleep,a=e.spawn??uo,c=e.log??console.log,g=performance.now(),m=performance.now(),b={},x="syncing",u=(h)=>{if(h===x){e.onStateChange?.(h);return}let C=performance.now(),H=C-m;b[x]=(b[x]??0)+H,e.onPhaseTiming?.({durationMs:H,phase:x,totalMs:C-g}),x=h,m=C,e.onStateChange?.(h)},p=null,f=null,_=async()=>{let h=p;p=null,await h?.close().catch(()=>{return})},A=async(h)=>{if(r&&e.nativeLog){await _(),p=gt(t,h,e);return}await P(ut(t,h,r!==void 0),"iOS app relaunch",i,{signal:e.signal})};try{if(await ie(t.projectRoot),W(e.signal),u("syncing"),await P([t.cap,"sync","ios"],"Capacitor iOS synchronization",i,{cwd:t.projectRoot,signal:e.signal}),u("configuring"),e.embeddedBundle!==!0)await So(t,e.port,e.https===!0,n);W(e.signal);let h=performance.now(),C=Io(t,{includePublicBundle:e.embeddedBundle===!0}).then((M)=>(b.fingerprinting=performance.now()-h,M)),H=r?await Wo({capture:s,deviceIdentifier:r,log:c,project:t,serverHost:n,startOptions:e,transition:u}):await Go({capture:s,log:c,project:t,run:i,sleep:l,spawn:a,startOptions:e,transition:u}),{caEnrollmentServer:Q,startedSimulator:pe,udid:d}=H;f=Q;let w=await C;u("checking-native");let y=await xo(t.projectRoot),T=r?await No({cache:y,capture:s,fingerprint:w,identifier:r,log:c,project:t,run:i,signal:e.signal,transition:u}):await Mo({cache:y,capture:s,fingerprint:w,log:c,project:t,run:i,signal:e.signal,transition:u,udid:d});if(W(e.signal),e.nativeLog)u("streaming-logs");if(p=gt(t,d,e),u("launching"),!r||!p)await P(ut(t,d,r!==void 0),"iOS app launch",i,{signal:e.signal});u("ready"),b.total=performance.now()-g,c(e.embeddedBundle===!0?`iOS ${o} connected with the embedded bundle and backend on port ${e.port} in ${q(b.total)} (${T?"native cache hit":"native build installed"}).`:`iOS ${o} connected with HMR on port ${e.port} in ${q(b.total)} (${T?"native cache hit":"native build installed"}).`),c(`iOS startup: ${Fo(b)}.`);let O=!1,N=async()=>{if(O)return;O=!0,u("closing"),await _(),await f?.close().catch(()=>{return}),f=null,await ie(t.projectRoot),u("closed")};return{close:N,nativeCacheHit:T,startedSimulator:pe,targetKind:o,timings:{...b},udid:d,rebuild:async()=>{if(O)throw Error("iOS development session is closed.");return c("iOS native inputs changed; rebuilding without restarting the dev server."),await N(),Oe(e)},relaunch:async()=>{if(O)throw Error("iOS development session is closed.");u("launching");try{await A(d),u("ready"),c(`iOS app relaunched on the selected ${o}.`)}catch(M){throw u("failed"),M}},screenshot:async(M)=>{if(r)throw Error("Physical iOS screenshots are captured in Xcode Device Hub; the CLI never records a device screen automatically.");let U=ce(t.projectRoot,M);if(!Y(t.projectRoot,U))throw Error("iOS screenshot destination must remain inside the project.");return await G(bt(U),{recursive:!0}),await P([t.xcrun,"simctl","io",d,"screenshot",U],"iOS simulator screenshot",i,{signal:e.signal}),U},get state(){return x}}}catch(h){throw u("failed"),await _(),await f?.close().catch(()=>{return}),await ie(t.projectRoot),h}};var yt="ABSOLUTE_REMOTE_MAC\t",le=2;var E=(e)=>process.stdout.write(`${yt}${JSON.stringify({...e,v:le})}
|
|
428
|
+
`),de=(e)=>e instanceof Error?e.message:String(e),J=(e,t)=>{let r=e.indexOf(t);return r<0?void 0:e[r+1]},Xo=(e)=>{let t=Number(J(e,"--port"));if(!Number.isInteger(t)||t<1||t>65535)throw TypeError("Remote iOS agent requires a valid --port.");return t},Pe=(e,t)=>{let r=J(e,t);if(r===void 0)return;let o=Number(r);if(!Number.isInteger(o)||o<1||o>65535)throw TypeError(`Remote iOS agent requires a valid ${t}.`);return o},qo=(e)=>{let t=J(e,"--mobile-config");if(!t)throw TypeError("Remote iOS agent requires --mobile-config.");let r=JSON.parse(Buffer.from(t,"base64url").toString("utf8"));if(typeof r!=="object"||r===null)throw TypeError("Remote iOS agent received an invalid mobile config.");return r},xt=async(e,t)=>{let r=e.getReader(),o=new TextDecoder,n="";try{while(!0){let{done:s,value:i}=await r.read();if(s)break;n+=o.decode(i,{stream:!0});let l=n.split(/\r?\n/u);n=l.pop()??"",l.forEach(t)}}finally{r.releaseLock()}},De=async(e,t={})=>{let r=Bun.spawn(e,{cwd:t.cwd,env:t.env,signal:t.signal,stderr:"pipe",stdin:"ignore",stdout:"pipe"});return xt(r.stdout,(o)=>E({message:o,type:"log"})),xt(r.stderr,(o)=>E({message:o,type:"log"})),r.exited},Ko=(e,t={})=>{try{let r=Bun.spawnSync(e,{cwd:t.cwd,env:t.env,stderr:"pipe",stdin:"ignore",stdout:"pipe"});return{exitCode:r.exitCode,stderr:r.stderr.toString(),stdout:r.stdout.toString()}}catch(r){return{exitCode:1,stderr:de(r),stdout:""}}},It=(e)=>({engine:"capacitor",nativeCacheHit:e.nativeCacheHit,startedSimulator:e.startedSimulator,targetKind:e.targetKind,timings:e.timings,type:"ready",udid:e.udid}),Yo=async(e)=>{if(e.config.engine!=="expo")throw TypeError("The remote Expo executor requires Expo configuration.");let t=Pe(e.args,"--metro-port");if(t===void 0)throw TypeError("Remote Expo execution requires --metro-port.");let r=Pe(e.args,"--metro-relay-port");if(e.deviceIdentifier&&r===void 0)throw TypeError("Remote physical Expo execution requires --metro-relay-port.");let o=await qe(e.config,{projectRoot:process.cwd()}),n=await Bun.file(ue(o.path,"node_modules","expo","package.json")).exists();if(!n){E({state:"preparing-native",type:"state"});let a=performance.now(),c=await De([process.execPath,"install"],{cwd:o.path});if(c!==0)throw Error(`Remote Expo dependency installation exited with status ${c}.`);E({durationMs:performance.now()-a,phase:"preparing-native",type:"timing"})}let s,i,l;try{s=e.deviceIdentifier?await oe({listenPort:e.port,targetPort:e.relayPort??0}):void 0,i=e.deviceIdentifier?await oe({listenPort:t,targetPort:r??0}):void 0,l=await Be({certificateAuthorityPath:e.certificateAuthorityPath,config:e.config,executable:await Ae(o.path),iosDevice:e.deviceIdentifier,iosOrigin:`${e.args.includes("--https")?"https":"http"}://${e.serverHost??"localhost"}:${e.port}`,metro:"external",metroHost:e.serverHost??"localhost",metroPort:t,platforms:["ios"],log:(c)=>E({message:c,type:"log"}),onPhaseTiming:(c)=>E({...c,type:"timing"}),onStateChange:(c)=>E({state:c,type:"state"})}),E({engine:"expo",nativeCacheHit:n,startedSimulator:!1,targetKind:e.deviceIdentifier?"device":"simulator",timings:l.timings,type:"ready",udid:e.deviceIdentifier??"booted"});let a=_t({input:process.stdin,terminal:!1});try{for await(let c of a){let g;try{if(g=JSON.parse(c),g.v!==le||typeof g.id!=="string"||g.command!=="close")throw Error("Invalid remote Expo request.");await l.close(),E({id:g.id,ok:!0,type:"response"});return}catch(m){E({error:de(m),id:g?.id??"",ok:!1,type:"response"})}}}finally{a.close()}}finally{await l?.close().catch(()=>{return}),await Promise.all([s?.close().catch(()=>{return}),i?.close().catch(()=>{return})])}},Zo=async(e)=>{if(process.platform!=="darwin")throw Error("The AbsoluteJS remote iOS agent can run only on macOS.");let t=process.cwd(),r=Ue(qo(e),t),o=Xo(e),n=J(e,"--ios-device"),s=J(e,"--server-host"),i=Pe(e,"--relay-port");if(n&&(!s||i===void 0))throw TypeError("Remote physical iOS development requires --server-host and --relay-port.");let l=J(e,"--certificate-authority"),a=l?ue(t,".absolutejs","mobile",`remote-dev-ca-${process.pid}.pem`):void 0;if(a&&l)await Jo(ue(t,".absolutejs","mobile"),{recursive:!0}),await zo(a,Buffer.from(l,"base64url"));if(r.engine==="expo"){try{await Yo({args:e,certificateAuthorityPath:a,config:r,deviceIdentifier:n,port:o,relayPort:i,serverHost:s})}finally{if(a)await Te(a,{force:!0})}return}let c=await St(r,{createNativeProject:!1,projectRoot:t,run:De,target:n?"device":"simulator"}),g=n?await oe({listenPort:o,targetPort:i??0}):void 0,m={capture:Ko,certificateAuthorityPath:a,deviceIdentifier:n,https:e.includes("--https"),port:o,project:c,run:De,serverHost:s,log:(u)=>E({message:u,type:"log"}),nativeLog:(u)=>E({entry:u,type:"native-log"}),onPhaseTiming:(u)=>E({...u,type:"timing"}),onStateChange:(u)=>E({state:u,type:"state"}),spawn:(u,p={})=>{Bun.spawn(u,{cwd:p.cwd,env:p.env,signal:p.signal,stderr:"ignore",stdin:"ignore",stdout:"ignore"})}},b;try{b=await Oe(m)}catch(u){if(await g?.close().catch(()=>{return}),a)await Te(a,{force:!0});throw u}E(It(b));let x=_t({input:process.stdin,terminal:!1});try{for await(let u of x){let p;try{if(p=JSON.parse(u),p.v!==le||typeof p.id!=="string")throw Error("Invalid remote Mac request.");let f;if(p.command==="close")await b.close();else if(p.command==="relaunch")await b.relaunch();else if(p.command==="rebuild")b=await b.rebuild(),f=It(b);else if(p.command==="screenshot"){let _=ue(".absolutejs","mobile","remote-screenshot.png"),A=await b.screenshot(_);f={data:(await Vo(A)).toString("base64")}}else throw Error("Unknown remote Mac request.");if(E({id:p.id,ok:!0,result:f,type:"response"}),p.command==="close")return}catch(f){E({error:de(f),id:p?.id??"",ok:!1,type:"response"})}}}finally{if(x.close(),await b.close().catch(()=>{return}),await g?.close().catch(()=>{return}),a)await Te(a,{force:!0})}},Ot=async(e)=>{try{await Zo(e)}catch(t){E({error:de(t),type:"fatal"}),process.exitCode=1}};await Ot(process.argv.slice(2));
|