@stacksjs/desktop-build 0.75.85 → 0.75.87

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  // @bun
2
- var c=(e,t,r)=>()=>{if(e)try{t=e(e=0)}catch(n){r=[n]}if(r)throw r[0];return t};function I(e,t,r={}){if(!t.trim())throw Error("Invite token is required");let n=new URL("/invite",/^https?:\/\//.test(e)?e:`https://${e}`);if(n.searchParams.set("token",t),r.email)n.searchParams.set("email",r.email);if(r.team)n.searchParams.set("team",r.team);if(r.role)n.searchParams.set("role",r.role);if(r.expiresAt){let i=r.expiresAt instanceof Date?r.expiresAt:new Date(r.expiresAt);if(Number.isNaN(i.valueOf()))throw Error("Invite expiration must be a valid date");n.searchParams.set("expires",i.toISOString())}return n.toString()}async function ne(e,t){return Promise.all(e.map(async(r)=>{let n=typeof r==="string"?{email:r}:r;try{A(n.email);let i=await t.createToken(n),s=I(t.baseUrl,i,{email:n.email,team:n.team,role:n.role,expiresAt:t.expiresAt});return await t.send({recipient:n,url:s}),{recipient:n,url:s,delivered:!0}}catch(i){return{recipient:n,url:"",delivered:!1,error:i instanceof Error?i.message:String(i)}}}))}function A(e){if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e))throw Error(`Invalid invite email: ${e}`)}import{createHash as B,createPublicKey as U,verify as L}from"crypto";import{mkdir as j,rename as F,writeFile as O}from"fs/promises";import{dirname as M}from"path";async function ce(e){w(e.manifestUrl);let t=await(e.fetcher||fetch)(e.manifestUrl,{headers:{Accept:"application/json"}});if(!t.ok)throw Error(`Desktop update check failed with HTTP ${t.status}`);let r=m(await t.json());return k(r,e.trustedKeys),P(r.version,e.currentVersion)>0?r:null}async function ue(e,t,r=fetch,n){if(m(e),!n)throw Error("Desktop update staging requires trusted signing keys");k(e,n),w(e.url);let i=await r(e.url);if(!i.ok)throw Error(`Desktop update download failed with HTTP ${i.status}`);let s=new Uint8Array(await i.arrayBuffer());if(s.byteLength!==e.size)throw Error(`Desktop update size mismatch: expected ${e.size}, received ${s.byteLength}`);if(B("sha256").update(s).digest("hex")!==e.sha256.toLowerCase())throw Error("Desktop update checksum mismatch");await j(M(t),{recursive:!0});let h=`${t}.download-${crypto.randomUUID()}`;return await O(h,s,{mode:448}),await F(h,t),{manifest:e,path:t,bytes:s.byteLength}}function P(e,t){let r=u(e),n=u(t);for(let i=0;i<3;i++){let s=r[i]-n[i];if(s!==0)return Math.sign(s)}return 0}function m(e){if(!e||typeof e!=="object")throw Error("Desktop update manifest must be an object");let t=e;if(u(t.version||""),typeof t.url!=="string")throw Error("Desktop update manifest requires a URL");if(typeof t.sha256!=="string"||!/^[a-f0-9]{64}$/i.test(t.sha256))throw Error("Desktop update manifest requires a SHA-256 checksum");if(!Number.isInteger(t.size)||t.size<0)throw Error("Desktop update manifest requires a non-negative integer size");if(typeof t.sourceRevision!=="string"||!/^[0-9a-f]{40}$/.test(t.sourceRevision))throw Error("Desktop update manifest requires a full source revision");if(typeof t.platform!=="string"||!["darwin","linux","win32"].includes(t.platform))throw Error("Desktop update manifest requires a supported platform identifier");if(typeof t.architecture!=="string"||!/^[a-z0-9_-]+$/.test(t.architecture))throw Error("Desktop update manifest requires an architecture");if(typeof t.signingKeyId!=="string"||!/^[a-zA-Z0-9._-]+$/.test(t.signingKeyId))throw Error("Desktop update manifest requires a signing key ID");if(typeof t.signature!=="string"||!/^[A-Za-z0-9_-]+$/.test(t.signature))throw Error("Desktop update manifest requires an Ed25519 signature");return t}function T(e){return Buffer.from(JSON.stringify({version:e.version,url:e.url,sha256:e.sha256.toLowerCase(),size:e.size,sourceRevision:e.sourceRevision,platform:e.platform,architecture:e.architecture,signingKeyId:e.signingKeyId,publishedAt:e.publishedAt||null,notes:e.notes||null}),"utf8")}function k(e,t){let r=t[e.signingKeyId];if(!r)throw Error(`Desktop update signing key is not trusted: ${e.signingKeyId}`);let n="ed25519-public:";if(!r.startsWith(n))throw Error("Desktop update signing key has an invalid encoding");try{let i=U({key:Buffer.from(r.slice(n.length),"base64url"),format:"der",type:"spki"}),s=Buffer.from(e.signature,"base64url");if(i.asymmetricKeyType!=="ed25519"||s.length!==64||!L(null,T(e),i,s))throw Error("invalid")}catch{throw Error("Desktop update manifest signature verification failed")}}function u(e){let t=e.match(/^v?(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/);if(!t)throw Error(`Invalid semantic version: ${e}`);return[Number(t[1]),Number(t[2]),Number(t[3])]}function w(e){let t=new URL(e),r=["localhost","127.0.0.1","::1"].includes(t.hostname);if(t.protocol!=="https:"&&!(t.protocol==="http:"&&r))throw Error("Desktop updates require HTTPS outside loopback development")}var b=()=>{};function N(e=process.platform,t=process.arch){return z.find((r)=>r.platform===e&&r.architecture===t)}function le(e,t=process.platform,r=process.arch){let n=N(t,r);if(!n)throw Error(`Desktop target ${t}/${r} is unsupported`);if(e==="stable"&&(n.status!=="stable"||n.signing!=="enforced"||n.platform==="darwin"&&n.notarization!=="enforced"||!n.installLaunchEvidence||!n.updateRollbackEvidence))throw Error(`Desktop target ${t}/${r} cannot be released as stable; complete stacksjs/stacks#2059, #2062, and #2063 first`);return n}var z;var y=c(()=>{z=[{platform:"darwin",architecture:"arm64",status:"experimental",osVersions:["macOS 27.0 (local run, Craft v0.0.107 published archive)","macOS 15 runner (CI, Craft v0.0.48 from source)"],packageFormat:"DMG + PKG",signing:"pending",notarization:"pending",installLaunchEvidence:"https://github.com/stacksjs/stacks/blob/main/storage/framework/core/desktop/evidence/lifecycle-darwin-arm64.json",updateRollbackEvidence:"https://github.com/stacksjs/stacks/blob/main/storage/framework/core/desktop/evidence/lifecycle-darwin-arm64.json",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["The Stacks-built app bundle, DMG and PKG are unsigned while platform identities remain unprovisioned. The Craft runtime inside them is Developer ID-signed by Craft's own release workflow, which does not make the app signed.","Lifecycle evidence was re-run on 2026-10-03 against the published Craft v0.0.107 archive (craft-darwin-arm64.zip, sha256 98e1a7ae478a8900256add2884d05cc07aea2f3de7d550590d9458abd4fe7725) with `storage/framework/core/desktop/evidence/lifecycle.ts`: PKG and DMG install, a real Craft window launched from the installed bundle, update, a refused truncated update, rollback and uninstall all passed.","That run was local on macOS 27.0, not CI, and installed into the user domain (~/Applications) rather than /Applications. Gatekeeper was not exercised, since nothing carried a quarantine flag. The last CI lifecycle run for this target (https://github.com/stacksjs/stacks/actions/runs/29890804159, macOS 15 runner) used Craft v0.0.48 built from source."]},{platform:"darwin",architecture:"x64",status:"experimental",osVersions:["macOS 15 Intel runner"],packageFormat:"DMG + PKG",signing:"pending",notarization:"pending",installLaunchEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",updateRollbackEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["Lifecycle fixtures are unsigned while platform identities remain unprovisioned.","Lifecycle evidence was produced against Craft v0.0.48 built from source (bf75807). Craft now publishes a prebuilt craft-darwin-x64.zip with every release, and no Stacks lifecycle run has re-verified a prebuilt release yet."]},{platform:"linux",architecture:"x64",status:"experimental",osVersions:["Ubuntu 24.04 runner"],packageFormat:"DEB",signing:"pending",notarization:"not-applicable",installLaunchEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",updateRollbackEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["The lifecycle fixture is unsigned and no package repository support policy is published.","Lifecycle evidence was produced against Craft v0.0.48 built from source (bf75807). Craft now publishes a prebuilt craft-linux-x64.zip with every release, and no Stacks lifecycle run has re-verified a prebuilt release yet."]},{platform:"win32",architecture:"x64",status:"experimental",osVersions:["Windows Server 2025 runner"],packageFormat:"MSI + ZIP",signing:"pending",notarization:"not-applicable",installLaunchEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",updateRollbackEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["The lifecycle fixture is not Authenticode-signed.","Lifecycle evidence was produced against Craft v0.0.48 built from source (bf75807). Craft now publishes a prebuilt craft-windows-x64.zip with every release, and no Stacks lifecycle run has re-verified a prebuilt release yet."]}]});import{closeSync as x,existsSync as p,openSync as D,readSync as E,statSync as K}from"fs";import{dirname as _,join as a}from"path";import o from"process";import{fileURLToPath as q}from"url";import{craftBinaryNotFoundMessage as V,resolveCraftBinary as G}from"craft-native";function S(e=o.env.CRAFT_BIN){return G(e)}function we(e=o.env.CRAFT_BIN,t=(r)=>Bun.which(r)){let r=S(e);if(r!=="craft")return r;let n=t(r);if(!n)throw Error(V(r));return n}function be(e=o.cwd()){return p(a(e,C))}function ye(e){return["build","--compile",...e.minify===!1?[]:["--minify"],"--no-compile-autoload-bunfig","--no-compile-autoload-dotenv",e.entry,"--outfile",e.outfile]}function ve(e=o.cwd()){let t=a(e,C);if(p(t))return t;let r=_(q(import.meta.url)),i=[a(r,"launcher.ts"),a(r,"launcher.js")].find((s)=>p(s));if(!i)throw Error("Desktop launcher entrypoint not found. Reinstall @stacksjs/desktop - the package must ship dist/launcher.js.");return i}function l(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&apos;")}function d(e,t=""){if(typeof e==="string")return`<string>${l(e)}</string>`;if(typeof e==="boolean")return e?"<true/>":"<false/>";if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`Unsupported Info.plist number: ${e}`);return Number.isInteger(e)?`<integer>${e}</integer>`:`<real>${e}</real>`}if(Array.isArray(e)){if(e.length===0)return"<array/>";return`<array>
3
- ${e.map((n)=>`${t} ${d(n,`${t} `)}`).join(`
2
+ var u=(e,t,r)=>()=>{if(e)try{t=e(e=0)}catch(n){r=[n]}if(r)throw r[0];return t};function B(e,t,r={}){if(!t.trim())throw Error("Invite token is required");let n=new URL("/invite",/^https?:\/\//.test(e)?e:`https://${e}`);if(n.searchParams.set("token",t),r.email)n.searchParams.set("email",r.email);if(r.team)n.searchParams.set("team",r.team);if(r.role)n.searchParams.set("role",r.role);if(r.expiresAt){let i=r.expiresAt instanceof Date?r.expiresAt:new Date(r.expiresAt);if(Number.isNaN(i.valueOf()))throw Error("Invite expiration must be a valid date");n.searchParams.set("expires",i.toISOString())}return n.toString()}async function ce(e,t){return Promise.all(e.map(async(r)=>{let n=typeof r==="string"?{email:r}:r;try{L(n.email);let i=await t.createToken(n),s=B(t.baseUrl,i,{email:n.email,team:n.team,role:n.role,expiresAt:t.expiresAt});return await t.send({recipient:n,url:s}),{recipient:n,url:s,delivered:!0}}catch(i){return{recipient:n,url:"",delivered:!1,error:i instanceof Error?i.message:String(i)}}}))}function L(e){if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e))throw Error(`Invalid invite email: ${e}`)}import{createHash as j,createPublicKey as F,verify as O}from"crypto";import{mkdir as M,rename as P,rm as T,writeFile as z}from"fs/promises";import m from"process";import{dirname as N}from"path";async function ge(e){y(e.manifestUrl);let t=await(e.fetcher||fetch)(e.manifestUrl,{headers:{Accept:"application/json"}});if(!t.ok)throw Error(`Desktop update check failed with HTTP ${t.status}`);let r=k(await t.json());return w(r,e.trustedKeys),b(r,e.target),K(r.version,e.currentVersion)>0?r:null}async function he(e,t,r=fetch,n,i){if(k(e),!n)throw Error("Desktop update staging requires trusted signing keys");w(e,n),b(e,i),y(e.url);let s=await r(e.url);if(!s.ok)throw Error(`Desktop update download failed with HTTP ${s.status}`);let o=new Uint8Array(await s.arrayBuffer());if(o.byteLength!==e.size)throw Error(`Desktop update size mismatch: expected ${e.size}, received ${o.byteLength}`);if(j("sha256").update(o).digest("hex")!==e.sha256.toLowerCase())throw Error("Desktop update checksum mismatch");await M(N(t),{recursive:!0});let p=`${t}.download-${crypto.randomUUID()}`;try{await z(p,o,{mode:448}),await P(p,t)}catch(A){throw await T(p,{force:!0}),A}return{manifest:e,path:t,bytes:o.byteLength}}function K(e,t){let r=l(e),n=l(t);for(let i=0;i<3;i++){let s=r[i]-n[i];if(s!==0)return Math.sign(s)}return 0}function k(e){if(!e||typeof e!=="object")throw Error("Desktop update manifest must be an object");let t=e;if(l(t.version||""),typeof t.url!=="string")throw Error("Desktop update manifest requires a URL");if(typeof t.sha256!=="string"||!/^[a-f0-9]{64}$/i.test(t.sha256))throw Error("Desktop update manifest requires a SHA-256 checksum");if(!Number.isInteger(t.size)||t.size<0)throw Error("Desktop update manifest requires a non-negative integer size");if(typeof t.sourceRevision!=="string"||!/^[0-9a-f]{40}$/.test(t.sourceRevision))throw Error("Desktop update manifest requires a full source revision");if(typeof t.platform!=="string"||!["darwin","linux","win32"].includes(t.platform))throw Error("Desktop update manifest requires a supported platform identifier");if(typeof t.architecture!=="string"||!/^[a-z0-9_-]+$/.test(t.architecture))throw Error("Desktop update manifest requires an architecture");if(typeof t.signingKeyId!=="string"||!/^[a-zA-Z0-9._-]+$/.test(t.signingKeyId))throw Error("Desktop update manifest requires a signing key ID");if(typeof t.signature!=="string"||!/^[A-Za-z0-9_-]+$/.test(t.signature))throw Error("Desktop update manifest requires an Ed25519 signature");return t}function _(e){return Buffer.from(JSON.stringify({version:e.version,url:e.url,sha256:e.sha256.toLowerCase(),size:e.size,sourceRevision:e.sourceRevision,platform:e.platform,architecture:e.architecture,signingKeyId:e.signingKeyId,publishedAt:e.publishedAt||null,notes:e.notes||null}),"utf8")}function w(e,t){let r=t[e.signingKeyId];if(!r)throw Error(`Desktop update signing key is not trusted: ${e.signingKeyId}`);let n="ed25519-public:";if(!r.startsWith(n))throw Error("Desktop update signing key has an invalid encoding");try{let i=F({key:Buffer.from(r.slice(n.length),"base64url"),format:"der",type:"spki"}),s=Buffer.from(e.signature,"base64url");if(i.asymmetricKeyType!=="ed25519"||s.length!==64||!O(null,_(e),i,s))throw Error("invalid")}catch{throw Error("Desktop update manifest signature verification failed")}}function b(e,t={platform:m.platform,architecture:m.arch}){if(e.platform!==t.platform||e.architecture!==t.architecture)throw Error(`Desktop update is built for ${e.platform}-${e.architecture}, not this app's ${t.platform}-${t.architecture}`)}function l(e){let t=e.match(/^v?(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/);if(!t)throw Error(`Invalid semantic version: ${e}`);return[Number(t[1]),Number(t[2]),Number(t[3])]}function y(e){let t=new URL(e),r=["localhost","127.0.0.1","::1"].includes(t.hostname);if(t.protocol!=="https:"&&!(t.protocol==="http:"&&r))throw Error("Desktop updates require HTTPS outside loopback development")}var v=()=>{};function V(e=process.platform,t=process.arch){return q.find((r)=>r.platform===e&&r.architecture===t)}function ke(e,t=process.platform,r=process.arch){let n=V(t,r);if(!n)throw Error(`Desktop target ${t}/${r} is unsupported`);if(e==="stable"&&(n.status!=="stable"||n.signing!=="enforced"||n.platform==="darwin"&&n.notarization!=="enforced"||!n.installLaunchEvidence||!n.updateRollbackEvidence))throw Error(`Desktop target ${t}/${r} cannot be released as stable; complete stacksjs/stacks#2059, #2062, and #2063 first`);return n}var q;var x=u(()=>{q=[{platform:"darwin",architecture:"arm64",status:"experimental",osVersions:["macOS 27.0 (local run, Craft v0.0.107 published archive)","macOS 15 runner (CI, Craft v0.0.48 from source)"],packageFormat:"DMG + PKG",signing:"pending",notarization:"pending",installLaunchEvidence:"https://github.com/stacksjs/stacks/blob/main/storage/framework/core/desktop/evidence/lifecycle-darwin-arm64.json",updateRollbackEvidence:"https://github.com/stacksjs/stacks/blob/main/storage/framework/core/desktop/evidence/lifecycle-darwin-arm64.json",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["The Stacks-built app bundle, DMG and PKG are unsigned while platform identities remain unprovisioned. The Craft runtime inside them is Developer ID-signed by Craft's own release workflow, which does not make the app signed.","Lifecycle evidence was re-run on 2026-10-03 against the published Craft v0.0.107 archive (craft-darwin-arm64.zip, sha256 98e1a7ae478a8900256add2884d05cc07aea2f3de7d550590d9458abd4fe7725) with `storage/framework/core/desktop/evidence/lifecycle.ts`: PKG and DMG install, a real Craft window launched from the installed bundle, update, a refused truncated update, rollback and uninstall all passed.","That run was local on macOS 27.0, not CI, and installed into the user domain (~/Applications) rather than /Applications. Gatekeeper was not exercised, since nothing carried a quarantine flag. The last CI lifecycle run for this target (https://github.com/stacksjs/stacks/actions/runs/29890804159, macOS 15 runner) used Craft v0.0.48 built from source."]},{platform:"darwin",architecture:"x64",status:"experimental",osVersions:["macOS 15 Intel runner"],packageFormat:"DMG + PKG",signing:"pending",notarization:"pending",installLaunchEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",updateRollbackEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["Lifecycle fixtures are unsigned while platform identities remain unprovisioned.","Lifecycle evidence was produced against Craft v0.0.48 built from source (bf75807). Craft now publishes a prebuilt craft-darwin-x64.zip with every release, and no Stacks lifecycle run has re-verified a prebuilt release yet."]},{platform:"linux",architecture:"x64",status:"experimental",osVersions:["Ubuntu 24.04 runner"],packageFormat:"DEB",signing:"pending",notarization:"not-applicable",installLaunchEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",updateRollbackEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["The lifecycle fixture is unsigned and no package repository support policy is published.","Lifecycle evidence was produced against Craft v0.0.48 built from source (bf75807). Craft now publishes a prebuilt craft-linux-x64.zip with every release, and no Stacks lifecycle run has re-verified a prebuilt release yet."]},{platform:"win32",architecture:"x64",status:"experimental",osVersions:["Windows Server 2025 runner"],packageFormat:"MSI + ZIP",signing:"pending",notarization:"not-applicable",installLaunchEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",updateRollbackEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["The lifecycle fixture is not Authenticode-signed.","Lifecycle evidence was produced against Craft v0.0.48 built from source (bf75807). Craft now publishes a prebuilt craft-windows-x64.zip with every release, and no Stacks lifecycle run has re-verified a prebuilt release yet."]}]});import{closeSync as E,existsSync as d,openSync as S,readSync as C,statSync as G}from"fs";import{dirname as W,join as c}from"path";import a from"process";import{fileURLToPath as H}from"url";import{craftBinaryNotFoundMessage as Z,resolveCraftBinary as J}from"craft-native";function R(e=a.env.CRAFT_BIN){return J(e)}function Ee(e=a.env.CRAFT_BIN,t=(r)=>Bun.which(r)){let r=R(e);if(r!=="craft")return r;let n=t(r);if(!n)throw Error(Z(r));return n}function Se(e=a.cwd()){return d(c(e,I))}function Ce(e){return["build","--compile",...e.minify===!1?[]:["--minify"],"--no-compile-autoload-bunfig","--no-compile-autoload-dotenv",e.entry,"--outfile",e.outfile]}function Re(e=a.cwd()){let t=c(e,I);if(d(t))return t;let r=W(H(import.meta.url)),i=[c(r,"launcher.ts"),c(r,"launcher.js")].find((s)=>d(s));if(!i)throw Error("Desktop launcher entrypoint not found. Reinstall @stacksjs/desktop - the package must ship dist/launcher.js.");return i}function f(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&apos;")}function g(e,t=""){if(typeof e==="string")return`<string>${f(e)}</string>`;if(typeof e==="boolean")return e?"<true/>":"<false/>";if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`Unsupported Info.plist number: ${e}`);return Number.isInteger(e)?`<integer>${e}</integer>`:`<real>${e}</real>`}if(Array.isArray(e)){if(e.length===0)return"<array/>";return`<array>
3
+ ${e.map((n)=>`${t} ${g(n,`${t} `)}`).join(`
4
4
  `)}
5
5
  ${t}</array>`}if(e&&typeof e==="object"){let r=Object.entries(e);if(r.length===0)return"<dict/>";return`<dict>
6
- ${r.map(([i,s])=>`${t} <key>${l(i)}</key>${d(s,`${t} `)}`).join(`
6
+ ${r.map(([i,s])=>`${t} <key>${f(i)}</key>${g(s,`${t} `)}`).join(`
7
7
  `)}
8
- ${t}</dict>`}throw Error(`Unsupported Info.plist value: ${JSON.stringify(e)??String(e)}`)}function xe(e){let t=Object.keys(e).filter((i)=>v.has(i)),r=Object.entries(e).filter(([i])=>!v.has(i));if(r.length===0)return{xml:"",ignored:t};return{xml:`
9
- ${r.map(([i,s])=>` <key>${l(i)}</key>${d(s," ")}`).join(`
10
- `)}`,ignored:t}}function W(e,t={}){if(!Number.isInteger(e)||e<1||e>65535)throw RangeError(`Invalid desktop development port: ${e}`);let r=t.url||o.env.APP_URL;if(!r)return"https://stacks.test";let n=/^https?:\/\//.test(r)?r:`https://${r}`;return new URL(n).toString().replace(/\/$/,"")}function H(e,t={}){let r=[S(t.craftBin),W(e,t),"--title",t.title||"Stacks","--width",String(t.width||1400),"--height",String(t.height||900)];if(t.hotReload!==!1)r.push("--hot-reload");if(t.devTools===!1)r.push("--no-devtools");if(t.darkMode)r.push("--dark");if(t.systemTray)r.push("--system-tray");if(t.hideDockIcon)r.push("--hide-dock-icon");if(t.menubarOnly)r.push("--menubar-only");return r}function De(e,t="stable"){if(!/^[a-z0-9-]+$/i.test(t))throw Error("Update channel may only contain letters, numbers, and hyphens");return new URL(`/desktop/updates/${t}.json`,/^https?:\/\//.test(e)?e:`https://${e}`).toString()}async function Z(e){Bun.spawn(e,{stdin:"inherit",stdout:"inherit",stderr:"inherit"}).unref()}async function Ee(e,t={},r=Z){return await r(H(e,t)),!0}function Q(e){try{let t=D(e,"r");try{let r=Buffer.alloc(4);if(E(t,r,0,4,0)<4)return!1;return X.has(r.readUInt32BE(0))}finally{x(t)}}catch{return!1}}function Se(e){return Q(e)?"MacOS":"Resources"}function Ce(e){return["codesign","--force",e.identity==="-"?"--timestamp=none":"--timestamp","--options","runtime",...e.entitlements?["--entitlements",e.entitlements]:[],"--sign",e.identity,e.target]}function Re(e,t=ee){let r=t(e);if(!r||r.size<f)return!1;return r.tail.includes(J)}function ee(e){try{let{size:t}=K(e);if(t<f)return{size:t,tail:""};let r=D(e,"r");try{let n=Math.min(Y,t),i=Buffer.alloc(n);return E(r,i,0,n,t-n),{size:t,tail:i.toString("latin1")}}finally{x(r)}}catch{return null}}function Ie(e){if(e.length<2)return null;let t=[...e].sort((s,g)=>g.bytes-s.bytes),n=t.slice(1).length*f,i=t.map((s)=>`${s.name} (${(s.bytes/1024/1024).toFixed(1)} MB)`).join(", ");return`This bundle ships ${e.length} Bun-compiled executables: ${i}. Each embeds a full copy of the Bun runtime, so at least ${(n/1024/1024).toFixed(0)} MB of the bundle is the same runtime repeated. `+"Compiling one binary that dispatches on a subcommand \u2014 `MyApp agent`, `MyApp worker` \u2014 keeps the helpers as separate processes and pays for the runtime once."}var C="app/Desktop/launcher.ts",v,J="---- Bun!",Y=8388608,f=41943040,X;var te=c(()=>{b();y();v=new Set(["CFBundleName","CFBundleDisplayName","CFBundleIdentifier","CFBundleVersion","CFBundleShortVersionString","CFBundleExecutable","CFBundlePackageType","CFBundleInfoDictionaryVersion","CFBundleIconFile"]);X=new Set([4277009102,3472551422,4277009103,3489328638,3405691582,3199925962])});te();export{J as BUN_EXECUTABLE_MARKER,v as RESERVED_PLIST_KEYS,C as USERLAND_LAUNCHER,le as assertDesktopReleaseChannel,Se as bundleDirectoryFor,ce as checkForDesktopUpdate,Ce as codesignArgs,P as compareVersions,H as craftDevCommand,I as createInviteLink,De as createUpdateManifestUrl,Ie as describeRuntimeDuplication,ye as desktopLauncherCompileArgs,N as desktopSupport,z as desktopSupportMatrix,be as hasUserlandDesktopLauncher,Q as isMachO,Re as looksLikeBunExecutable,Ee as openDevWindow,d as plistValue,xe as renderUserlandPlistEntries,S as resolveCraftBinary,we as resolveCraftExecutable,ve as resolveDesktopLauncher,W as resolveDevWindowUrl,ne as sendDesktopInvites,ue as stageDesktopUpdate,T as updateManifestPayload,m as validateUpdateManifest,k as verifyUpdateManifestSignature,l as xmlEscape};
8
+ ${t}</dict>`}throw Error(`Unsupported Info.plist value: ${JSON.stringify(e)??String(e)}`)}function Ie(e){let t=Object.keys(e).filter((i)=>D.has(i)),r=Object.entries(e).filter(([i])=>!D.has(i));if(r.length===0)return{xml:"",ignored:t};return{xml:`
9
+ ${r.map(([i,s])=>` <key>${f(i)}</key>${g(s," ")}`).join(`
10
+ `)}`,ignored:t}}function Y(e,t={}){if(!Number.isInteger(e)||e<1||e>65535)throw RangeError(`Invalid desktop development port: ${e}`);let r=t.url||a.env.APP_URL;if(!r)return"https://stacks.test";let n=/^https?:\/\//.test(r)?r:`https://${r}`;return new URL(n).toString().replace(/\/$/,"")}function X(e,t={}){let r=[R(t.craftBin),Y(e,t),"--title",t.title||"Stacks","--width",String(t.width||1400),"--height",String(t.height||900)];if(t.hotReload!==!1)r.push("--hot-reload");if(t.devTools===!1)r.push("--no-devtools");if(t.darkMode)r.push("--dark");if(t.systemTray)r.push("--system-tray");if(t.hideDockIcon)r.push("--hide-dock-icon");if(t.menubarOnly)r.push("--menubar-only");return r}function $e(e,t="stable"){if(!/^[a-z0-9-]+$/i.test(t))throw Error("Update channel may only contain letters, numbers, and hyphens");return new URL(`/desktop/updates/${t}.json`,/^https?:\/\//.test(e)?e:`https://${e}`).toString()}async function Q(e){Bun.spawn(e,{stdin:"inherit",stdout:"inherit",stderr:"inherit"}).unref()}async function Ae(e,t={},r=Q){return await r(X(e,t)),!0}function ne(e){try{let t=S(e,"r");try{let r=Buffer.alloc(4);if(C(t,r,0,4,0)<4)return!1;return re.has(r.readUInt32BE(0))}finally{E(t)}}catch{return!1}}function Ue(e){return ne(e)?"MacOS":"Resources"}function Be(e){return["codesign","--force",e.identity==="-"?"--timestamp=none":"--timestamp","--options","runtime",...e.entitlements?["--entitlements",e.entitlements]:[],"--sign",e.identity,e.target]}function Le(e,t=ie){let r=t(e);if(!r||r.size<h)return!1;return r.tail.includes(ee)}function ie(e){try{let{size:t}=G(e);if(t<h)return{size:t,tail:""};let r=S(e,"r");try{let n=Math.min(te,t),i=Buffer.alloc(n);return C(r,i,0,n,t-n),{size:t,tail:i.toString("latin1")}}finally{E(r)}}catch{return null}}function je(e){if(e.length<2)return null;let t=[...e].sort((s,o)=>o.bytes-s.bytes),n=t.slice(1).length*h,i=t.map((s)=>`${s.name} (${(s.bytes/1024/1024).toFixed(1)} MB)`).join(", ");return`This bundle ships ${e.length} Bun-compiled executables: ${i}. Each embeds a full copy of the Bun runtime, so at least ${(n/1024/1024).toFixed(0)} MB of the bundle is the same runtime repeated. `+"Compiling one binary that dispatches on a subcommand \u2014 `MyApp agent`, `MyApp worker` \u2014 keeps the helpers as separate processes and pays for the runtime once."}var I="app/Desktop/launcher.ts",D,ee="---- Bun!",te=8388608,h=41943040,re;var se=u(()=>{v();x();D=new Set(["CFBundleName","CFBundleDisplayName","CFBundleIdentifier","CFBundleVersion","CFBundleShortVersionString","CFBundleExecutable","CFBundlePackageType","CFBundleInfoDictionaryVersion","CFBundleIconFile"]);re=new Set([4277009102,3472551422,4277009103,3489328638,3405691582,3199925962])});se();export{ee as BUN_EXECUTABLE_MARKER,D as RESERVED_PLIST_KEYS,I as USERLAND_LAUNCHER,ke as assertDesktopReleaseChannel,Ue as bundleDirectoryFor,ge as checkForDesktopUpdate,Be as codesignArgs,K as compareVersions,X as craftDevCommand,B as createInviteLink,$e as createUpdateManifestUrl,je as describeRuntimeDuplication,Ce as desktopLauncherCompileArgs,V as desktopSupport,q as desktopSupportMatrix,Se as hasUserlandDesktopLauncher,ne as isMachO,Le as looksLikeBunExecutable,Ae as openDevWindow,g as plistValue,Ie as renderUserlandPlistEntries,R as resolveCraftBinary,Ee as resolveCraftExecutable,Re as resolveDesktopLauncher,Y as resolveDevWindowUrl,ce as sendDesktopInvites,he as stageDesktopUpdate,_ as updateManifestPayload,k as validateUpdateManifest,w as verifyUpdateManifestSignature,f as xmlEscape};
@@ -1,13 +1,13 @@
1
1
  // @bun
2
- var O=(e,t,n)=>()=>{if(e)try{t=e(e=0)}catch(r){n=[r]}if(n)throw n[0];return t};function Ke(e,t,n={}){if(!t.trim())throw Error("Invite token is required");let r=new URL("/invite",/^https?:\/\//.test(e)?e:`https://${e}`);if(r.searchParams.set("token",t),n.email)r.searchParams.set("email",n.email);if(n.team)r.searchParams.set("team",n.team);if(n.role)r.searchParams.set("role",n.role);if(n.expiresAt){let s=n.expiresAt instanceof Date?n.expiresAt:new Date(n.expiresAt);if(Number.isNaN(s.valueOf()))throw Error("Invite expiration must be a valid date");r.searchParams.set("expires",s.toISOString())}return r.toString()}async function Zt(e,t){return Promise.all(e.map(async(n)=>{let r=typeof n==="string"?{email:n}:n;try{Je(r.email);let s=await t.createToken(r),o=Ke(t.baseUrl,s,{email:r.email,team:r.team,role:r.role,expiresAt:t.expiresAt});return await t.send({recipient:r,url:o}),{recipient:r,url:o,delivered:!0}}catch(s){return{recipient:r,url:"",delivered:!1,error:s instanceof Error?s.message:String(s)}}}))}function Je(e){if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e))throw Error(`Invalid invite email: ${e}`)}import{createHash as Ze,createPublicKey as Ye,verify as Xe}from"crypto";import{mkdir as Qe,rename as et,writeFile as tt}from"fs/promises";import{dirname as nt}from"path";async function tn(e){ce(e.manifestUrl);let t=await(e.fetcher||fetch)(e.manifestUrl,{headers:{Accept:"application/json"}});if(!t.ok)throw Error(`Desktop update check failed with HTTP ${t.status}`);let n=ae(await t.json());return ue(n,e.trustedKeys),rt(n.version,e.currentVersion)>0?n:null}async function nn(e,t,n=fetch,r){if(ae(e),!r)throw Error("Desktop update staging requires trusted signing keys");ue(e,r),ce(e.url);let s=await n(e.url);if(!s.ok)throw Error(`Desktop update download failed with HTTP ${s.status}`);let o=new Uint8Array(await s.arrayBuffer());if(o.byteLength!==e.size)throw Error(`Desktop update size mismatch: expected ${e.size}, received ${o.byteLength}`);if(Ze("sha256").update(o).digest("hex")!==e.sha256.toLowerCase())throw Error("Desktop update checksum mismatch");await Qe(nt(t),{recursive:!0});let i=`${t}.download-${crypto.randomUUID()}`;return await tt(i,o,{mode:448}),await et(i,t),{manifest:e,path:t,bytes:o.byteLength}}function rt(e,t){let n=_(e),r=_(t);for(let s=0;s<3;s++){let o=n[s]-r[s];if(o!==0)return Math.sign(o)}return 0}function ae(e){if(!e||typeof e!=="object")throw Error("Desktop update manifest must be an object");let t=e;if(_(t.version||""),typeof t.url!=="string")throw Error("Desktop update manifest requires a URL");if(typeof t.sha256!=="string"||!/^[a-f0-9]{64}$/i.test(t.sha256))throw Error("Desktop update manifest requires a SHA-256 checksum");if(!Number.isInteger(t.size)||t.size<0)throw Error("Desktop update manifest requires a non-negative integer size");if(typeof t.sourceRevision!=="string"||!/^[0-9a-f]{40}$/.test(t.sourceRevision))throw Error("Desktop update manifest requires a full source revision");if(typeof t.platform!=="string"||!["darwin","linux","win32"].includes(t.platform))throw Error("Desktop update manifest requires a supported platform identifier");if(typeof t.architecture!=="string"||!/^[a-z0-9_-]+$/.test(t.architecture))throw Error("Desktop update manifest requires an architecture");if(typeof t.signingKeyId!=="string"||!/^[a-zA-Z0-9._-]+$/.test(t.signingKeyId))throw Error("Desktop update manifest requires a signing key ID");if(typeof t.signature!=="string"||!/^[A-Za-z0-9_-]+$/.test(t.signature))throw Error("Desktop update manifest requires an Ed25519 signature");return t}function st(e){return Buffer.from(JSON.stringify({version:e.version,url:e.url,sha256:e.sha256.toLowerCase(),size:e.size,sourceRevision:e.sourceRevision,platform:e.platform,architecture:e.architecture,signingKeyId:e.signingKeyId,publishedAt:e.publishedAt||null,notes:e.notes||null}),"utf8")}function ue(e,t){let n=t[e.signingKeyId];if(!n)throw Error(`Desktop update signing key is not trusted: ${e.signingKeyId}`);let r="ed25519-public:";if(!n.startsWith(r))throw Error("Desktop update signing key has an invalid encoding");try{let s=Ye({key:Buffer.from(n.slice(r.length),"base64url"),format:"der",type:"spki"}),o=Buffer.from(e.signature,"base64url");if(s.asymmetricKeyType!=="ed25519"||o.length!==64||!Xe(null,st(e),s,o))throw Error("invalid")}catch{throw Error("Desktop update manifest signature verification failed")}}function _(e){let t=e.match(/^v?(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/);if(!t)throw Error(`Invalid semantic version: ${e}`);return[Number(t[1]),Number(t[2]),Number(t[3])]}function ce(e){let t=new URL(e),n=["localhost","127.0.0.1","::1"].includes(t.hostname);if(t.protocol!=="https:"&&!(t.protocol==="http:"&&n))throw Error("Desktop updates require HTTPS outside loopback development")}var le=()=>{};function it(e=process.platform,t=process.arch){return ot.find((n)=>n.platform===e&&n.architecture===t)}function sn(e,t=process.platform,n=process.arch){let r=it(t,n);if(!r)throw Error(`Desktop target ${t}/${n} is unsupported`);if(e==="stable"&&(r.status!=="stable"||r.signing!=="enforced"||r.platform==="darwin"&&r.notarization!=="enforced"||!r.installLaunchEvidence||!r.updateRollbackEvidence))throw Error(`Desktop target ${t}/${n} cannot be released as stable; complete stacksjs/stacks#2059, #2062, and #2063 first`);return r}var ot;var de=O(()=>{ot=[{platform:"darwin",architecture:"arm64",status:"experimental",osVersions:["macOS 27.0 (local run, Craft v0.0.107 published archive)","macOS 15 runner (CI, Craft v0.0.48 from source)"],packageFormat:"DMG + PKG",signing:"pending",notarization:"pending",installLaunchEvidence:"https://github.com/stacksjs/stacks/blob/main/storage/framework/core/desktop/evidence/lifecycle-darwin-arm64.json",updateRollbackEvidence:"https://github.com/stacksjs/stacks/blob/main/storage/framework/core/desktop/evidence/lifecycle-darwin-arm64.json",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["The Stacks-built app bundle, DMG and PKG are unsigned while platform identities remain unprovisioned. The Craft runtime inside them is Developer ID-signed by Craft's own release workflow, which does not make the app signed.","Lifecycle evidence was re-run on 2026-10-03 against the published Craft v0.0.107 archive (craft-darwin-arm64.zip, sha256 98e1a7ae478a8900256add2884d05cc07aea2f3de7d550590d9458abd4fe7725) with `storage/framework/core/desktop/evidence/lifecycle.ts`: PKG and DMG install, a real Craft window launched from the installed bundle, update, a refused truncated update, rollback and uninstall all passed.","That run was local on macOS 27.0, not CI, and installed into the user domain (~/Applications) rather than /Applications. Gatekeeper was not exercised, since nothing carried a quarantine flag. The last CI lifecycle run for this target (https://github.com/stacksjs/stacks/actions/runs/29890804159, macOS 15 runner) used Craft v0.0.48 built from source."]},{platform:"darwin",architecture:"x64",status:"experimental",osVersions:["macOS 15 Intel runner"],packageFormat:"DMG + PKG",signing:"pending",notarization:"pending",installLaunchEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",updateRollbackEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["Lifecycle fixtures are unsigned while platform identities remain unprovisioned.","Lifecycle evidence was produced against Craft v0.0.48 built from source (bf75807). Craft now publishes a prebuilt craft-darwin-x64.zip with every release, and no Stacks lifecycle run has re-verified a prebuilt release yet."]},{platform:"linux",architecture:"x64",status:"experimental",osVersions:["Ubuntu 24.04 runner"],packageFormat:"DEB",signing:"pending",notarization:"not-applicable",installLaunchEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",updateRollbackEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["The lifecycle fixture is unsigned and no package repository support policy is published.","Lifecycle evidence was produced against Craft v0.0.48 built from source (bf75807). Craft now publishes a prebuilt craft-linux-x64.zip with every release, and no Stacks lifecycle run has re-verified a prebuilt release yet."]},{platform:"win32",architecture:"x64",status:"experimental",osVersions:["Windows Server 2025 runner"],packageFormat:"MSI + ZIP",signing:"pending",notarization:"not-applicable",installLaunchEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",updateRollbackEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["The lifecycle fixture is not Authenticode-signed.","Lifecycle evidence was produced against Craft v0.0.48 built from source (bf75807). Craft now publishes a prebuilt craft-windows-x64.zip with every release, and no Stacks lifecycle run has re-verified a prebuilt release yet."]}]});import{closeSync as me,existsSync as z,openSync as fe,readSync as ge,statSync as at}from"fs";import{dirname as ut,join as F}from"path";import E from"process";import{fileURLToPath as ct}from"url";import{craftBinaryNotFoundMessage as lt,resolveCraftBinary as dt}from"craft-native";function he(e=E.env.CRAFT_BIN){return dt(e)}function be(e=E.env.CRAFT_BIN,t=(n)=>Bun.which(n)){let n=he(e);if(n!=="craft")return n;let r=t(n);if(!r)throw Error(lt(n));return r}function pn(e=E.cwd()){return z(F(e,we))}function mn(e){return["build","--compile",...e.minify===!1?[]:["--minify"],"--no-compile-autoload-bunfig","--no-compile-autoload-dotenv",e.entry,"--outfile",e.outfile]}function fn(e=E.cwd()){let t=F(e,we);if(z(t))return t;let n=ut(ct(import.meta.url)),s=[F(n,"launcher.ts"),F(n,"launcher.js")].find((o)=>z(o));if(!s)throw Error("Desktop launcher entrypoint not found. Reinstall @stacksjs/desktop - the package must ship dist/launcher.js.");return s}function H(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&apos;")}function q(e,t=""){if(typeof e==="string")return`<string>${H(e)}</string>`;if(typeof e==="boolean")return e?"<true/>":"<false/>";if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`Unsupported Info.plist number: ${e}`);return Number.isInteger(e)?`<integer>${e}</integer>`:`<real>${e}</real>`}if(Array.isArray(e)){if(e.length===0)return"<array/>";return`<array>
2
+ var O=(e,t,n)=>()=>{if(e)try{t=e(e=0)}catch(r){n=[r]}if(n)throw n[0];return t};function Ze(e,t,n={}){if(!t.trim())throw Error("Invite token is required");let r=new URL("/invite",/^https?:\/\//.test(e)?e:`https://${e}`);if(r.searchParams.set("token",t),n.email)r.searchParams.set("email",n.email);if(n.team)r.searchParams.set("team",n.team);if(n.role)r.searchParams.set("role",n.role);if(n.expiresAt){let s=n.expiresAt instanceof Date?n.expiresAt:new Date(n.expiresAt);if(Number.isNaN(s.valueOf()))throw Error("Invite expiration must be a valid date");r.searchParams.set("expires",s.toISOString())}return r.toString()}async function Qt(e,t){return Promise.all(e.map(async(n)=>{let r=typeof n==="string"?{email:n}:n;try{Ye(r.email);let s=await t.createToken(r),o=Ze(t.baseUrl,s,{email:r.email,team:r.team,role:r.role,expiresAt:t.expiresAt});return await t.send({recipient:r,url:o}),{recipient:r,url:o,delivered:!0}}catch(s){return{recipient:r,url:"",delivered:!1,error:s instanceof Error?s.message:String(s)}}}))}function Ye(e){if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e))throw Error(`Invalid invite email: ${e}`)}import{createHash as Xe,createPublicKey as Qe,verify as et}from"crypto";import{mkdir as tt,rename as nt,rm as rt,writeFile as st}from"fs/promises";import ae from"process";import{dirname as ot}from"path";async function on(e){de(e.manifestUrl);let t=await(e.fetcher||fetch)(e.manifestUrl,{headers:{Accept:"application/json"}});if(!t.ok)throw Error(`Desktop update check failed with HTTP ${t.status}`);let n=ue(await t.json());return ce(n,e.trustedKeys),le(n,e.target),it(n.version,e.currentVersion)>0?n:null}async function an(e,t,n=fetch,r,s){if(ue(e),!r)throw Error("Desktop update staging requires trusted signing keys");ce(e,r),le(e,s),de(e.url);let o=await n(e.url);if(!o.ok)throw Error(`Desktop update download failed with HTTP ${o.status}`);let a=new Uint8Array(await o.arrayBuffer());if(a.byteLength!==e.size)throw Error(`Desktop update size mismatch: expected ${e.size}, received ${a.byteLength}`);if(Xe("sha256").update(a).digest("hex")!==e.sha256.toLowerCase())throw Error("Desktop update checksum mismatch");await tt(ot(t),{recursive:!0});let u=`${t}.download-${crypto.randomUUID()}`;try{await st(u,a,{mode:448}),await nt(u,t)}catch(c){throw await rt(u,{force:!0}),c}return{manifest:e,path:t,bytes:a.byteLength}}function it(e,t){let n=_(e),r=_(t);for(let s=0;s<3;s++){let o=n[s]-r[s];if(o!==0)return Math.sign(o)}return 0}function ue(e){if(!e||typeof e!=="object")throw Error("Desktop update manifest must be an object");let t=e;if(_(t.version||""),typeof t.url!=="string")throw Error("Desktop update manifest requires a URL");if(typeof t.sha256!=="string"||!/^[a-f0-9]{64}$/i.test(t.sha256))throw Error("Desktop update manifest requires a SHA-256 checksum");if(!Number.isInteger(t.size)||t.size<0)throw Error("Desktop update manifest requires a non-negative integer size");if(typeof t.sourceRevision!=="string"||!/^[0-9a-f]{40}$/.test(t.sourceRevision))throw Error("Desktop update manifest requires a full source revision");if(typeof t.platform!=="string"||!["darwin","linux","win32"].includes(t.platform))throw Error("Desktop update manifest requires a supported platform identifier");if(typeof t.architecture!=="string"||!/^[a-z0-9_-]+$/.test(t.architecture))throw Error("Desktop update manifest requires an architecture");if(typeof t.signingKeyId!=="string"||!/^[a-zA-Z0-9._-]+$/.test(t.signingKeyId))throw Error("Desktop update manifest requires a signing key ID");if(typeof t.signature!=="string"||!/^[A-Za-z0-9_-]+$/.test(t.signature))throw Error("Desktop update manifest requires an Ed25519 signature");return t}function at(e){return Buffer.from(JSON.stringify({version:e.version,url:e.url,sha256:e.sha256.toLowerCase(),size:e.size,sourceRevision:e.sourceRevision,platform:e.platform,architecture:e.architecture,signingKeyId:e.signingKeyId,publishedAt:e.publishedAt||null,notes:e.notes||null}),"utf8")}function ce(e,t){let n=t[e.signingKeyId];if(!n)throw Error(`Desktop update signing key is not trusted: ${e.signingKeyId}`);let r="ed25519-public:";if(!n.startsWith(r))throw Error("Desktop update signing key has an invalid encoding");try{let s=Qe({key:Buffer.from(n.slice(r.length),"base64url"),format:"der",type:"spki"}),o=Buffer.from(e.signature,"base64url");if(s.asymmetricKeyType!=="ed25519"||o.length!==64||!et(null,at(e),s,o))throw Error("invalid")}catch{throw Error("Desktop update manifest signature verification failed")}}function le(e,t={platform:ae.platform,architecture:ae.arch}){if(e.platform!==t.platform||e.architecture!==t.architecture)throw Error(`Desktop update is built for ${e.platform}-${e.architecture}, not this app's ${t.platform}-${t.architecture}`)}function _(e){let t=e.match(/^v?(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/);if(!t)throw Error(`Invalid semantic version: ${e}`);return[Number(t[1]),Number(t[2]),Number(t[3])]}function de(e){let t=new URL(e),n=["localhost","127.0.0.1","::1"].includes(t.hostname);if(t.protocol!=="https:"&&!(t.protocol==="http:"&&n))throw Error("Desktop updates require HTTPS outside loopback development")}var pe=()=>{};function ct(e=process.platform,t=process.arch){return ut.find((n)=>n.platform===e&&n.architecture===t)}function cn(e,t=process.platform,n=process.arch){let r=ct(t,n);if(!r)throw Error(`Desktop target ${t}/${n} is unsupported`);if(e==="stable"&&(r.status!=="stable"||r.signing!=="enforced"||r.platform==="darwin"&&r.notarization!=="enforced"||!r.installLaunchEvidence||!r.updateRollbackEvidence))throw Error(`Desktop target ${t}/${n} cannot be released as stable; complete stacksjs/stacks#2059, #2062, and #2063 first`);return r}var ut;var me=O(()=>{ut=[{platform:"darwin",architecture:"arm64",status:"experimental",osVersions:["macOS 27.0 (local run, Craft v0.0.107 published archive)","macOS 15 runner (CI, Craft v0.0.48 from source)"],packageFormat:"DMG + PKG",signing:"pending",notarization:"pending",installLaunchEvidence:"https://github.com/stacksjs/stacks/blob/main/storage/framework/core/desktop/evidence/lifecycle-darwin-arm64.json",updateRollbackEvidence:"https://github.com/stacksjs/stacks/blob/main/storage/framework/core/desktop/evidence/lifecycle-darwin-arm64.json",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["The Stacks-built app bundle, DMG and PKG are unsigned while platform identities remain unprovisioned. The Craft runtime inside them is Developer ID-signed by Craft's own release workflow, which does not make the app signed.","Lifecycle evidence was re-run on 2026-10-03 against the published Craft v0.0.107 archive (craft-darwin-arm64.zip, sha256 98e1a7ae478a8900256add2884d05cc07aea2f3de7d550590d9458abd4fe7725) with `storage/framework/core/desktop/evidence/lifecycle.ts`: PKG and DMG install, a real Craft window launched from the installed bundle, update, a refused truncated update, rollback and uninstall all passed.","That run was local on macOS 27.0, not CI, and installed into the user domain (~/Applications) rather than /Applications. Gatekeeper was not exercised, since nothing carried a quarantine flag. The last CI lifecycle run for this target (https://github.com/stacksjs/stacks/actions/runs/29890804159, macOS 15 runner) used Craft v0.0.48 built from source."]},{platform:"darwin",architecture:"x64",status:"experimental",osVersions:["macOS 15 Intel runner"],packageFormat:"DMG + PKG",signing:"pending",notarization:"pending",installLaunchEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",updateRollbackEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["Lifecycle fixtures are unsigned while platform identities remain unprovisioned.","Lifecycle evidence was produced against Craft v0.0.48 built from source (bf75807). Craft now publishes a prebuilt craft-darwin-x64.zip with every release, and no Stacks lifecycle run has re-verified a prebuilt release yet."]},{platform:"linux",architecture:"x64",status:"experimental",osVersions:["Ubuntu 24.04 runner"],packageFormat:"DEB",signing:"pending",notarization:"not-applicable",installLaunchEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",updateRollbackEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["The lifecycle fixture is unsigned and no package repository support policy is published.","Lifecycle evidence was produced against Craft v0.0.48 built from source (bf75807). Craft now publishes a prebuilt craft-linux-x64.zip with every release, and no Stacks lifecycle run has re-verified a prebuilt release yet."]},{platform:"win32",architecture:"x64",status:"experimental",osVersions:["Windows Server 2025 runner"],packageFormat:"MSI + ZIP",signing:"pending",notarization:"not-applicable",installLaunchEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",updateRollbackEvidence:"https://github.com/stacksjs/stacks/actions/runs/29890804159",packagingEvidence:"https://github.com/stacksjs/protocol/blob/main/evidence/craft.json",blockingIssues:["https://github.com/stacksjs/stacks/issues/2062"],limitations:["The lifecycle fixture is not Authenticode-signed.","Lifecycle evidence was produced against Craft v0.0.48 built from source (bf75807). Craft now publishes a prebuilt craft-windows-x64.zip with every release, and no Stacks lifecycle run has re-verified a prebuilt release yet."]}]});import{closeSync as ge,existsSync as z,openSync as he,readSync as be,statSync as lt}from"fs";import{dirname as dt,join as F}from"path";import E from"process";import{fileURLToPath as pt}from"url";import{craftBinaryNotFoundMessage as mt,resolveCraftBinary as ft}from"craft-native";function we(e=E.env.CRAFT_BIN){return ft(e)}function ye(e=E.env.CRAFT_BIN,t=(n)=>Bun.which(n)){let n=we(e);if(n!=="craft")return n;let r=t(n);if(!r)throw Error(mt(n));return r}function hn(e=E.cwd()){return z(F(e,ke))}function bn(e){return["build","--compile",...e.minify===!1?[]:["--minify"],"--no-compile-autoload-bunfig","--no-compile-autoload-dotenv",e.entry,"--outfile",e.outfile]}function wn(e=E.cwd()){let t=F(e,ke);if(z(t))return t;let n=dt(pt(import.meta.url)),s=[F(n,"launcher.ts"),F(n,"launcher.js")].find((o)=>z(o));if(!s)throw Error("Desktop launcher entrypoint not found. Reinstall @stacksjs/desktop - the package must ship dist/launcher.js.");return s}function H(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&apos;")}function q(e,t=""){if(typeof e==="string")return`<string>${H(e)}</string>`;if(typeof e==="boolean")return e?"<true/>":"<false/>";if(typeof e==="number"){if(!Number.isFinite(e))throw Error(`Unsupported Info.plist number: ${e}`);return Number.isInteger(e)?`<integer>${e}</integer>`:`<real>${e}</real>`}if(Array.isArray(e)){if(e.length===0)return"<array/>";return`<array>
3
3
  ${e.map((r)=>`${t} ${q(r,`${t} `)}`).join(`
4
4
  `)}
5
5
  ${t}</array>`}if(e&&typeof e==="object"){let n=Object.entries(e);if(n.length===0)return"<dict/>";return`<dict>
6
6
  ${n.map(([s,o])=>`${t} <key>${H(s)}</key>${q(o,`${t} `)}`).join(`
7
7
  `)}
8
- ${t}</dict>`}throw Error(`Unsupported Info.plist value: ${JSON.stringify(e)??String(e)}`)}function gn(e){let t=Object.keys(e).filter((s)=>pe.has(s)),n=Object.entries(e).filter(([s])=>!pe.has(s));if(n.length===0)return{xml:"",ignored:t};return{xml:`
8
+ ${t}</dict>`}throw Error(`Unsupported Info.plist value: ${JSON.stringify(e)??String(e)}`)}function yn(e){let t=Object.keys(e).filter((s)=>fe.has(s)),n=Object.entries(e).filter(([s])=>!fe.has(s));if(n.length===0)return{xml:"",ignored:t};return{xml:`
9
9
  ${n.map(([s,o])=>` <key>${H(s)}</key>${q(o," ")}`).join(`
10
- `)}`,ignored:t}}function pt(e,t={}){if(!Number.isInteger(e)||e<1||e>65535)throw RangeError(`Invalid desktop development port: ${e}`);let n=t.url||E.env.APP_URL;if(!n)return"https://stacks.test";let r=/^https?:\/\//.test(n)?n:`https://${n}`;return new URL(r).toString().replace(/\/$/,"")}function mt(e,t={}){let n=[he(t.craftBin),pt(e,t),"--title",t.title||"Stacks","--width",String(t.width||1400),"--height",String(t.height||900)];if(t.hotReload!==!1)n.push("--hot-reload");if(t.devTools===!1)n.push("--no-devtools");if(t.darkMode)n.push("--dark");if(t.systemTray)n.push("--system-tray");if(t.hideDockIcon)n.push("--hide-dock-icon");if(t.menubarOnly)n.push("--menubar-only");return n}function hn(e,t="stable"){if(!/^[a-z0-9-]+$/i.test(t))throw Error("Update channel may only contain letters, numbers, and hyphens");return new URL(`/desktop/updates/${t}.json`,/^https?:\/\//.test(e)?e:`https://${e}`).toString()}async function ft(e){Bun.spawn(e,{stdin:"inherit",stdout:"inherit",stderr:"inherit"}).unref()}async function bn(e,t={},n=ft){return await n(mt(e,t)),!0}function wt(e){try{let t=fe(e,"r");try{let n=Buffer.alloc(4);if(ge(t,n,0,4,0)<4)return!1;return bt.has(n.readUInt32BE(0))}finally{me(t)}}catch{return!1}}function wn(e){return wt(e)?"MacOS":"Resources"}function yn(e){return["codesign","--force",e.identity==="-"?"--timestamp=none":"--timestamp","--options","runtime",...e.entitlements?["--entitlements",e.entitlements]:[],"--sign",e.identity,e.target]}function kn(e,t=yt){let n=t(e);if(!n||n.size<W)return!1;return n.tail.includes(gt)}function yt(e){try{let{size:t}=at(e);if(t<W)return{size:t,tail:""};let n=fe(e,"r");try{let r=Math.min(ht,t),s=Buffer.alloc(r);return ge(n,s,0,r,t-r),{size:t,tail:s.toString("latin1")}}finally{me(n)}}catch{return null}}function vn(e){if(e.length<2)return null;let t=[...e].sort((o,a)=>a.bytes-o.bytes),r=t.slice(1).length*W,s=t.map((o)=>`${o.name} (${(o.bytes/1024/1024).toFixed(1)} MB)`).join(", ");return`This bundle ships ${e.length} Bun-compiled executables: ${s}. Each embeds a full copy of the Bun runtime, so at least ${(r/1024/1024).toFixed(0)} MB of the bundle is the same runtime repeated. `+"Compiling one binary that dispatches on a subcommand \u2014 `MyApp agent`, `MyApp worker` \u2014 keeps the helpers as separate processes and pays for the runtime once."}var we="app/Desktop/launcher.ts",pe,gt="---- Bun!",ht=8388608,W=41943040,bt;var ye=O(()=>{le();de();pe=new Set(["CFBundleName","CFBundleDisplayName","CFBundleIdentifier","CFBundleVersion","CFBundleShortVersionString","CFBundleExecutable","CFBundlePackageType","CFBundleInfoDictionaryVersion","CFBundleIconFile"]);bt=new Set([4277009102,3472551422,4277009103,3489328638,3405691582,3199925962])});import{existsSync as Re,mkdirSync as kt,writeFileSync as ke}from"fs";import ve from"os";import{dirname as vt,join as L,relative as xe,resolve as xt}from"path";import l from"process";import{fileURLToPath as Pt}from"url";function Se(e){return e.replaceAll("</script","<\\/script")}function G(e,t){return`<!doctype html>
10
+ `)}`,ignored:t}}function gt(e,t={}){if(!Number.isInteger(e)||e<1||e>65535)throw RangeError(`Invalid desktop development port: ${e}`);let n=t.url||E.env.APP_URL;if(!n)return"https://stacks.test";let r=/^https?:\/\//.test(n)?n:`https://${n}`;return new URL(r).toString().replace(/\/$/,"")}function ht(e,t={}){let n=[we(t.craftBin),gt(e,t),"--title",t.title||"Stacks","--width",String(t.width||1400),"--height",String(t.height||900)];if(t.hotReload!==!1)n.push("--hot-reload");if(t.devTools===!1)n.push("--no-devtools");if(t.darkMode)n.push("--dark");if(t.systemTray)n.push("--system-tray");if(t.hideDockIcon)n.push("--hide-dock-icon");if(t.menubarOnly)n.push("--menubar-only");return n}function kn(e,t="stable"){if(!/^[a-z0-9-]+$/i.test(t))throw Error("Update channel may only contain letters, numbers, and hyphens");return new URL(`/desktop/updates/${t}.json`,/^https?:\/\//.test(e)?e:`https://${e}`).toString()}async function bt(e){Bun.spawn(e,{stdin:"inherit",stdout:"inherit",stderr:"inherit"}).unref()}async function vn(e,t={},n=bt){return await n(ht(e,t)),!0}function vt(e){try{let t=he(e,"r");try{let n=Buffer.alloc(4);if(be(t,n,0,4,0)<4)return!1;return kt.has(n.readUInt32BE(0))}finally{ge(t)}}catch{return!1}}function xn(e){return vt(e)?"MacOS":"Resources"}function Pn(e){return["codesign","--force",e.identity==="-"?"--timestamp=none":"--timestamp","--options","runtime",...e.entitlements?["--entitlements",e.entitlements]:[],"--sign",e.identity,e.target]}function $n(e,t=xt){let n=t(e);if(!n||n.size<W)return!1;return n.tail.includes(wt)}function xt(e){try{let{size:t}=lt(e);if(t<W)return{size:t,tail:""};let n=he(e,"r");try{let r=Math.min(yt,t),s=Buffer.alloc(r);return be(n,s,0,r,t-r),{size:t,tail:s.toString("latin1")}}finally{ge(n)}}catch{return null}}function Rn(e){if(e.length<2)return null;let t=[...e].sort((o,a)=>a.bytes-o.bytes),r=t.slice(1).length*W,s=t.map((o)=>`${o.name} (${(o.bytes/1024/1024).toFixed(1)} MB)`).join(", ");return`This bundle ships ${e.length} Bun-compiled executables: ${s}. Each embeds a full copy of the Bun runtime, so at least ${(r/1024/1024).toFixed(0)} MB of the bundle is the same runtime repeated. `+"Compiling one binary that dispatches on a subcommand \u2014 `MyApp agent`, `MyApp worker` \u2014 keeps the helpers as separate processes and pays for the runtime once."}var ke="app/Desktop/launcher.ts",fe,wt="---- Bun!",yt=8388608,W=41943040,kt;var ve=O(()=>{pe();me();fe=new Set(["CFBundleName","CFBundleDisplayName","CFBundleIdentifier","CFBundleVersion","CFBundleShortVersionString","CFBundleExecutable","CFBundlePackageType","CFBundleInfoDictionaryVersion","CFBundleIconFile"]);kt=new Set([4277009102,3472551422,4277009103,3489328638,3405691582,3199925962])});import{existsSync as Se,mkdirSync as Pt,writeFileSync as xe}from"fs";import Pe from"os";import{dirname as $t,join as L,relative as $e,resolve as Rt}from"path";import l from"process";import{fileURLToPath as St}from"url";function Me(e){return e.replaceAll("</script","<\\/script")}function G(e,t){return`<!doctype html>
11
11
  <html lang="en">
12
12
  <head>
13
13
  <meta charset="utf-8">
@@ -48,19 +48,19 @@ ${n.map(([s,o])=>` <key>${H(s)}</key>${q(o," ")}`).join(`
48
48
  <div id="countdown"></div>
49
49
  </div>
50
50
  <script type="application/json" id="probe-config">${JSON.stringify(t).replaceAll("<","\\u003c")}</script>
51
- <script type="module">${Se(e)}</script>
51
+ <script type="module">${Me(e)}</script>
52
52
  </body>
53
53
  </html>
54
- `}var V={idleMs:3000,loadMs:6000,cpuLoadMsPerFrame:6,instances:40000,interactionWindowMs:20000,tryNativeFullscreen:!0};var J=1,K=["available","unavailable","measured","requires-interaction","denied","error"],b=[["webgl2","WebGL2"],["webgpu","WebGPU"],["framePacing","Frame pacing"],["inputLatency","Input latency"],["audioLatency","Audio latency"],["gamepad","Gamepad API"],["fullscreen","Fullscreen"],["pointerLock","Pointer lock"]];function f(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function x(e){return typeof e==="string"&&e.trim().length>0}function Ee(e){let t=[];if(!f(e))return["record is not an object"];if(e.schema!==1)t.push(`schema must be 1, got ${JSON.stringify(e.schema)}`);if(!x(e.measuredAt)||Number.isNaN(Date.parse(e.measuredAt)))t.push("measuredAt must be an ISO 8601 timestamp");let n=e.host;if(!f(n))t.push("host is missing");else{if(n.kind!=="craft"&&n.kind!=="browser")t.push(`host.kind must be craft or browser, got ${JSON.stringify(n.kind)}`);for(let i of["name","version","userAgent"])if(!x(n[i]))t.push(`host.${i} is required`);if(n.kind==="craft"&&x(n.version)&&!/^\d+\.\d+\.\d+/.test(n.version))t.push(`host.version must be a Craft release version, got ${JSON.stringify(n.version)}`)}let r=e.os;if(!f(r))t.push("os is missing");else for(let i of["platform","arch","name","version"])if(!x(r[i]))t.push(`os.${i} is required`);let s=e.machine;if(!f(s))t.push("machine is missing");else{for(let u of["model","cpu"])if(!x(s[u]))t.push(`machine.${u} is required`);let i=s.display;if(!f(i))t.push("machine.display is missing");else{if(i.refreshHz!==null&&!(typeof i.refreshHz==="number"&&i.refreshHz>0))t.push("machine.display.refreshHz must be a positive number or null");if(!(typeof i.devicePixelRatio==="number"&&i.devicePixelRatio>0))t.push("machine.display.devicePixelRatio must be a positive number")}}let o=e.run;if(!f(o))t.push("run is missing");else{for(let i of["idleMs","loadMs","cpuLoadMsPerFrame","triangles","interactionWindowMs"])if(!(typeof o[i]==="number"&&o[i]>=0))t.push(`run.${i} must be a non-negative number`);if(typeof o.sawRealInput!=="boolean")t.push("run.sawRealInput must be a boolean")}let a=e.probes;if(!f(a))return t.push("probes is missing"),t;for(let[i]of b){let u=a[i];if(!f(u)){t.push(`probes.${i} is missing`);continue}if(!K.includes(u.status))t.push(`probes.${i}.status must be one of ${K.join(", ")}, got ${JSON.stringify(u.status)}`);if(!x(u.note))t.push(`probes.${i}.note is required: say what was observed, even on success`);if(u.data!==void 0&&!f(u.data))t.push(`probes.${i}.data must be an object when present`);if(u.status==="measured"&&!f(u.data))t.push(`probes.${i} is measured but carries no data`)}for(let i of Object.keys(a))if(!b.some(([u])=>u===i))t.push(`probes.${i} is not a known probe`);return t}function I(e,t="probe record"){let n=Ee(e);if(n.length>0)throw Error(`${t} is invalid:
54
+ `}var V={idleMs:3000,loadMs:6000,cpuLoadMsPerFrame:6,instances:40000,interactionWindowMs:20000,tryNativeFullscreen:!0};var J=1,K=["available","unavailable","measured","requires-interaction","denied","error"],b=[["webgl2","WebGL2"],["webgpu","WebGPU"],["framePacing","Frame pacing"],["inputLatency","Input latency"],["audioLatency","Audio latency"],["gamepad","Gamepad API"],["fullscreen","Fullscreen"],["pointerLock","Pointer lock"]];function f(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function x(e){return typeof e==="string"&&e.trim().length>0}function De(e){let t=[];if(!f(e))return["record is not an object"];if(e.schema!==1)t.push(`schema must be 1, got ${JSON.stringify(e.schema)}`);if(!x(e.measuredAt)||Number.isNaN(Date.parse(e.measuredAt)))t.push("measuredAt must be an ISO 8601 timestamp");let n=e.host;if(!f(n))t.push("host is missing");else{if(n.kind!=="craft"&&n.kind!=="browser")t.push(`host.kind must be craft or browser, got ${JSON.stringify(n.kind)}`);for(let i of["name","version","userAgent"])if(!x(n[i]))t.push(`host.${i} is required`);if(n.kind==="craft"&&x(n.version)&&!/^\d+\.\d+\.\d+/.test(n.version))t.push(`host.version must be a Craft release version, got ${JSON.stringify(n.version)}`)}let r=e.os;if(!f(r))t.push("os is missing");else for(let i of["platform","arch","name","version"])if(!x(r[i]))t.push(`os.${i} is required`);let s=e.machine;if(!f(s))t.push("machine is missing");else{for(let u of["model","cpu"])if(!x(s[u]))t.push(`machine.${u} is required`);let i=s.display;if(!f(i))t.push("machine.display is missing");else{if(i.refreshHz!==null&&!(typeof i.refreshHz==="number"&&i.refreshHz>0))t.push("machine.display.refreshHz must be a positive number or null");if(!(typeof i.devicePixelRatio==="number"&&i.devicePixelRatio>0))t.push("machine.display.devicePixelRatio must be a positive number")}}let o=e.run;if(!f(o))t.push("run is missing");else{for(let i of["idleMs","loadMs","cpuLoadMsPerFrame","triangles","interactionWindowMs"])if(!(typeof o[i]==="number"&&o[i]>=0))t.push(`run.${i} must be a non-negative number`);if(typeof o.sawRealInput!=="boolean")t.push("run.sawRealInput must be a boolean")}let a=e.probes;if(!f(a))return t.push("probes is missing"),t;for(let[i]of b){let u=a[i];if(!f(u)){t.push(`probes.${i} is missing`);continue}if(!K.includes(u.status))t.push(`probes.${i}.status must be one of ${K.join(", ")}, got ${JSON.stringify(u.status)}`);if(!x(u.note))t.push(`probes.${i}.note is required: say what was observed, even on success`);if(u.data!==void 0&&!f(u.data))t.push(`probes.${i}.data must be an object when present`);if(u.status==="measured"&&!f(u.data))t.push(`probes.${i} is measured but carries no data`)}for(let i of Object.keys(a))if(!b.some(([u])=>u===i))t.push(`probes.${i} is not a known probe`);return t}function D(e,t="probe record"){let n=De(e);if(n.length>0)throw Error(`${t} is invalid:
55
55
  - ${n.join(`
56
- - `)}`);return e}var Me=[24,30,48,50,60,72,75,90,100,120,144,165,240];function y(e,t){if(e.length===0)return null;if(!Number.isFinite(t)||t<0||t>100)throw RangeError(`percentile must be between 0 and 100, got ${t}`);let n=[...e].sort((u,c)=>u-c),r=t/100*(n.length-1),s=Math.floor(r),o=Math.ceil(r),a=n[s],i=n[o];return a+(i-a)*(r-s)}function T(e){let t=[];for(let n=1;n<e.length;n++)t.push(e[n]-e[n-1]);return t}function Ie(e,t=1,n=50){if(t<=0||!Number.isFinite(t))throw RangeError(`bucketMs must be positive, got ${t}`);if(!Number.isInteger(n)||n<1)throw RangeError(`buckets must be a positive integer, got ${n}`);let r=Array.from({length:n},()=>0),s=0;for(let o of e){let a=Math.floor(Math.max(0,o)/t);if(a>=n)s++;else r[a]++}return{bucketMs:t,counts:r,overflow:s}}function Z(e,t=0.03){if(!(e>0))throw RangeError(`period must be positive, got ${e}`);let n=1000/e,r=n,s=Number.POSITIVE_INFINITY;for(let o of Me){let a=Math.abs(n-o)/o;if(a<s)r=o,s=a}return s<=t?r:Math.round(n*100)/100}function Ae(e,t){if(!(t>0))throw RangeError(`period must be positive, got ${t}`);let n=0;for(let r of e)n+=Math.max(0,Math.round(r/t)-1);return n}function A(e,t){if(e.length<3)return null;if(!(t>0))throw RangeError(`targetHz must be positive, got ${t}`);let n=T(e),r=e[e.length-1]-e[0],s=1000/t,o=(a)=>Math.round(a*100)/100;return{frames:e.length,durationMs:o(r),fps:o(r>0?n.length*1000/r:0),p50Ms:o(y(n,50)),p95Ms:o(y(n,95)),p99Ms:o(y(n,99)),maxMs:o(Math.max(...n)),targetPeriodMs:o(s),targetHz:t,droppedFrames:Ae(n,s),longFrames:n.filter((a)=>a>s*1.5).length,histogram:Ie(n)}}function D(e){let t=e.filter((r)=>Number.isFinite(r)&&r>=0);if(t.length===0)return null;let n=(r)=>Math.round(r*100)/100;return{samples:t.length,p50Ms:n(y(t,50)),p95Ms:n(y(t,95)),maxMs:n(Math.max(...t))}}function Y(e){let t=e?.match(/@\s*([\d.]+)\s*Hz/i);if(!t)return null;let n=Number(t[1]);return Number.isFinite(n)&&n>0?n:null}function ee(e){let t=[["Edge",/Edg\/([\d.]+)/],["Firefox",/Firefox\/([\d.]+)/],["Chrome",/Chrome\/([\d.]+)/],["Safari",/Version\/([\d.]+).*Safari\//],["WebKit",/AppleWebKit\/([\d.]+)/]];for(let[n,r]of t){let s=e.match(r);if(s)return{name:n,version:s[1]}}return{name:"unknown",version:"unknown"}}function X(e){return typeof e==="number"?`${Math.round(e*1e4)/10} ms`:"not reported"}function R(e){return e.detail?`${e.outcome} (${e.detail})`:e.outcome}function De(e){let t=e.webgl2;if(!t.ok)return{status:"unavailable",note:`No WebGL2 context: ${t.error??"unknown reason"}.`,data:{...t}};let n=t.unmaskedRenderer||t.renderer;return{status:"available",note:`WebGL2 context created (${t.version}); renderer "${n}", max texture ${t.maxTextureSize}, MSAA up to ${t.maxSamples}x.`,data:{...t}}}function Fe(e){let t=e.webgpu;if(!t.present)return{status:"unavailable",note:"navigator.gpu is undefined.",data:{...t}};if(!t.adapter||!t.device)return{status:"unavailable",note:`navigator.gpu exists but ${t.adapter?"requestDevice()":"requestAdapter()"} failed: ${t.error??"no reason given"}.`,data:{...t}};return{status:"available",note:`Adapter (${[...new Set([t.vendor,t.architecture].filter(Boolean))].join(" ")||"unnamed adapter"}) and device created; maxTextureDimension2D ${t.maxTextureDimension2D??"unknown"}.`,data:{...t}}}function Ce(e,t){let{frames:n,config:r}=e;if(n.idle.length<3||n.load.length<3)return{status:"error",note:`Too few animation frames to measure (${n.idle.length} idle, ${n.load.length} under load).`};let s=Z(y(T(n.idle),50)),o=t.machine.display.refreshHz??s,a=A(n.idle,o),i=A(n.load,o),u=A(n.load,s),c={rafHz:s,displayHz:o,timerResolutionMs:e.timerResolutionMs,triangles:n.triangles,cpuLoadMsPerFrame:r.cpuLoadMsPerFrame,hiddenDuringSampling:n.hiddenDuringSampling,idle:a,load:i,loadAtRafRate:{droppedFrames:u.droppedFrames,longFrames:u.longFrames}};if(n.hiddenDuringSampling)return{status:"error",note:"The page was hidden while sampling, which throttles requestAnimationFrame; these numbers are not representative. Re-run with the window visible.",data:c};let d=s<o-1?`requestAnimationFrame ran at ${s} Hz on a ${o} Hz display, so a game cannot reach the panel's rate. `:`requestAnimationFrame ran at the display's ${o} Hz. `,p=n.triangles>0?`${n.triangles.toLocaleString("en-US")} triangles`:"a 2D canvas fallback";return{status:"measured",note:`${d}Under load (${p} + ${r.cpuLoadMsPerFrame} ms CPU per frame): p50 ${i.p50Ms} ms, p95 ${i.p95Ms} ms, p99 ${i.p99Ms} ms; ${u.droppedFrames} frames dropped at ${s} Hz and ${i.droppedFrames} at the display's ${o} Hz over ${Math.round(i.durationMs/100)/10} s.${e.timerResolutionMs>=0.5?` The page clock is clamped to ${e.timerResolutionMs} ms, so intervals read as whole milliseconds.`:""}`,data:c}}var Q=5;function Le(e){let{input:t}=e,n={eventTimeStampComparable:e.eventTimeStampComparable,eventTimingSupported:t.eventTimingSupported,timerResolutionMs:e.timerResolutionMs};if(!e.eventTimeStampComparable)return{status:"error",note:"event.timeStamp does not share performance.now()'s time origin, so event-to-frame time cannot be computed.",data:n};let r={pointerdown:t.pointerdown,pointermove:t.pointermove,keydown:t.keydown},s=Object.values(r).reduce((i,u)=>i+u.length,0);if(s===0)return{status:"requires-interaction",note:`${e.interaction.skipped?"The interactive window was skipped":`No trusted input reached the page in the ${e.interaction.windowMs/1000} s window`}, and synthetic events are not counted. Observable: event.timeStamp is on the performance.now() clock, timer resolution ${e.timerResolutionMs} ms, Event Timing API ${t.eventTimingSupported?"supported":"not supported"}.`,data:n};let o={...n},a=[];for(let[i,u]of Object.entries(r)){let c=D(u.map((p)=>p.toFrameMs)),d=D(u.map((p)=>p.toPostFrameMs));if(o[i]={toFrame:c,toPostFrame:d},c)a.push(`${i} p50 ${c.p50Ms} ms to next frame (${c.samples} ${c.samples===1?"sample":"samples"})`)}if(o.eventTiming=D(t.eventTimingDurations),s<Q)return{status:"requires-interaction",note:`Only ${s} trusted input ${s===1?"event":"events"} arrived, fewer than the ${Q} needed to summarise. Seen: ${a.join("; ")}. Re-run and use the panel's buttons and keys.`,data:o};return{status:"measured",note:`Event timestamp to the next rAF callback: ${a.join("; ")}. Real input from the OS; no synthetic events.`,data:o}}function Oe(e){let t=e.audio;if(!t.present)return{status:"unavailable",note:"window.AudioContext is undefined."};if(t.error||!t.initial)return{status:"error",note:`Creating an AudioContext failed: ${t.error??"unknown reason"}.`};let n=t.afterGesturedResume??t.afterUngesturedResume??t.initial,r={initial:t.initial,afterUngesturedResume:t.afterUngesturedResume??null,afterGesturedResume:t.afterGesturedResume??null},s=`baseLatency ${X(n.baseLatency)}, outputLatency ${X(n.outputLatency)} at ${n.sampleRate} Hz`,o=t.afterUngesturedResume?.state==="running"?"The context started without a user gesture":"The context stayed suspended without a user gesture";if(n.state!=="running")return{status:"requires-interaction",note:`${o}, so device latency was not observable. Reported before running: ${s}.`,data:r};if(n.baseLatency===null&&n.outputLatency===null)return{status:"unavailable",note:`${o}, but neither baseLatency nor outputLatency is implemented.`,data:r};return{status:"measured",note:`${o}. ${s}${n.outputLatency===null?" (outputLatency not implemented)":""}.`,data:r}}function Te(e){let t=e.gamepad;if(!t.present)return{status:"unavailable",note:"navigator.getGamepads is undefined."};return{status:"available",note:t.connected>0?`navigator.getGamepads() is present and reported ${t.connected} controller(s).`:"navigator.getGamepads() is present; no controller was connected, so axis and button input was not exercised.",data:{...t,secureContext:e.isSecureContext}}}function Ne(e){let t=e.fullscreen,n=t.nativeBridge.outcome==="not-attempted"||t.nativeBridge.outcome==="no-api"?"":` Craft's own window.craft.window.setFullscreen(true), which needs no gesture: ${R(t.nativeBridge)}.`,r={...t};if(!t.elementApi&&!t.webkitElementApi)return{status:"unavailable",note:`Element.requestFullscreen and webkitRequestFullscreen are both undefined, so the standard Fullscreen API engines call does nothing.${n}`,data:r};let s=`Without a gesture: ${R(t.ungestured)}.`;switch(t.gestured.outcome){case"entered":return{status:"available",note:`requestFullscreen() from a real click entered fullscreen. ${s}${n}`,data:r};case"rejected":case"timeout":return{status:"denied",note:`requestFullscreen() from a real click: ${R(t.gestured)}. ${s}${n}`,data:r};default:return{status:"requires-interaction",note:`The API exists (fullscreenEnabled ${String(t.enabled)}) but needs a real click to test. ${s}${n}`,data:r}}}function Be(e){let t=e.pointerLock,n={...t};if(!t.api)return{status:"unavailable",note:"Element.requestPointerLock is undefined.",data:n};let r=`Without a gesture: ${R(t.ungestured)}.`;switch(t.gestured.outcome){case"entered":return{status:"available",note:`requestPointerLock() from a real click locked the pointer. ${r}`,data:n};case"rejected":case"timeout":return{status:"denied",note:`requestPointerLock() from a real click: ${R(t.gestured)}. ${r}`,data:n};default:return{status:"requires-interaction",note:`The API exists but needs a real click to test. ${r}`,data:n}}}function te(e,t){return{schema:J,measuredAt:t.measuredAt,host:{...t.host,userAgent:e.userAgent},os:t.os,machine:{model:t.machine.model,cpu:t.machine.cpu,display:{...t.machine.display,devicePixelRatio:e.devicePixelRatio}},run:{idleMs:e.config.idleMs,loadMs:e.config.loadMs,cpuLoadMsPerFrame:e.config.cpuLoadMsPerFrame,triangles:e.frames.triangles,interactionWindowMs:e.interaction.windowMs,sawRealInput:e.interaction.sawRealInput},probes:{webgl2:De(e),webgpu:Fe(e),framePacing:Ce(e,t),inputLatency:Le(e),audioLatency:Oe(e),gamepad:Te(e),fullscreen:Ne(e),pointerLock:Be(e)}}}import{readdirSync as je,readFileSync as Ue}from"fs";import{join as _e}from"path";import{fileURLToPath as ze}from"url";var S=ze(new URL("./results",import.meta.url));function N(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function ne(e){return`${e.host.kind==="craft"?"craft":`browser-${N(e.host.name)}`}-${N(e.os.platform)}-${N(e.os.arch)}.json`}function Wt(e=S){let t;try{t=je(e).filter((n)=>n.endsWith(".json")).sort()}catch{return[]}return t.map((n)=>{let r=_e(e,n);return I(JSON.parse(Ue(r,"utf8")),r)})}var He="<!-- interactive-capability:begin -->",re="<!-- interactive-capability:end -->",B="buddy desktop:probe",qe={darwin:"macOS",linux:"Linux",win32:"Windows"};function se(e){return e.host.kind==="craft"?`Craft ${e.host.version}`:`${e.host.name} ${e.host.version}`}function oe(e){return`${qe[e.os.platform]??e.os.name} ${e.os.version} ${e.os.arch}, ${e.machine.cpu} (${e.machine.model})`}function We(e){return[...e].sort((t,n)=>{if(t.host.kind!==n.host.kind)return t.host.kind==="craft"?-1:1;return`${t.host.name} ${t.os.platform} ${t.os.arch}`.localeCompare(`${n.host.name} ${n.os.platform} ${n.os.arch}`)})}function P(e){return typeof e==="number"&&Number.isFinite(e)?e:null}function ie(e,...t){let n=e;for(let r of t){if(typeof n!=="object"||n===null)return;n=n[r]}return n}function U(e,t){let n=t.data;if(t.status!=="measured"||!n)return null;switch(e){case"framePacing":{let r=P(n.rafHz),s=P(n.displayHz),o=P(ie(n,"load","p99Ms"));if(r===null||s===null||o===null)return null;return`${r} Hz rAF on a ${s} Hz display, p99 ${o} ms under load`}case"inputLatency":{for(let r of["pointerdown","keydown","pointermove"]){let s=P(ie(n,r,"toFrame","p50Ms"));if(s!==null)return`${r} p50 ${s} ms to next frame`}return null}case"audioLatency":{let r=n.afterGesturedResume??n.afterUngesturedResume,s=P(r?.baseLatency),o=P(r?.outputLatency),a=(i)=>i===null?"n/a":`${Math.round(i*1e4)/10} ms`;return`base ${a(s)}, output ${a(o)}`}default:return null}}function Ge(e,t){if(!t)return"not probed";let n=U(e,t);return n?`**${t.status}** - ${n}`:`**${t.status}**`}function j(e){return e.replaceAll("|","\\|").replaceAll(`
57
- `," ")}function Kt(e){let t=We(e),n=[He,"","<!-- Generated by `buddy docs:capabilities` from the records in"," storage/framework/core/desktop/src/probe/results/. Re-measure with",` \`${B} --record\`; do not edit this by hand. -->`,"","Whether a Craft window can host a game or other frame-driven content that somebody else","builds, measured by the probe harness in `@stacksjs/desktop-build`","([stacksjs/stacks#877](https://github.com/stacksjs/stacks/issues/877)). Ordinary browsers","on the same machine are measured alongside it for comparison.","","> **How current is this?** Unlike the driver tables above, these are measurements, not","> claims CI can re-check. Each column is one run on one machine, and says when, against","> which host version, on which OS and hardware. If your Craft is newer than the column,",`> the numbers describe something you are not running: re-measure with \`${B} --record\`.`,""];if(t.length===0)return n.push(`No measurement has been recorded yet. Run \`${B} --record\`.`,"",re),n.join(`
58
- `);n.push(`| Probe | ${t.map((r)=>j(se(r))).join(" | ")} |`,`|---|${t.map(()=>"---").join("|")}|`);for(let[r,s]of b)n.push(`| ${s} | ${t.map((o)=>j(Ge(r,o.probes[r]))).join(" | ")} |`);n.push(`| Measured on | ${t.map((r)=>j(oe(r))).join(" | ")} |`,`| Measured at | ${t.map((r)=>r.measuredAt.slice(0,10)).join(" | ")} |`,"","- **available** / **measured** - the API exists and the probe exercised it.","- **unavailable** - the API is missing, or creating it failed.","- **requires-interaction** - needs a real click, key press or controller that the run did not have. What was still observable is in the notes; no number is invented.","- **denied** - requested from a real click and refused.","");for(let r of t){let s=r.machine.display.refreshHz===null?"unknown refresh rate":`${r.machine.display.refreshHz} Hz display`;n.push(`**${se(r)}** - ${oe(r)}, ${s}`,"");for(let[o,a]of b){let i=r.probes[o];if(i)n.push(`- **${a}** - ${i.note}`)}n.push("")}return n.push("**Out of scope.** Stacks is not shipping a game engine, a physics engine, an audio mixer or","an asset pipeline. This section establishes what a Craft window gives an engine somebody","else wrote; it is not a plan to write one.","",re),n.join(`
59
- `)}function k(e){try{let t=Bun.spawnSync(e,{stdout:"pipe",stderr:"ignore"});return t.exitCode===0?t.stdout.toString().trim():null}catch{return null}}function Rt(e){return e?.match(/craft version\s+(\d+\.\d+\.\d+\S*)/i)?.[1]??null}function $t(e){if(!e)return null;try{let n=(JSON.parse(e).SPDisplaysDataType??[]).flatMap((o)=>o.spdisplays_ndrvs??[]),r=n.find((o)=>o.spdisplays_main==="spdisplays_yes")??n[0];if(!r)return null;let s=r._spdisplays_resolution??"unknown";return{name:r._name??"unknown",resolution:s,refreshHz:Y(s)}}catch{return null}}function St(e,t,n){let r=l.platform,s=Et[r]??r,o=ve.release(),a,i="unknown",u=ve.cpus()[0]?.model?.trim()||"unknown",c={name:"unknown",resolution:"unknown",refreshHz:null};if(r==="darwin")s="macOS",o=k(["sw_vers","-productVersion"])??o,a=k(["sw_vers","-buildVersion"])??void 0,i=k(["sysctl","-n","hw.model"])??i,u=k(["sysctl","-n","machdep.cpu.brand_string"])??u,c=$t(k(["system_profiler","SPDisplaysDataType","-json"]))??c;else if(r==="linux")i=k(["cat","/sys/devices/virtual/dmi/id/product_name"])??i;let d=ee(n);return{measuredAt:new Date().toISOString(),host:e==="craft"?{kind:"craft",name:"craft",version:t??"unknown"}:{kind:"browser",name:d.name,version:d.version},os:{platform:r,arch:l.arch,name:s,version:o,...a?{build:a}:{}},machine:{model:i,cpu:u,display:c}}}var Et={darwin:"macOS",linux:"Linux",win32:"Windows"};async function Mt(){let e=vt(Pt(import.meta.url)),t=[L(e,"page.ts"),L(e,"page.js")].find(Re);if(!t)throw Error("The probe page script is missing; reinstall @stacksjs/desktop-build.");let n=await Bun.build({entrypoints:[t],target:"browser",format:"esm",minify:!0});if(!n.success)throw Error(`Bundling the probe page failed:
56
+ - `)}`);return e}var Ie=[24,30,48,50,60,72,75,90,100,120,144,165,240];function y(e,t){if(e.length===0)return null;if(!Number.isFinite(t)||t<0||t>100)throw RangeError(`percentile must be between 0 and 100, got ${t}`);let n=[...e].sort((u,c)=>u-c),r=t/100*(n.length-1),s=Math.floor(r),o=Math.ceil(r),a=n[s],i=n[o];return a+(i-a)*(r-s)}function T(e){let t=[];for(let n=1;n<e.length;n++)t.push(e[n]-e[n-1]);return t}function Ae(e,t=1,n=50){if(t<=0||!Number.isFinite(t))throw RangeError(`bucketMs must be positive, got ${t}`);if(!Number.isInteger(n)||n<1)throw RangeError(`buckets must be a positive integer, got ${n}`);let r=Array.from({length:n},()=>0),s=0;for(let o of e){let a=Math.floor(Math.max(0,o)/t);if(a>=n)s++;else r[a]++}return{bucketMs:t,counts:r,overflow:s}}function Z(e,t=0.03){if(!(e>0))throw RangeError(`period must be positive, got ${e}`);let n=1000/e,r=n,s=Number.POSITIVE_INFINITY;for(let o of Ie){let a=Math.abs(n-o)/o;if(a<s)r=o,s=a}return s<=t?r:Math.round(n*100)/100}function Fe(e,t){if(!(t>0))throw RangeError(`period must be positive, got ${t}`);let n=0;for(let r of e)n+=Math.max(0,Math.round(r/t)-1);return n}function I(e,t){if(e.length<3)return null;if(!(t>0))throw RangeError(`targetHz must be positive, got ${t}`);let n=T(e),r=e[e.length-1]-e[0],s=1000/t,o=(a)=>Math.round(a*100)/100;return{frames:e.length,durationMs:o(r),fps:o(r>0?n.length*1000/r:0),p50Ms:o(y(n,50)),p95Ms:o(y(n,95)),p99Ms:o(y(n,99)),maxMs:o(Math.max(...n)),targetPeriodMs:o(s),targetHz:t,droppedFrames:Fe(n,s),longFrames:n.filter((a)=>a>s*1.5).length,histogram:Ae(n)}}function A(e){let t=e.filter((r)=>Number.isFinite(r)&&r>=0);if(t.length===0)return null;let n=(r)=>Math.round(r*100)/100;return{samples:t.length,p50Ms:n(y(t,50)),p95Ms:n(y(t,95)),maxMs:n(Math.max(...t))}}function Y(e){let t=e?.match(/@\s*([\d.]+)\s*Hz/i);if(!t)return null;let n=Number(t[1]);return Number.isFinite(n)&&n>0?n:null}function ee(e){let t=[["Edge",/Edg\/([\d.]+)/],["Firefox",/Firefox\/([\d.]+)/],["Chrome",/Chrome\/([\d.]+)/],["Safari",/Version\/([\d.]+).*Safari\//],["WebKit",/AppleWebKit\/([\d.]+)/]];for(let[n,r]of t){let s=e.match(r);if(s)return{name:n,version:s[1]}}return{name:"unknown",version:"unknown"}}function X(e){return typeof e==="number"?`${Math.round(e*1e4)/10} ms`:"not reported"}function R(e){return e.detail?`${e.outcome} (${e.detail})`:e.outcome}function Ce(e){let t=e.webgl2;if(!t.ok)return{status:"unavailable",note:`No WebGL2 context: ${t.error??"unknown reason"}.`,data:{...t}};let n=t.unmaskedRenderer||t.renderer;return{status:"available",note:`WebGL2 context created (${t.version}); renderer "${n}", max texture ${t.maxTextureSize}, MSAA up to ${t.maxSamples}x.`,data:{...t}}}function Le(e){let t=e.webgpu;if(!t.present)return{status:"unavailable",note:"navigator.gpu is undefined.",data:{...t}};if(!t.adapter||!t.device)return{status:"unavailable",note:`navigator.gpu exists but ${t.adapter?"requestDevice()":"requestAdapter()"} failed: ${t.error??"no reason given"}.`,data:{...t}};return{status:"available",note:`Adapter (${[...new Set([t.vendor,t.architecture].filter(Boolean))].join(" ")||"unnamed adapter"}) and device created; maxTextureDimension2D ${t.maxTextureDimension2D??"unknown"}.`,data:{...t}}}function Oe(e,t){let{frames:n,config:r}=e;if(n.idle.length<3||n.load.length<3)return{status:"error",note:`Too few animation frames to measure (${n.idle.length} idle, ${n.load.length} under load).`};let s=Z(y(T(n.idle),50)),o=t.machine.display.refreshHz??s,a=I(n.idle,o),i=I(n.load,o),u=I(n.load,s),c={rafHz:s,displayHz:o,timerResolutionMs:e.timerResolutionMs,triangles:n.triangles,cpuLoadMsPerFrame:r.cpuLoadMsPerFrame,hiddenDuringSampling:n.hiddenDuringSampling,idle:a,load:i,loadAtRafRate:{droppedFrames:u.droppedFrames,longFrames:u.longFrames}};if(n.hiddenDuringSampling)return{status:"error",note:"The page was hidden while sampling, which throttles requestAnimationFrame; these numbers are not representative. Re-run with the window visible.",data:c};let d=s<o-1?`requestAnimationFrame ran at ${s} Hz on a ${o} Hz display, so a game cannot reach the panel's rate. `:`requestAnimationFrame ran at the display's ${o} Hz. `,p=n.triangles>0?`${n.triangles.toLocaleString("en-US")} triangles`:"a 2D canvas fallback";return{status:"measured",note:`${d}Under load (${p} + ${r.cpuLoadMsPerFrame} ms CPU per frame): p50 ${i.p50Ms} ms, p95 ${i.p95Ms} ms, p99 ${i.p99Ms} ms; ${u.droppedFrames} frames dropped at ${s} Hz and ${i.droppedFrames} at the display's ${o} Hz over ${Math.round(i.durationMs/100)/10} s.${e.timerResolutionMs>=0.5?` The page clock is clamped to ${e.timerResolutionMs} ms, so intervals read as whole milliseconds.`:""}`,data:c}}var Q=5;function Te(e){let{input:t}=e,n={eventTimeStampComparable:e.eventTimeStampComparable,eventTimingSupported:t.eventTimingSupported,timerResolutionMs:e.timerResolutionMs};if(!e.eventTimeStampComparable)return{status:"error",note:"event.timeStamp does not share performance.now()'s time origin, so event-to-frame time cannot be computed.",data:n};let r={pointerdown:t.pointerdown,pointermove:t.pointermove,keydown:t.keydown},s=Object.values(r).reduce((i,u)=>i+u.length,0);if(s===0)return{status:"requires-interaction",note:`${e.interaction.skipped?"The interactive window was skipped":`No trusted input reached the page in the ${e.interaction.windowMs/1000} s window`}, and synthetic events are not counted. Observable: event.timeStamp is on the performance.now() clock, timer resolution ${e.timerResolutionMs} ms, Event Timing API ${t.eventTimingSupported?"supported":"not supported"}.`,data:n};let o={...n},a=[];for(let[i,u]of Object.entries(r)){let c=A(u.map((p)=>p.toFrameMs)),d=A(u.map((p)=>p.toPostFrameMs));if(o[i]={toFrame:c,toPostFrame:d},c)a.push(`${i} p50 ${c.p50Ms} ms to next frame (${c.samples} ${c.samples===1?"sample":"samples"})`)}if(o.eventTiming=A(t.eventTimingDurations),s<Q)return{status:"requires-interaction",note:`Only ${s} trusted input ${s===1?"event":"events"} arrived, fewer than the ${Q} needed to summarise. Seen: ${a.join("; ")}. Re-run and use the panel's buttons and keys.`,data:o};return{status:"measured",note:`Event timestamp to the next rAF callback: ${a.join("; ")}. Real input from the OS; no synthetic events.`,data:o}}function Ne(e){let t=e.audio;if(!t.present)return{status:"unavailable",note:"window.AudioContext is undefined."};if(t.error||!t.initial)return{status:"error",note:`Creating an AudioContext failed: ${t.error??"unknown reason"}.`};let n=t.afterGesturedResume??t.afterUngesturedResume??t.initial,r={initial:t.initial,afterUngesturedResume:t.afterUngesturedResume??null,afterGesturedResume:t.afterGesturedResume??null},s=`baseLatency ${X(n.baseLatency)}, outputLatency ${X(n.outputLatency)} at ${n.sampleRate} Hz`,o=t.afterUngesturedResume?.state==="running"?"The context started without a user gesture":"The context stayed suspended without a user gesture";if(n.state!=="running")return{status:"requires-interaction",note:`${o}, so device latency was not observable. Reported before running: ${s}.`,data:r};if(n.baseLatency===null&&n.outputLatency===null)return{status:"unavailable",note:`${o}, but neither baseLatency nor outputLatency is implemented.`,data:r};return{status:"measured",note:`${o}. ${s}${n.outputLatency===null?" (outputLatency not implemented)":""}.`,data:r}}function Be(e){let t=e.gamepad;if(!t.present)return{status:"unavailable",note:"navigator.getGamepads is undefined."};return{status:"available",note:t.connected>0?`navigator.getGamepads() is present and reported ${t.connected} controller(s).`:"navigator.getGamepads() is present; no controller was connected, so axis and button input was not exercised.",data:{...t,secureContext:e.isSecureContext}}}function je(e){let t=e.fullscreen,n=t.nativeBridge.outcome==="not-attempted"||t.nativeBridge.outcome==="no-api"?"":` Craft's own window.craft.window.setFullscreen(true), which needs no gesture: ${R(t.nativeBridge)}.`,r={...t};if(!t.elementApi&&!t.webkitElementApi)return{status:"unavailable",note:`Element.requestFullscreen and webkitRequestFullscreen are both undefined, so the standard Fullscreen API engines call does nothing.${n}`,data:r};let s=`Without a gesture: ${R(t.ungestured)}.`;switch(t.gestured.outcome){case"entered":return{status:"available",note:`requestFullscreen() from a real click entered fullscreen. ${s}${n}`,data:r};case"rejected":case"timeout":return{status:"denied",note:`requestFullscreen() from a real click: ${R(t.gestured)}. ${s}${n}`,data:r};default:return{status:"requires-interaction",note:`The API exists (fullscreenEnabled ${String(t.enabled)}) but needs a real click to test. ${s}${n}`,data:r}}}function Ue(e){let t=e.pointerLock,n={...t};if(!t.api)return{status:"unavailable",note:"Element.requestPointerLock is undefined.",data:n};let r=`Without a gesture: ${R(t.ungestured)}.`;switch(t.gestured.outcome){case"entered":return{status:"available",note:`requestPointerLock() from a real click locked the pointer. ${r}`,data:n};case"rejected":case"timeout":return{status:"denied",note:`requestPointerLock() from a real click: ${R(t.gestured)}. ${r}`,data:n};default:return{status:"requires-interaction",note:`The API exists but needs a real click to test. ${r}`,data:n}}}function te(e,t){return{schema:J,measuredAt:t.measuredAt,host:{...t.host,userAgent:e.userAgent},os:t.os,machine:{model:t.machine.model,cpu:t.machine.cpu,display:{...t.machine.display,devicePixelRatio:e.devicePixelRatio}},run:{idleMs:e.config.idleMs,loadMs:e.config.loadMs,cpuLoadMsPerFrame:e.config.cpuLoadMsPerFrame,triangles:e.frames.triangles,interactionWindowMs:e.interaction.windowMs,sawRealInput:e.interaction.sawRealInput},probes:{webgl2:Ce(e),webgpu:Le(e),framePacing:Oe(e,t),inputLatency:Te(e),audioLatency:Ne(e),gamepad:Be(e),fullscreen:je(e),pointerLock:Ue(e)}}}import{readdirSync as _e,readFileSync as ze}from"fs";import{join as He}from"path";import{fileURLToPath as qe}from"url";var S=qe(new URL("./results",import.meta.url));function N(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function ne(e){return`${e.host.kind==="craft"?"craft":`browser-${N(e.host.name)}`}-${N(e.os.platform)}-${N(e.os.arch)}.json`}function Kt(e=S){let t;try{t=_e(e).filter((n)=>n.endsWith(".json")).sort()}catch{return[]}return t.map((n)=>{let r=He(e,n);return D(JSON.parse(ze(r,"utf8")),r)})}var We="<!-- interactive-capability:begin -->",re="<!-- interactive-capability:end -->",B="buddy desktop:probe",Ge={darwin:"macOS",linux:"Linux",win32:"Windows"};function se(e){return e.host.kind==="craft"?`Craft ${e.host.version}`:`${e.host.name} ${e.host.version}`}function oe(e){return`${Ge[e.os.platform]??e.os.name} ${e.os.version} ${e.os.arch}, ${e.machine.cpu} (${e.machine.model})`}function Ve(e){return[...e].sort((t,n)=>{if(t.host.kind!==n.host.kind)return t.host.kind==="craft"?-1:1;return`${t.host.name} ${t.os.platform} ${t.os.arch}`.localeCompare(`${n.host.name} ${n.os.platform} ${n.os.arch}`)})}function P(e){return typeof e==="number"&&Number.isFinite(e)?e:null}function ie(e,...t){let n=e;for(let r of t){if(typeof n!=="object"||n===null)return;n=n[r]}return n}function U(e,t){let n=t.data;if(t.status!=="measured"||!n)return null;switch(e){case"framePacing":{let r=P(n.rafHz),s=P(n.displayHz),o=P(ie(n,"load","p99Ms"));if(r===null||s===null||o===null)return null;return`${r} Hz rAF on a ${s} Hz display, p99 ${o} ms under load`}case"inputLatency":{for(let r of["pointerdown","keydown","pointermove"]){let s=P(ie(n,r,"toFrame","p50Ms"));if(s!==null)return`${r} p50 ${s} ms to next frame`}return null}case"audioLatency":{let r=n.afterGesturedResume??n.afterUngesturedResume,s=P(r?.baseLatency),o=P(r?.outputLatency),a=(i)=>i===null?"n/a":`${Math.round(i*1e4)/10} ms`;return`base ${a(s)}, output ${a(o)}`}default:return null}}function Ke(e,t){if(!t)return"not probed";let n=U(e,t);return n?`**${t.status}** - ${n}`:`**${t.status}**`}function j(e){return e.replaceAll("|","\\|").replaceAll(`
57
+ `," ")}function Yt(e){let t=Ve(e),n=[We,"","<!-- Generated by `buddy docs:capabilities` from the records in"," storage/framework/core/desktop/src/probe/results/. Re-measure with",` \`${B} --record\`; do not edit this by hand. -->`,"","Whether a Craft window can host a game or other frame-driven content that somebody else","builds, measured by the probe harness in `@stacksjs/desktop-build`","([stacksjs/stacks#877](https://github.com/stacksjs/stacks/issues/877)). Ordinary browsers","on the same machine are measured alongside it for comparison.","","> **How current is this?** Unlike the driver tables above, these are measurements, not","> claims CI can re-check. Each column is one run on one machine, and says when, against","> which host version, on which OS and hardware. If your Craft is newer than the column,",`> the numbers describe something you are not running: re-measure with \`${B} --record\`.`,""];if(t.length===0)return n.push(`No measurement has been recorded yet. Run \`${B} --record\`.`,"",re),n.join(`
58
+ `);n.push(`| Probe | ${t.map((r)=>j(se(r))).join(" | ")} |`,`|---|${t.map(()=>"---").join("|")}|`);for(let[r,s]of b)n.push(`| ${s} | ${t.map((o)=>j(Ke(r,o.probes[r]))).join(" | ")} |`);n.push(`| Measured on | ${t.map((r)=>j(oe(r))).join(" | ")} |`,`| Measured at | ${t.map((r)=>r.measuredAt.slice(0,10)).join(" | ")} |`,"","- **available** / **measured** - the API exists and the probe exercised it.","- **unavailable** - the API is missing, or creating it failed.","- **requires-interaction** - needs a real click, key press or controller that the run did not have. What was still observable is in the notes; no number is invented.","- **denied** - requested from a real click and refused.","");for(let r of t){let s=r.machine.display.refreshHz===null?"unknown refresh rate":`${r.machine.display.refreshHz} Hz display`;n.push(`**${se(r)}** - ${oe(r)}, ${s}`,"");for(let[o,a]of b){let i=r.probes[o];if(i)n.push(`- **${a}** - ${i.note}`)}n.push("")}return n.push("**Out of scope.** Stacks is not shipping a game engine, a physics engine, an audio mixer or","an asset pipeline. This section establishes what a Craft window gives an engine somebody","else wrote; it is not a plan to write one.","",re),n.join(`
59
+ `)}function k(e){try{let t=Bun.spawnSync(e,{stdout:"pipe",stderr:"ignore"});return t.exitCode===0?t.stdout.toString().trim():null}catch{return null}}function Et(e){return e?.match(/craft version\s+(\d+\.\d+\.\d+\S*)/i)?.[1]??null}function Mt(e){if(!e)return null;try{let n=(JSON.parse(e).SPDisplaysDataType??[]).flatMap((o)=>o.spdisplays_ndrvs??[]),r=n.find((o)=>o.spdisplays_main==="spdisplays_yes")??n[0];if(!r)return null;let s=r._spdisplays_resolution??"unknown";return{name:r._name??"unknown",resolution:s,refreshHz:Y(s)}}catch{return null}}function Dt(e,t,n){let r=l.platform,s=It[r]??r,o=Pe.release(),a,i="unknown",u=Pe.cpus()[0]?.model?.trim()||"unknown",c={name:"unknown",resolution:"unknown",refreshHz:null};if(r==="darwin")s="macOS",o=k(["sw_vers","-productVersion"])??o,a=k(["sw_vers","-buildVersion"])??void 0,i=k(["sysctl","-n","hw.model"])??i,u=k(["sysctl","-n","machdep.cpu.brand_string"])??u,c=Mt(k(["system_profiler","SPDisplaysDataType","-json"]))??c;else if(r==="linux")i=k(["cat","/sys/devices/virtual/dmi/id/product_name"])??i;let d=ee(n);return{measuredAt:new Date().toISOString(),host:e==="craft"?{kind:"craft",name:"craft",version:t??"unknown"}:{kind:"browser",name:d.name,version:d.version},os:{platform:r,arch:l.arch,name:s,version:o,...a?{build:a}:{}},machine:{model:i,cpu:u,display:c}}}var It={darwin:"macOS",linux:"Linux",win32:"Windows"};async function At(){let e=$t(St(import.meta.url)),t=[L(e,"page.ts"),L(e,"page.js")].find(Se);if(!t)throw Error("The probe page script is missing; reinstall @stacksjs/desktop-build.");let n=await Bun.build({entrypoints:[t],target:"browser",format:"esm",minify:!0});if(!n.success)throw Error(`Bundling the probe page failed:
60
60
  ${n.logs.map(String).join(`
61
- `)}`);return await n.outputs[0].text()}function It(e,t){if(t===!1)return null;if(l.platform==="darwin")return typeof t==="string"?["open","-a",t,e]:["open",e];if(l.platform==="win32")return["cmd","/c","start","",e];return["xdg-open",e]}async function At(e){let t=e.log??((m)=>console.log(m)),n={...V,...e.page};if(e.mode==="browser")n.tryNativeFullscreen=!1;let r=null,s=null;if(e.mode==="craft"){await Promise.resolve().then(() => ye());if(r=be(e.craftBin),s=Rt(k([r,"--version"])),!s)throw Error(`Could not read a version from \`${r} --version\`; is it a Craft binary?`)}let o=G(await Mt(),n),a,i,u=new Promise((m,h)=>{a=m,i=h}),c=Bun.serve({hostname:"127.0.0.1",port:e.port??0,async fetch(m){let{pathname:h}=new URL(m.url);if(m.method==="POST"&&h==="/results")return a(await m.json()),new Response("ok");if(m.method==="POST"&&h==="/progress")return t(` page: ${await m.text()}`),new Response("ok");if(m.method==="POST"&&h==="/failed")return i(Error(`The probe page failed: ${await m.text()}`)),new Response("ok");if(h==="/")return new Response(o,{headers:{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}});return new Response("not found",{status:404})}}),d=c.url.href,p=null,g=()=>{p?.kill(),l.exit(130)};l.once("SIGINT",g),l.once("SIGTERM",g);let v=e.timeoutMs??n.idleMs+n.loadMs+n.interactionWindowMs+60000,$e=setTimeout(()=>i(Error(`No results within ${Math.round(v/1000)} s. Was the window visible?`)),v);try{if(e.mode==="craft")t(`Opening ${d} in Craft ${s} (${r})`),p=Bun.spawn([r,d,"--title","Stacks interactive probe","--width","1280","--height","800"],{stdout:"ignore",stderr:"ignore"}),p.exited.then((M)=>i(Error(`Craft exited (code ${M}) before the probe finished`)));else{t(`Probe page: ${d}`);let M=It(d,e.open??!0);if(M)Bun.spawn(M,{stdout:"ignore",stderr:"ignore"});else t("Open that URL in the browser to measure, and keep its window visible.")}let m=await u,h=St(e.mode,s,m.userAgent);return I(te(m,h),"the probe result")}finally{clearTimeout($e),l.off("SIGINT",g),l.off("SIGTERM",g),p?.kill(),c.stop(!0)}}function Dt(e){let t=Math.max(...b.map(([,r])=>r.length)),n=[`${e.host.kind==="craft"?`Craft ${e.host.version}`:`${e.host.name} ${e.host.version}`} on ${e.os.name} ${e.os.version} (${e.machine.model}, ${e.machine.cpu}), display ${e.machine.display.refreshHz??"unknown"} Hz`];for(let[r,s]of b){let o=e.probes[r],a=U(r,o);n.push(` ${s.padEnd(t)} ${o.status}${a?` - ${a}`:""}`)}return n.join(`
62
- `)}function w(e){let t=`--${e}=`;for(let n=0;n<l.argv.length;n++){let r=l.argv[n];if(r===`--${e}`){let s=l.argv[n+1];return s&&!s.startsWith("--")?s:!0}if(r.startsWith(t))return r.slice(t.length)}return}function C(e){let t=w(e);if(t===void 0||t===!0)return;let n=Number(t);if(!Number.isFinite(n)||n<0)throw Error(`--${e} must be a non-negative number of seconds`);return n*1000}var Pe=xt(S,"../../../../../../..");async function Ft(){let e=w("browser"),t=w("record")!==void 0,n=w("out"),r=w("cpu-load"),s=w("instances");if(t){if(xe(Pe,l.cwd()).startsWith("..")||!Re(L(Pe,"storage/framework/core/buddy")))throw Error("--record writes into the Stacks framework repository; run it from that checkout, or use --out <file>.")}let o={},a=C("idle"),i=C("load"),u=C("interaction-window");if(a!==void 0)o.idleMs=a;if(i!==void 0)o.loadMs=i;if(u!==void 0)o.interactionWindowMs=u;if(typeof r==="string")o.cpuLoadMsPerFrame=Number(r);if(typeof s==="string")o.instances=Number(s);let c=typeof e==="string"?e:!0;if(w("no-open")!==void 0)c=!1;let d=w("craft-bin"),p=await At({mode:e===void 0?"craft":"browser",open:c,craftBin:typeof d==="string"?d:void 0,timeoutMs:C("timeout"),page:o});console.log(`
63
- ${Dt(p)}
61
+ `)}`);return await n.outputs[0].text()}function Ft(e,t){if(t===!1)return null;if(l.platform==="darwin")return typeof t==="string"?["open","-a",t,e]:["open",e];if(l.platform==="win32")return["cmd","/c","start","",e];return["xdg-open",e]}async function Ct(e){let t=e.log??((m)=>console.log(m)),n={...V,...e.page};if(e.mode==="browser")n.tryNativeFullscreen=!1;let r=null,s=null;if(e.mode==="craft"){await Promise.resolve().then(() => ve());if(r=ye(e.craftBin),s=Et(k([r,"--version"])),!s)throw Error(`Could not read a version from \`${r} --version\`; is it a Craft binary?`)}let o=G(await At(),n),a,i,u=new Promise((m,h)=>{a=m,i=h}),c=Bun.serve({hostname:"127.0.0.1",port:e.port??0,async fetch(m){let{pathname:h}=new URL(m.url);if(m.method==="POST"&&h==="/results")return a(await m.json()),new Response("ok");if(m.method==="POST"&&h==="/progress")return t(` page: ${await m.text()}`),new Response("ok");if(m.method==="POST"&&h==="/failed")return i(Error(`The probe page failed: ${await m.text()}`)),new Response("ok");if(h==="/")return new Response(o,{headers:{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}});return new Response("not found",{status:404})}}),d=c.url.href,p=null,g=()=>{p?.kill(),l.exit(130)};l.once("SIGINT",g),l.once("SIGTERM",g);let v=e.timeoutMs??n.idleMs+n.loadMs+n.interactionWindowMs+60000,Ee=setTimeout(()=>i(Error(`No results within ${Math.round(v/1000)} s. Was the window visible?`)),v);try{if(e.mode==="craft")t(`Opening ${d} in Craft ${s} (${r})`),p=Bun.spawn([r,d,"--title","Stacks interactive probe","--width","1280","--height","800"],{stdout:"ignore",stderr:"ignore"}),p.exited.then((M)=>i(Error(`Craft exited (code ${M}) before the probe finished`)));else{t(`Probe page: ${d}`);let M=Ft(d,e.open??!0);if(M)Bun.spawn(M,{stdout:"ignore",stderr:"ignore"});else t("Open that URL in the browser to measure, and keep its window visible.")}let m=await u,h=Dt(e.mode,s,m.userAgent);return D(te(m,h),"the probe result")}finally{clearTimeout(Ee),l.off("SIGINT",g),l.off("SIGTERM",g),p?.kill(),c.stop(!0)}}function Lt(e){let t=Math.max(...b.map(([,r])=>r.length)),n=[`${e.host.kind==="craft"?`Craft ${e.host.version}`:`${e.host.name} ${e.host.version}`} on ${e.os.name} ${e.os.version} (${e.machine.model}, ${e.machine.cpu}), display ${e.machine.display.refreshHz??"unknown"} Hz`];for(let[r,s]of b){let o=e.probes[r],a=U(r,o);n.push(` ${s.padEnd(t)} ${o.status}${a?` - ${a}`:""}`)}return n.join(`
62
+ `)}function w(e){let t=`--${e}=`;for(let n=0;n<l.argv.length;n++){let r=l.argv[n];if(r===`--${e}`){let s=l.argv[n+1];return s&&!s.startsWith("--")?s:!0}if(r.startsWith(t))return r.slice(t.length)}return}function C(e){let t=w(e);if(t===void 0||t===!0)return;let n=Number(t);if(!Number.isFinite(n)||n<0)throw Error(`--${e} must be a non-negative number of seconds`);return n*1000}var Re=Rt(S,"../../../../../../..");async function Ot(){let e=w("browser"),t=w("record")!==void 0,n=w("out"),r=w("cpu-load"),s=w("instances");if(t){if($e(Re,l.cwd()).startsWith("..")||!Se(L(Re,"storage/framework/core/buddy")))throw Error("--record writes into the Stacks framework repository; run it from that checkout, or use --out <file>.")}let o={},a=C("idle"),i=C("load"),u=C("interaction-window");if(a!==void 0)o.idleMs=a;if(i!==void 0)o.loadMs=i;if(u!==void 0)o.interactionWindowMs=u;if(typeof r==="string")o.cpuLoadMsPerFrame=Number(r);if(typeof s==="string")o.instances=Number(s);let c=typeof e==="string"?e:!0;if(w("no-open")!==void 0)c=!1;let d=w("craft-bin"),p=await Ct({mode:e===void 0?"craft":"browser",open:c,craftBin:typeof d==="string"?d:void 0,timeoutMs:C("timeout"),page:o});console.log(`
63
+ ${Lt(p)}
64
64
  `);let g=`${JSON.stringify(p,null,2)}
65
- `;if(t){kt(S,{recursive:!0});let v=L(S,ne(p));ke(v,g),console.log(`Recorded ${xe(l.cwd(),v)}. Run \`buddy docs:capabilities\` to publish it.`)}if(typeof n==="string")ke(n,g),console.log(`Wrote ${n}`);if(!t&&typeof n!=="string")console.log(g)}if(import.meta.main)try{await Ft()}catch(e){l.stderr.write(`${e instanceof Error?e.message:String(e)}
66
- `),l.exit(1)}export{St as collectHostFacts,$t as mainDisplayFromProfiler,Rt as parseCraftVersion,Ft as run,At as runProbe,Dt as summarize};
65
+ `;if(t){Pt(S,{recursive:!0});let v=L(S,ne(p));xe(v,g),console.log(`Recorded ${$e(l.cwd(),v)}. Run \`buddy docs:capabilities\` to publish it.`)}if(typeof n==="string")xe(n,g),console.log(`Wrote ${n}`);if(!t&&typeof n!=="string")console.log(g)}if(import.meta.main)try{await Ot()}catch(e){l.stderr.write(`${e instanceof Error?e.message:String(e)}
66
+ `),l.exit(1)}export{Dt as collectHostFacts,Mt as mainDisplayFromProfiler,Et as parseCraftVersion,Ot as run,Ct as runProbe,Lt as summarize};
package/dist/updater.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export declare function checkForDesktopUpdate(options: CheckDesktopUpdateOptions): Promise<DesktopUpdateManifest | null>;
2
- export declare function stageDesktopUpdate(manifest: DesktopUpdateManifest, destination: string, fetcher?: DesktopFetcher, trustedKeys?: Record<string, string>): Promise<StagedDesktopUpdate>;
2
+ export declare function stageDesktopUpdate(manifest: DesktopUpdateManifest, destination: string, fetcher?: DesktopFetcher, trustedKeys?: Record<string, string>, target?: DesktopUpdateTarget): Promise<StagedDesktopUpdate>;
3
3
  export declare function compareVersions(left: string, right: string): number;
4
4
  export declare function validateUpdateManifest(input: unknown): DesktopUpdateManifest;
5
5
  export declare function updateManifestPayload(manifest: DesktopUpdateManifest): Buffer;
@@ -17,11 +17,17 @@ export declare interface DesktopUpdateManifest {
17
17
  publishedAt?: string
18
18
  notes?: string
19
19
  }
20
+ /** The build an update has to match: this process's, unless told otherwise. */
21
+ export declare interface DesktopUpdateTarget {
22
+ platform: string
23
+ architecture: string
24
+ }
20
25
  export declare interface CheckDesktopUpdateOptions {
21
26
  currentVersion: string
22
27
  manifestUrl: string
23
28
  trustedKeys: Record<string, string>
24
29
  fetcher?: DesktopFetcher
30
+ target?: DesktopUpdateTarget
25
31
  }
26
32
  export declare interface StagedDesktopUpdate {
27
33
  manifest: DesktopUpdateManifest
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/desktop-build",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.85",
5
+ "version": "0.75.87",
6
6
  "description": "Build and launch Craft desktop applications for Stacks",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -70,8 +70,8 @@
70
70
  "craft-native": ">=0.0.118"
71
71
  },
72
72
  "devDependencies": {
73
- "@stacksjs/build": "0.75.85",
73
+ "@stacksjs/build": "0.75.87",
74
74
  "better-dx": "^0.2.24",
75
- "@stacksjs/server": "0.75.85"
75
+ "@stacksjs/server": "0.75.87"
76
76
  }
77
77
  }