@stacksjs/testing 0.75.233 → 0.75.237

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/browser.d.ts CHANGED
@@ -34,9 +34,23 @@ export declare function prepareMacChrome(options?: { source?: string, directory?
34
34
  * function that stops it.
35
35
  */
36
36
  export declare function quietLiveReload(cdp: Cdp, patterns?: readonly string[]): Promise<() => Promise<void>>;
37
+ /** Stop every shared Chrome now, instead of after it lingers. */
38
+ export declare function closeSharedBrowsers(): Promise<void>;
37
39
  /**
38
- * Start a private headless Chrome and open a session on its page, with the
39
- * `Page`, `Runtime`, `Network` and `Log` domains enabled.
40
+ * Open a session on a page of a headless Chrome, with the `Page`, `Runtime`,
41
+ * `Network` and `Log` domains enabled.
42
+ *
43
+ * **One Chrome per process, a fresh browser context per launch.** Starting
44
+ * Chrome is the slow part (seconds, more on a loaded machine), so launches
45
+ * with the same executable, headless mode and flags share one process, and
46
+ * each gets its own browser context: separate cookies, storage, cache and
47
+ * service workers, so a journey signed in as one person and another signed in
48
+ * beside it never see each other's session. `close()` disposes the context; the
49
+ * Chrome lingers briefly ({@link SHARED_CHROME_LINGER_MS}) for the next launch,
50
+ * then stops, and `closeSharedBrowsers()` stops it at once.
51
+ *
52
+ * A launch that keeps a `profile`, passes `shared: false`, or runs with
53
+ * `STACKS_BROWSER_SHARED=0` gets a Chrome of its own, stopped by `close()`.
40
54
  */
41
55
  export declare function launch(options?: LaunchOptions): Promise<LaunchedChrome>;
42
56
  /**
@@ -79,8 +93,15 @@ export declare function formatPageAudit(report: PageAuditReport): string;
79
93
  export declare function pageAuditClean(report: PageAuditReport): boolean;
80
94
  /** stx's live-reload stream. */
81
95
  export declare const LIVE_RELOAD_PATTERNS: readonly string[];
96
+ /**
97
+ * How long a shared Chrome outlives its last session, for the next `launch()`
98
+ * to reuse. Long enough for a suite that closes one journey and opens the
99
+ * next; short enough that a finished script exits promptly.
100
+ */
101
+ export declare const SHARED_CHROME_LINGER_MS: number;
82
102
  export declare interface CdpOptions {
83
103
  commandTimeoutMs?: number
104
+ connectTimeoutMs?: number
84
105
  }
85
106
  /** One `Fetch.enable` pattern, as the protocol spells it. */
86
107
  export declare interface FetchPattern {
@@ -123,6 +144,23 @@ export declare interface LaunchOptions extends CdpOptions {
123
144
  acceptDialogs?: boolean
124
145
  userAgent?: boolean | string
125
146
  startTimeoutMs?: number
147
+ shared?: boolean
148
+ }
149
+ export declare interface ChromeLaunchDiagnostics {
150
+ pid: number | null
151
+ chromePath: string
152
+ profile: string
153
+ temporaryProfile: boolean
154
+ elapsedMs: number
155
+ exitCodeBeforeCleanup: number | null
156
+ signalCodeBeforeCleanup: string | number | null
157
+ exitCode: number | null
158
+ signalCode: string | number | null
159
+ portFileExisted: boolean
160
+ portFileContents: string | null
161
+ stderrTail: string
162
+ stderrTruncated: boolean
163
+ cleanupError: string | null
126
164
  }
127
165
  export declare interface LaunchedChrome {
128
166
  cdp: Cdp
@@ -226,3 +264,7 @@ export declare class Cdp {
226
264
  evaluate<T = any>(expression: string): Promise<T>;
227
265
  disconnect(): void;
228
266
  }
267
+ export declare class ChromeLaunchError extends Error {
268
+ public readonly diagnostics: ChromeLaunchDiagnostics;
269
+ constructor(cause: unknown, diagnostics: ChromeLaunchDiagnostics);
270
+ }
package/dist/browser.js CHANGED
@@ -1,6 +1,8 @@
1
1
  // @bun
2
- import{existsSync as C,mkdirSync as U,mkdtempSync as M,readFileSync as q,renameSync as B,rmSync as R}from"fs";import{tmpdir as L}from"os";import{join as m}from"path";import b from"process";async function S(e,t=12000,r={}){let n=Date.now(),s=await e();while(!s&&Date.now()-n<t)await Bun.sleep(r.intervalMs??100),s=await e();if(!s)throw Error(`Timed out after ${t}ms${r.label?` waiting for ${r.label}`:""}`);return s}var _=new Set(["Fetch.continueRequest","Fetch.fulfillRequest","Fetch.failRequest","Fetch.continueResponse"]);function H(e,t){let r="";for(let n=0;n<e.length;n++){let s=e[n];if(s==="\\"&&n+1<e.length)r+=T(e[++n]);else if(s==="*")r+="[\\s\\S]*";else if(s==="?")r+="[\\s\\S]";else r+=T(s)}return new RegExp(`^${r}$`).test(t)}function T(e){return e.replace(/[.*+?^${}()|[\]\\/]/g,"\\$&")}function F(e,t){let r=t.responseStatusCode!=null||t.responseErrorReason!=null?"Response":"Request";if((e.requestStage??"Request")!==r)return!1;if(e.resourceType&&e.resourceType!==t.resourceType)return!1;return H(e.urlPattern??"*",t.request?.url??"")}class O{socket;id=0;pending=new Map;listeners=new Map;commandTimeoutMs;interceptors=[];ownFetch=null;answered=new Map;errors=[];inFlight=new Map;closed=!1;constructor(e,t={}){this.socket=e;this.commandTimeoutMs=t.commandTimeoutMs??30000,e.addEventListener("close",()=>{this.closed=!0;for(let[r,n]of this.pending)clearTimeout(n.timer),n.reject(Error("Chrome closed the connection")),this.pending.delete(r)}),e.addEventListener("message",(r)=>this.receive(String(r.data)))}receive(e){let t;try{t=JSON.parse(e)}catch{return}if(t.id){let r=this.pending.get(t.id);if(!r)return;if(this.pending.delete(t.id),clearTimeout(r.timer),t.error)r.reject(Error(t.error.message));else r.resolve(t.result);return}if(this.track(t.method,t.params??{}),t.method==="Fetch.requestPaused"&&!this.routePaused(t.params??{}))return;this.dispatch(t.method,t.params??{})}dispatch(e,t){for(let r of[...this.listeners.get(e)??[]])try{r(t)}catch(n){this.errors.push(`Listener for ${e} threw: ${n?.message??String(n)}`)}}routePaused(e){this.remember(e.requestId,!1);let t=this.interceptors.filter((s)=>s.patterns.some((i)=>F(i,e)));for(let s of t)try{let i=s.handler(e,this);if(i&&typeof i.catch==="function")i.catch((a)=>this.errors.push(`Fetch handler threw: ${a?.message??String(a)}`))}catch(i){this.errors.push(`Fetch handler threw: ${i?.message??String(i)}`)}let r=(this.listeners.get("Fetch.requestPaused")?.size??0)>0,n=this.ownFetch?this.ownFetch.patterns.some((s)=>F(s,e)):!1;if(!t.length&&!n&&!r)this.send("Fetch.continueRequest",{requestId:e.requestId}).catch(()=>{});return r&&(n||!t.length)}remember(e,t){if(this.answered.delete(e),this.answered.set(e,t),this.answered.size>2000){for(let[r]of this.answered)if(this.answered.delete(r),this.answered.size<=1000)break}}async applyFetch(){let e=[...this.interceptors.flatMap((t)=>t.patterns),...this.ownFetch?.patterns??[]];if(!e.length)return await this.command("Fetch.disable");return await this.command("Fetch.enable",{...this.ownFetch?.params,patterns:e})}async intercept(e,t){if(!e.length)throw TypeError("intercept needs at least one pattern");let r={patterns:e.map((s)=>({...s})),handler:t};this.interceptors.push(r);try{await this.applyFetch()}catch(s){throw this.interceptors=this.interceptors.filter((i)=>i!==r),s}let n=!1;return async()=>{if(n)return;if(n=!0,this.interceptors=this.interceptors.filter((s)=>s!==r),!this.closed)await this.applyFetch()}}track(e,t){if(e==="Network.requestWillBeSent")this.inFlight.set(t.requestId,`${t.request?.method} ${t.request?.url}`);else if(e==="Network.loadingFinished"||e==="Network.loadingFailed")this.inFlight.delete(t.requestId);else if(e==="Network.responseReceived"&&t.response?.mimeType==="text/event-stream")this.inFlight.delete(t.requestId);if(e==="Runtime.exceptionThrown")this.errors.push(t.exceptionDetails?.exception?.description||t.exceptionDetails?.text||"Runtime exception");else if(e==="Runtime.consoleAPICalled"&&t.type==="error")this.errors.push((t.args||[]).map((r)=>r.value??r.description??"").join(" "));else if(e==="Log.entryAdded"&&t.entry?.level==="error"&&!String(t.entry.text).includes("favicon"))this.errors.push(t.entry.text);else if(e==="Network.loadingFailed"&&!t.canceled)this.errors.push(`Network: ${t.errorText||"request failed"}`)}on(e,t){let r=this.listeners.get(e);if(!r)this.listeners.set(e,r=new Set);return r.add(t),()=>this.off(e,t)}off(e,t){this.listeners.get(e)?.delete(t)}once(e,t=()=>!0,r=this.commandTimeoutMs){return new Promise((n,s)=>{if(this.closed)return s(Error("Chrome closed the connection"));let i=setTimeout(()=>{c(),clearInterval(a),s(Error(`Timed out after ${r}ms waiting for ${e}`))},r),a=setInterval(()=>{if(!this.closed)return;clearTimeout(i),clearInterval(a),c(),s(Error("Chrome closed the connection"))},100),c=this.on(e,(p)=>{if(!t(p))return;clearTimeout(i),clearInterval(a),c(),n(p)})})}send(e,t={}){if(e==="Fetch.enable"){let{patterns:r,...n}=t;return this.ownFetch={patterns:r?.length?r.map((s)=>({...s})):[{urlPattern:"*"}],params:n},this.closed?this.command(e,t):this.applyFetch()}if(e==="Fetch.disable")return this.ownFetch=null,this.closed?this.command(e,t):this.applyFetch();if(_.has(e)&&typeof t.requestId==="string"){if(this.answered.get(t.requestId)===!0)return Promise.resolve({});if(this.answered.has(t.requestId))this.remember(t.requestId,!0)}return this.command(e,t)}command(e,t={}){let r=++this.id;return new Promise((n,s)=>{if(this.closed)return s(Error("Chrome closed the connection"));let i=setTimeout(()=>{this.pending.delete(r),s(Error(`Chrome command timed out: ${e}`))},this.commandTimeoutMs);this.pending.set(r,{resolve:n,reject:s,timer:i}),this.socket.send(JSON.stringify({id:r,method:e,params:t}))})}async evaluate(e){let t=await this.send("Runtime.evaluate",{expression:e,awaitPromise:!0,returnByValue:!0});if(t.exceptionDetails)throw Error(t.exceptionDetails.exception?.description||t.exceptionDetails.text);return t.result?.value}disconnect(){try{this.socket.close()}catch{}}}async function j(e,t={}){let r=new WebSocket(e);return await new Promise((n,s)=>{r.addEventListener("open",()=>n(),{once:!0}),r.addEventListener("error",()=>s(Error(`Could not connect to Chrome at ${e}`)),{once:!0})}),new O(r,t)}var D=["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome","/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing","/Applications/Chromium.app/Contents/MacOS/Chromium","/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary"],V=["google-chrome","google-chrome-stable","chromium","chromium-browser","chrome"],I=["KSProductID","KSUpdateURL","KSChannelID","KSVersion"],E="dev.stacks.testing.chrome";function W(){if(b.env.CHROME_PATH)return b.env.CHROME_PATH;if(b.platform==="darwin"){let e=D.find((t)=>C(t));if(e)return e}for(let e of V){let t=Bun.which(e);if(t)return t}return null}function N(e){let t=/^(.*?\.app)\/Contents\/MacOS\/[^/]+$/.exec(e);return t?t[1]:null}function P(e,t){let r=Bun.spawnSync(["/usr/libexec/PlistBuddy","-c",t,e],{stdout:"pipe",stderr:"pipe"});return{ok:r.exitCode===0,out:r.stdout.toString().trim()}}function G(e){let t=N(e);if(!t||b.platform!=="darwin")return!1;let r=m(t,"Contents/Info.plist");return C(r)&&I.some((n)=>P(r,`Print :${n}`).ok)}function K(e={}){if(b.platform!=="darwin")throw Error("prepareMacChrome is for macOS; elsewhere launch Chrome as installed");let t=e.source??D[0],r=N(t);if(!r||!C(r))throw Error(`No Chrome app bundle at ${t}`);let n=m(r,"Contents/Info.plist"),s=P(n,"Print :CFBundleShortVersionString").out||"unknown",i=P(n,"Print :CFBundleExecutable").out||"Google Chrome",a=e.directory??m(L(),`stacks-chrome-${s.replace(/[^\w.-]/g,"_")}`),c=m(a,"Chrome.app"),p=m(c,"Contents/MacOS",i),h=m(c,"Contents/Info.plist");if(C(p)&&P(h,"Print :CFBundleIdentifier").out===E&&P(h,"Print :CFBundleShortVersionString").out===s)return{executable:p,app:c,reused:!0};U(a,{recursive:!0});let g=M(m(a,"staging-")),l=m(g,"Chrome.app"),o=(f,y)=>{let d=Bun.spawnSync(f,{stdout:"pipe",stderr:"pipe"});if(d.exitCode!==0)throw Error(`${y} failed: ${d.stderr.toString().trim()}`)};try{o(["cp","-cR",r,l],"Cloning Chrome");let f=m(l,"Contents/Info.plist");for(let y of I)P(f,`Delete :${y}`);o(["/usr/libexec/PlistBuddy","-c",`Set :CFBundleIdentifier ${E}`,f],"Renaming the Chrome bundle"),o(["codesign","--force","--deep","--sign","-",l],"Re-signing Chrome"),R(c,{recursive:!0,force:!0}),B(l,c)}finally{R(g,{recursive:!0,force:!0})}return{executable:p,app:c,reused:!1}}var z=["*/_stx/hmr*"];async function J(e,t=z){if(!t.length)return async()=>{};return await e.intercept(t.map((r)=>({urlPattern:r})),(r,n)=>{n.send("Fetch.fulfillRequest",{requestId:r.requestId,responseCode:200,responseHeaders:[{name:"Content-Type",value:"text/event-stream"}],body:Buffer.from(`retry: 86400000
2
+ import{closeSync as ee,existsSync as S,mkdirSync as te,mkdtempSync as W,openSync as re,readSync as ne,renameSync as oe,rmSync as x}from"fs";import{tmpdir as V}from"os";import{join as y}from"path";import P from"process";async function K(e,t=12000,r={}){let o=Date.now(),n=await e();while(!n&&Date.now()-o<t)await Bun.sleep(r.intervalMs??100),n=await e();if(!n)throw Error(`Timed out after ${t}ms${r.label?` waiting for ${r.label}`:""}`);return n}var ie=new Set(["Fetch.continueRequest","Fetch.fulfillRequest","Fetch.failRequest","Fetch.continueResponse"]);function se(e,t){let r="";for(let o=0;o<e.length;o++){let n=e[o];if(n==="\\"&&o+1<e.length)r+=_(e[++o]);else if(n==="*")r+="[\\s\\S]*";else if(n==="?")r+="[\\s\\S]";else r+=_(n)}return new RegExp(`^${r}$`).test(t)}function _(e){return e.replace(/[.*+?^${}()|[\]\\/]/g,"\\$&")}function B(e,t){let r=t.responseStatusCode!=null||t.responseErrorReason!=null?"Response":"Request";if((e.requestStage??"Request")!==r)return!1;if(e.resourceType&&e.resourceType!==t.resourceType)return!1;return se(e.urlPattern??"*",t.request?.url??"")}class J{socket;id=0;pending=new Map;listeners=new Map;commandTimeoutMs;interceptors=[];ownFetch=null;answered=new Map;errors=[];inFlight=new Map;closed=!1;constructor(e,t={}){this.socket=e;this.commandTimeoutMs=t.commandTimeoutMs??30000,e.addEventListener("close",()=>this.markClosed()),e.addEventListener("message",(r)=>this.receive(String(r.data)))}markClosed(){this.closed=!0;for(let[e,t]of this.pending)clearTimeout(t.timer),t.reject(Error("Chrome closed the connection")),this.pending.delete(e)}receive(e){let t;try{t=JSON.parse(e)}catch{return}if(t.id){let r=this.pending.get(t.id);if(!r)return;if(this.pending.delete(t.id),clearTimeout(r.timer),t.error)r.reject(Error(t.error.message));else r.resolve(t.result);return}if(this.track(t.method,t.params??{}),t.method==="Fetch.requestPaused"&&!this.routePaused(t.params??{}))return;this.dispatch(t.method,t.params??{})}dispatch(e,t){for(let r of[...this.listeners.get(e)??[]])try{r(t)}catch(o){this.errors.push(`Listener for ${e} threw: ${o?.message??String(o)}`)}}routePaused(e){this.remember(e.requestId,!1);let t=this.interceptors.filter((n)=>n.patterns.some((i)=>B(i,e)));for(let n of t)try{let i=n.handler(e,this);if(i&&typeof i.catch==="function")i.catch((a)=>this.errors.push(`Fetch handler threw: ${a?.message??String(a)}`))}catch(i){this.errors.push(`Fetch handler threw: ${i?.message??String(i)}`)}let r=(this.listeners.get("Fetch.requestPaused")?.size??0)>0,o=this.ownFetch?this.ownFetch.patterns.some((n)=>B(n,e)):!1;if(!t.length&&!o&&!r)this.send("Fetch.continueRequest",{requestId:e.requestId}).catch(()=>{});return r&&(o||!t.length)}remember(e,t){if(this.answered.delete(e),this.answered.set(e,t),this.answered.size>2000){for(let[r]of this.answered)if(this.answered.delete(r),this.answered.size<=1000)break}}async applyFetch(){let e=[...this.interceptors.flatMap((t)=>t.patterns),...this.ownFetch?.patterns??[]];if(!e.length)return await this.command("Fetch.disable");return await this.command("Fetch.enable",{...this.ownFetch?.params,patterns:e})}async intercept(e,t){if(!e.length)throw TypeError("intercept needs at least one pattern");let r={patterns:e.map((n)=>({...n})),handler:t};this.interceptors.push(r);try{await this.applyFetch()}catch(n){throw this.interceptors=this.interceptors.filter((i)=>i!==r),n}let o=!1;return async()=>{if(o)return;if(o=!0,this.interceptors=this.interceptors.filter((n)=>n!==r),!this.closed)await this.applyFetch()}}track(e,t){if(e==="Network.requestWillBeSent")this.inFlight.set(t.requestId,`${t.request?.method} ${t.request?.url}`);else if(e==="Network.loadingFinished"||e==="Network.loadingFailed")this.inFlight.delete(t.requestId);else if(e==="Network.responseReceived"&&t.response?.mimeType==="text/event-stream")this.inFlight.delete(t.requestId);if(e==="Runtime.exceptionThrown")this.errors.push(t.exceptionDetails?.exception?.description||t.exceptionDetails?.text||"Runtime exception");else if(e==="Runtime.consoleAPICalled"&&t.type==="error")this.errors.push((t.args||[]).map((r)=>r.value??r.description??"").join(" "));else if(e==="Log.entryAdded"&&t.entry?.level==="error"&&!String(t.entry.text).includes("favicon"))this.errors.push(t.entry.text);else if(e==="Network.loadingFailed"&&!t.canceled)this.errors.push(`Network: ${t.errorText||"request failed"}`)}on(e,t){let r=this.listeners.get(e);if(!r)this.listeners.set(e,r=new Set);return r.add(t),()=>this.off(e,t)}off(e,t){this.listeners.get(e)?.delete(t)}once(e,t=()=>!0,r=this.commandTimeoutMs){return new Promise((o,n)=>{if(this.closed)return n(Error("Chrome closed the connection"));let i=setTimeout(()=>{s(),clearInterval(a),n(Error(`Timed out after ${r}ms waiting for ${e}`))},r),a=setInterval(()=>{if(!this.closed)return;clearTimeout(i),clearInterval(a),s(),n(Error("Chrome closed the connection"))},100),s=this.on(e,(d)=>{if(!t(d))return;clearTimeout(i),clearInterval(a),s(),o(d)})})}send(e,t={}){if(e==="Fetch.enable"){let{patterns:r,...o}=t;return this.ownFetch={patterns:r?.length?r.map((n)=>({...n})):[{urlPattern:"*"}],params:o},this.closed?this.command(e,t):this.applyFetch()}if(e==="Fetch.disable")return this.ownFetch=null,this.closed?this.command(e,t):this.applyFetch();if(ie.has(e)&&typeof t.requestId==="string"){if(this.answered.get(t.requestId)===!0)return Promise.resolve({});if(this.answered.has(t.requestId))this.remember(t.requestId,!0)}return this.command(e,t)}command(e,t={}){let r=++this.id;return new Promise((o,n)=>{if(this.closed)return n(Error("Chrome closed the connection"));let i=setTimeout(()=>{this.pending.delete(r),n(Error(`Chrome command timed out: ${e}`))},this.commandTimeoutMs);this.pending.set(r,{resolve:o,reject:n,timer:i}),this.socket.send(JSON.stringify({id:r,method:e,params:t}))})}async evaluate(e){let t=await this.send("Runtime.evaluate",{expression:e,awaitPromise:!0,returnByValue:!0});if(t.exceptionDetails)throw Error(t.exceptionDetails.exception?.description||t.exceptionDetails.text);return t.result?.value}disconnect(){this.markClosed();try{this.socket.close()}catch{}}}async function O(e,t={}){let r=new WebSocket(e);return await new Promise((o,n)=>{let i=(u)=>{if(clearTimeout(d),r.removeEventListener("open",a),r.removeEventListener("error",s),r.removeEventListener("close",s),u){try{r.close()}catch{}n(u)}else o()},a=()=>i(),s=()=>i(Error(`Could not connect to Chrome at ${e}`)),d=setTimeout(()=>i(Error(`Timed out connecting to Chrome at ${e}`)),t.connectTimeoutMs??30000);r.addEventListener("open",a,{once:!0}),r.addEventListener("error",s,{once:!0}),r.addEventListener("close",s,{once:!0})}),new J(r,t)}var z=["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome","/Applications/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing","/Applications/Chromium.app/Contents/MacOS/Chromium","/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary"],ae=["google-chrome","google-chrome-stable","chromium","chromium-browser","chrome"],Y=["KSProductID","KSUpdateURL","KSChannelID","KSVersion"],N="dev.stacks.testing.chrome";function ce(){if(P.env.CHROME_PATH)return P.env.CHROME_PATH;if(P.platform==="darwin"){let e=z.find((t)=>S(t));if(e)return e}for(let e of ae){let t=Bun.which(e);if(t)return t}return null}function Q(e){let t=/^(.*?\.app)\/Contents\/MacOS\/[^/]+$/.exec(e);return t?t[1]:null}function T(e,t){let r=Bun.spawnSync(["/usr/libexec/PlistBuddy","-c",t,e],{stdout:"pipe",stderr:"pipe"});return{ok:r.exitCode===0,out:r.stdout.toString().trim()}}function le(e){let t=Q(e);if(!t||P.platform!=="darwin")return!1;let r=y(t,"Contents/Info.plist");return S(r)&&Y.some((o)=>T(r,`Print :${o}`).ok)}function ue(e={}){if(P.platform!=="darwin")throw Error("prepareMacChrome is for macOS; elsewhere launch Chrome as installed");let t=e.source??z[0],r=Q(t);if(!r||!S(r))throw Error(`No Chrome app bundle at ${t}`);let o=y(r,"Contents/Info.plist"),n=T(o,"Print :CFBundleShortVersionString").out||"unknown",i=T(o,"Print :CFBundleExecutable").out||"Google Chrome",a=e.directory??y(V(),`stacks-chrome-${n.replace(/[^\w.-]/g,"_")}`),s=y(a,"Chrome.app"),d=y(s,"Contents/MacOS",i),u=y(s,"Contents/Info.plist");if(S(d)&&T(u,"Print :CFBundleIdentifier").out===N&&T(u,"Print :CFBundleShortVersionString").out===n)return{executable:d,app:s,reused:!0};te(a,{recursive:!0});let h=W(y(a,"staging-")),w=y(h,"Chrome.app"),l=(f,v)=>{let p=Bun.spawnSync(f,{stdout:"pipe",stderr:"pipe"});if(p.exitCode!==0)throw Error(`${v} failed: ${p.stderr.toString().trim()}`)};try{l(["cp","-cR",r,w],"Cloning Chrome");let f=y(w,"Contents/Info.plist");for(let v of Y)T(f,`Delete :${v}`);l(["/usr/libexec/PlistBuddy","-c",`Set :CFBundleIdentifier ${N}`,f],"Renaming the Chrome bundle"),l(["codesign","--force","--deep","--sign","-",w],"Re-signing Chrome"),x(s,{recursive:!0,force:!0}),oe(w,s)}finally{x(h,{recursive:!0,force:!0})}return{executable:d,app:s,reused:!1}}class I extends Error{diagnostics;constructor(e,t){super(`${e instanceof Error?e.message:String(e)}
3
+ Chrome startup: ${JSON.stringify(t)}`,{cause:e});this.diagnostics=t;this.name="ChromeLaunchError"}}async function q(e,t){let r;try{return await Promise.race([e.then(()=>!0),new Promise((o)=>{r=setTimeout(()=>o(!1),t)})])}finally{clearTimeout(r)}}function A(e){let t;try{t=re(e,"r");let r=Buffer.alloc(1024);return r.subarray(0,ne(t,r,0,r.length,0)).toString("utf8")}catch{return null}finally{if(t!==void 0)try{ee(t)}catch{}}}var de=["*/_stx/hmr*"];async function he(e,t=de){if(!t.length)return async()=>{};return await e.intercept(t.map((r)=>({urlPattern:r})),(r,o)=>{o.send("Fetch.fulfillRequest",{requestId:r.requestId,responseCode:200,responseHeaders:[{name:"Content-Type",value:"text/event-stream"}],body:Buffer.from(`retry: 86400000
3
4
 
4
- `).toString("base64")}).catch(()=>{})})}function Y(e){let t=e.chromePath??b.env.CHROME_PATH;if(t)return t;let r=W();if(!r)throw Error("No Chrome found: install Google Chrome or Chromium, or set CHROME_PATH");if((e.stableMacChrome??!0)&&b.platform==="darwin"&&G(r))return K({source:r}).executable;return r}async function Q(e={}){let t=Y(e);if(!C(t))throw Error(`Chrome was not found at ${t}`);let r=e.profile||M(m(L(),"stacks-browser-"));R(m(r,"DevToolsActivePort"),{force:!0});let n=Bun.spawn([t,...e.headless===!1?[]:["--headless=new"],"--remote-debugging-port=0",`--user-data-dir=${r}`,"--no-first-run","--no-default-browser-check","--disable-background-networking","--disable-component-update","--disable-extensions","--hide-scrollbars","--use-mock-keychain",...e.args??[],"about:blank"],{stdout:"ignore",stderr:"ignore"}),s,i=!1,a=async()=>{if(i)return;if(i=!0,s?.disconnect(),n.kill(),await n.exited,!e.profile)R(r,{recursive:!0,force:!0})};try{let c=m(r,"DevToolsActivePort");await S(()=>C(c)&&q(c,"utf8").includes(`
5
- `),e.startTimeoutMs??30000,{label:"Chrome to start"});let p=Number(q(c,"utf8").split(`
6
- `)[0]),h=await S(async()=>{let o=await fetch(`http://127.0.0.1:${p}/json/list`).then((f)=>f.json()).catch(()=>[]);return o.length?o:null},12000,{label:"a page target"}),g=h.find((o)=>o.type==="page"&&o.url==="about:blank")||h.find((o)=>o.type==="page");if(!g)throw Error("Chrome did not expose a page target");s=await j(g.webSocketDebuggerUrl,e);let l=s;if(await Promise.all([l.send("Page.enable"),l.send("Runtime.enable"),l.send("Network.enable"),l.send("Log.enable"),...e.viewport===!1?[]:[l.send("Emulation.setDeviceMetricsOverride",{deviceScaleFactor:1,mobile:!1,width:1440,height:1000,...e.viewport})]]),e.userAgent!==!1){let o=typeof e.userAgent==="string"?e.userAgent:String((await l.send("Browser.getVersion")).userAgent||"").replace("HeadlessChrome","Chrome");if(o)await l.send("Network.setUserAgentOverride",{userAgent:o})}if(e.quietLiveReload!==!1)await J(l,e.quietLiveReload);if(e.acceptDialogs!==!1)l.on("Page.javascriptDialogOpening",(o)=>{l.errors.push(`Dialog (${o.type}) on ${o.url}: ${o.message||"(no message)"}`),l.send("Page.handleJavaScriptDialog",{accept:!0}).catch(()=>{})});return{cdp:l,close:a,target:g.webSocketDebuggerUrl,port:p,chromePath:t}}catch(c){throw await a(),c}}async function X(e,t={}){let r=t.quietMs??1000,n=t.timeoutMs??15000,s=Date.now(),i=0;while(Date.now()-s<n){if(e.closed)throw Error("Chrome closed the connection");if(e.inFlight.size>0||await e.evaluate("document.readyState !== 'complete'").catch(()=>!0))i=0;else if(!i)i=Date.now();else if(Date.now()-i>=r)return!0;await Bun.sleep(100)}return!1}async function Z(e,t,r={}){let n=await e.send("Page.navigate",{url:t});if(n?.errorText)throw Error(`Could not open ${t}: ${n.errorText}`);return await X(e,r)}var ee=(e)=>e.pathname.startsWith("/api/")&&!e.pathname.endsWith("/events");function te(e){let t={};for(let r of e)t[r]=(t[r]||0)+1;return Object.fromEntries(Object.entries(t).filter(([,r])=>r>1))}function k(e){try{let t=new URL(e);return`${t.origin}${t.pathname}`}catch{return e.replace(/[?#].*$/,"")}}function re(e,t,r="navigation"){let n=[{url:e,apiRequests:[]}],s=!1;for(let i of t){let a=n.at(-1);if(i.type==="request"){a.apiRequests.push(i.request);continue}let c=r==="navigation"&&s&&(i.sameDocument?k(i.url)!==k(a.url):!0);if(s=!0,c)n.push({url:i.url,apiRequests:[]});else a.url=i.url}return n.map((i)=>({...i,duplicates:te(i.apiRequests)}))}function ne(e){let t={};for(let r of e)for(let[n,s]of Object.entries(r.duplicates))t[n]=Math.max(t[n]??0,s);return t}async function ce(e,t={}){let r=Date.now(),n=new URL(e).origin,s=t.isApiRequest??ee,i=[],a=[],c=[],p=null,h=t.cdp,g=[],l=h?.errors.length??0,o,f=e,y=async(d,x=[])=>{let u=re(e,a,t.pageLoads),w=f;if(h&&!h.closed){let v=await h.evaluate("location.href").catch(()=>null);if(v&&v!=="about:blank"&&!v.startsWith("chrome-error:"))w=v}return{url:e,finalUrl:w,apiRequests:i,duplicates:ne(u),loads:u,failed:c,errors:[...h?.errors.slice(l)??[],...x],settled:d,durationMs:Date.now()-r}};try{if(!h)p=await Q(t.launch),h=p.cdp;let d=h;if(g.push(d.on("Network.requestWillBeSent",(u)=>{let w=new URL(u.request.url);if(w.origin===n&&s(w,u.request.method)){let v=`${u.request.method} ${w.pathname}${w.search}`;i.push(v),a.push({type:"request",request:v})}})),g.push(d.on("Network.responseReceived",(u)=>{let w=new URL(u.response.url);if(w.origin===n&&u.response.status>=400)c.push(`${u.response.status} ${w.pathname}`)})),g.push(d.on("Page.frameNavigated",(u)=>{if(u.frame?.parentId)return;o=u.frame?.id??o,f=u.frame?.unreachableUrl||`${u.frame?.url??f}${u.frame?.urlFragment??""}`,a.push({type:"navigated",url:f,sameDocument:!1})})),g.push(d.on("Page.navigatedWithinDocument",(u)=>{if(o&&u.frameId!==o)return;f=u.url,a.push({type:"navigated",url:u.url,sameDocument:!0})})),await d.send("Page.enable"),o=(await d.send("Page.getFrameTree").catch(()=>null))?.frameTree?.frame?.id,t.viewport)await d.send("Emulation.setDeviceMetricsOverride",{deviceScaleFactor:1,mobile:!1,...t.viewport});await t.beforeNavigate?.(d);let x=await Z(d,e,t);return await y(x)}catch(d){return await y(!1,[`audit: ${d?.message??String(d)}`])}finally{for(let d of g)d();await p?.close().catch(()=>{})}}function A(e){try{let t=new URL(e);return`${t.pathname}${t.search}`}catch{return e}}function ue(e){let t=A(e.url),r=e.finalUrl?A(e.finalUrl):t,n=Object.entries(e.duplicates).map(([s,i])=>`${i}x ${s}`);return`${t}${r!==t?` -> ${r}`:""}: ${e.apiRequests.length} api${n.length?` | dup ${n.join(", ")}`:""}${e.failed.length?` | failed ${e.failed.join(", ")}`:""}${e.errors.length?` | errors ${e.errors.slice(0,3).join(" ; ")}`:""}${e.settled?"":" | never settled"}`}function le(e){return e.settled&&!Object.keys(e.duplicates).length&&!e.failed.length&&!e.errors.length}export{O as Cdp,z as LIVE_RELOAD_PATTERNS,ce as auditPage,G as chromeHasUpdater,j as connect,te as duplicateRequests,W as findChrome,ue as formatPageAudit,Q as launch,ne as loadDuplicates,H as matchesUrlPattern,Z as navigate,le as pageAuditClean,F as pausedRequestMatches,K as prepareMacChrome,J as quietLiveReload,X as settle,re as splitPageLoads,S as waitFor};
5
+ `).toString("base64")}).catch(()=>{})})}function pe(e){let t=e.chromePath??P.env.CHROME_PATH;if(t)return t;let r=ce();if(!r)throw Error("No Chrome found: install Google Chrome or Chromium, or set CHROME_PATH");if((e.stableMacChrome??!0)&&P.platform==="darwin"&&le(r))return ue({source:r}).executable;return r}async function X(e,t,r,o){if(!S(t))throw Error(`Chrome was not found at ${t}`);let n=e.profile||W(y(V(),"stacks-browser-"));x(y(n,"DevToolsActivePort"),{force:!0});let i=y(n,"DevToolsActivePort"),a=(()=>{try{return Bun.spawn([t,...e.headless===!1?[]:["--headless=new"],"--remote-debugging-port=0",`--user-data-dir=${n}`,"--no-first-run","--no-default-browser-check","--disable-background-networking","--disable-component-update","--disable-extensions","--hide-scrollbars","--use-mock-keychain",...e.args??[],"about:blank"],{stdin:"ignore",stdout:"ignore",stderr:"pipe"})}catch(g){let c=null;try{if(!e.profile)x(n,{recursive:!0,force:!0})}catch(m){c=String(m)}throw new I(g,{pid:null,chromePath:t,profile:n,temporaryProfile:!e.profile,elapsedMs:Date.now()-r,exitCodeBeforeCleanup:null,signalCodeBeforeCleanup:null,exitCode:null,signalCode:null,portFileExisted:!1,portFileContents:null,stderrTail:"",stderrTruncated:!1,cleanupError:c})}})(),s="",d=!1,u,h=Promise.resolve(),w,l=()=>w??=(async()=>{let g=!1,c=[];try{if(a.exitCode===null&&a.signalCode===null)try{a.kill("SIGTERM")}catch{}if(!await q(a.exited,2000)){try{a.kill("SIGKILL")}catch{}if(!await q(a.exited,2000))throw Error(`Chrome process ${a.pid} did not exit after SIGKILL`)}g=!0}catch(m){c.push(String(m)),g=a.exitCode!==null||a.signalCode!==null}finally{try{if(!await q(h,100)&&u){if(!await q(u.cancel(),100))throw Error("Chrome stderr cancellation did not finish")}if(!await q(h,100))throw Error("Chrome stderr reader did not finish")}catch(m){c.push(String(m))}finally{if(g&&!e.profile)try{x(n,{recursive:!0,force:!0})}catch(m){c.push(String(m))}}}if(c.length)throw Error(`Chrome cleanup failed: ${c.join("; ")}`)})(),f=()=>a.exitCode!==null||a.signalCode!==null,v=()=>{let g=o-(Date.now()-r);if(g<=0)throw Error(`Timed out after ${o}ms starting Chrome`);if(f())throw Error("Chrome exited during startup");return g},p=async(g,c)=>{let m=Date.now()-r,{exitCode:b,signalCode:R}=a,M=S(i),L=A(i),E=null;if(c)try{await l()}catch(D){E=D instanceof Error?D.message:String(D)}return new I(g,{pid:a.pid,chromePath:t,profile:n,temporaryProfile:!e.profile,elapsedMs:m,exitCodeBeforeCleanup:b,signalCodeBeforeCleanup:R,exitCode:a.exitCode,signalCode:a.signalCode,portFileExisted:M,portFileContents:L,stderrTail:s,stderrTruncated:d,cleanupError:E})};try{u=a.stderr.getReader();let g=u;h=(async()=>{let R=new TextDecoder;for(;;){let{value:M,done:L}=await g.read(),E=R.decode(M,{stream:!L});if(d||=s.length+E.length>16384,s=(s+E).slice(-16384),L)return}})().catch((R)=>{s=`${s}
6
+ [stderr read failed: ${String(R)}]`.slice(-16384)}),await K(()=>(v(),A(i)?.includes(`
7
+ `)),v(),{label:"Chrome to start"});let[c,m=""]=(A(i)??"").split(`
8
+ `),b=Number(c);if(!Number.isInteger(b)||b<1||b>65535)throw Error("Chrome wrote an invalid DevToolsActivePort");return{pid:a.pid,chromePath:t,profile:n,port:b,browserPath:m.trim(),remaining:v,close:l,failure:p,exited:f}}catch(g){throw await p(g,!0)}}async function k(e,t,r){let o;try{return await Promise.race([e,new Promise((n,i)=>{o=setTimeout(()=>i(Error(`Timed out after ${t}ms ${r}`)),t)})])}finally{clearTimeout(o)}}async function Z(e,t){if(await Promise.all([e.send("Page.enable"),e.send("Runtime.enable"),e.send("Network.enable"),e.send("Log.enable"),...t.viewport===!1?[]:[e.send("Emulation.setDeviceMetricsOverride",{deviceScaleFactor:1,mobile:!1,width:1440,height:1000,...t.viewport})]]),t.userAgent!==!1){let r=typeof t.userAgent==="string"?t.userAgent:String((await e.send("Browser.getVersion")).userAgent||"").replace("HeadlessChrome","Chrome");if(r)await e.send("Network.setUserAgentOverride",{userAgent:r})}if(t.quietLiveReload!==!1)await he(e,t.quietLiveReload);if(t.acceptDialogs!==!1)e.on("Page.javascriptDialogOpening",(r)=>{e.errors.push(`Dialog (${r.type}) on ${r.url}: ${r.message||"(no message)"}`),e.send("Page.handleJavaScriptDialog",{accept:!0}).catch(()=>{})})}var me=1500,C=new Map,F=new Map,U=!1;function fe(){if(U)return;U=!0,P.once("exit",()=>{for(let[e,t]of F){try{P.kill(e,"SIGKILL")}catch{}try{if(t)x(t,{recursive:!0,force:!0})}catch{}}})}async function Fe(){let e=[...C.values()];C.clear(),await Promise.allSettled(e.map(async(t)=>{let r=await t;if(clearTimeout(r.linger),r.browser.disconnect(),await r.chrome.close(),r.chrome.pid!==null)F.delete(r.chrome.pid)}))}async function ge(e,t,r,o,n){let i=C.get(e);if(i){let s=await i.catch(()=>null);if(s&&!s.browser.closed&&!s.chrome.exited())return clearTimeout(s.linger),s.sessions++,s;if(C.get(e)===i)C.delete(e)}let a=(async()=>{let s=await X(t,r,o,n);if(s.pid!==null)F.set(s.pid,s.profile);try{if(!s.browserPath.startsWith("/devtools/browser/"))throw Error("Chrome wrote no browser endpoint in DevToolsActivePort");let d=await O(`ws://127.0.0.1:${s.port}${s.browserPath}`,{...t,connectTimeoutMs:Math.min(t.connectTimeoutMs??30000,s.remaining())});return{chrome:s,browser:d,sessions:0}}catch(d){if(s.pid!==null)F.delete(s.pid);throw await s.failure(d,!0)}})();C.set(e,a),fe();try{let s=await a;return s.sessions++,s}catch(s){if(C.get(e)===a)C.delete(e);throw s}}function H(e,t,r=!1){if(t.sessions=Math.max(0,t.sessions-1),t.sessions>0)return Promise.resolve();let o=async()=>{if(t.sessions>0)return;let n=C.get(e);if(n&&await n.catch(()=>null)===t)C.delete(e);if(t.browser.disconnect(),await t.chrome.close(),t.chrome.pid!==null)F.delete(t.chrome.pid)};if(r)return o();return clearTimeout(t.linger),t.linger=setTimeout(()=>{o().catch(()=>{})},me),Promise.resolve()}async function we(e,t,r,o){let n=JSON.stringify([t,e.headless!==!1,e.args??[]]),i=await ge(n,e,t,r,o),a=()=>{let h=o-(Date.now()-r);if(h<=0)throw Error(`Timed out after ${o}ms starting Chrome`);return h},s,d,u=async()=>{if(s&&!i.browser.closed)await i.browser.send("Target.disposeBrowserContext",{browserContextId:s}).catch(()=>{})};try{s=(await k(i.browser.send("Target.createBrowserContext",{disposeOnDetach:!1}),a(),"creating a browser context")).browserContextId;let{targetId:h}=await k(i.browser.send("Target.createTarget",{url:"about:blank",browserContextId:s}),a(),"opening a page"),w=`ws://127.0.0.1:${i.chrome.port}/devtools/page/${h}`,l=d=await O(w,{...e,connectTimeoutMs:Math.min(e.connectTimeoutMs??30000,a())});await k(Z(l,e),a(),"preparing the page"),a();let f;return{cdp:l,close:()=>f??=(async()=>{l.disconnect(),await u(),await H(n,i)})(),target:w,port:i.chrome.port,chromePath:t}}catch(h){d?.disconnect(),await u();let w=i.sessions<=1,l=await i.chrome.failure(h,!1);throw await H(n,i,w).catch((f)=>{l.diagnostics.cleanupError=f instanceof Error?f.message:String(f)}),l}}async function be(e,t,r,o){let n=await X(e,t,r,o),i,a,s=()=>a??=(async()=>{i?.disconnect(),await n.close()})(),d=setTimeout(()=>i?.disconnect(),Math.max(1,o-(Date.now()-r)));try{let u=n.port,h=await K(async()=>{let l=await fetch(`http://127.0.0.1:${u}/json/list`,{signal:AbortSignal.timeout(Math.min(1000,n.remaining()))}).then((f)=>f.json()).catch(()=>[]);return Array.isArray(l)&&l.length?l:null},Math.min(12000,n.remaining()),{label:"a page target"}),w=h.find((l)=>l.type==="page"&&l.url==="about:blank")||h.find((l)=>l.type==="page");if(!w)throw Error("Chrome did not expose a page target");return i=await O(w.webSocketDebuggerUrl,{...e,connectTimeoutMs:Math.min(e.connectTimeoutMs??30000,n.remaining())}),n.remaining(),await Z(i,e),n.remaining(),clearTimeout(d),{cdp:i,close:s,target:w.webSocketDebuggerUrl,port:u,chromePath:t}}catch(u){throw clearTimeout(d),i?.disconnect(),await n.failure(u,!0)}}async function ye(e={}){let t=e.startTimeoutMs??30000;if(!Number.isFinite(t)||t<=0)throw RangeError("startTimeoutMs must be positive and finite");let r=pe(e),o=Date.now();return!e.profile&&e.shared!==!1&&P.env.STACKS_BROWSER_SHARED!=="0"?await we(e,r,o,t):await be(e,r,o,t)}async function ve(e,t={}){let r=t.quietMs??1000,o=t.timeoutMs??15000,n=Date.now(),i=0;while(Date.now()-n<o){if(e.closed)throw Error("Chrome closed the connection");if(e.inFlight.size>0||await e.evaluate("document.readyState !== 'complete'").catch(()=>!0))i=0;else if(!i)i=Date.now();else if(Date.now()-i>=r)return!0;await Bun.sleep(100)}return!1}async function Ce(e,t,r={}){let o=await e.send("Page.navigate",{url:t});if(o?.errorText)throw Error(`Could not open ${t}: ${o.errorText}`);return await ve(e,r)}var Pe=(e)=>e.pathname.startsWith("/api/")&&!e.pathname.endsWith("/events");function Te(e){let t={};for(let r of e)t[r]=(t[r]||0)+1;return Object.fromEntries(Object.entries(t).filter(([,r])=>r>1))}function j(e){try{let t=new URL(e);return`${t.origin}${t.pathname}`}catch{return e.replace(/[?#].*$/,"")}}function xe(e,t,r="navigation"){let o=[{url:e,apiRequests:[]}],n=!1;for(let i of t){let a=o.at(-1);if(i.type==="request"){a.apiRequests.push(i.request);continue}let s=r==="navigation"&&n&&(i.sameDocument?j(i.url)!==j(a.url):!0);if(n=!0,s)o.push({url:i.url,apiRequests:[]});else a.url=i.url}return o.map((i)=>({...i,duplicates:Te(i.apiRequests)}))}function Se(e){let t={};for(let r of e)for(let[o,n]of Object.entries(r.duplicates))t[o]=Math.max(t[o]??0,n);return t}async function Le(e,t={}){let r=Date.now(),o=new URL(e).origin,n=t.isApiRequest??Pe,i=[],a=[],s=[],d=null,u=t.cdp,h=[],w=u?.errors.length??0,l,f=e,v=async(p,g=[])=>{let c=xe(e,a,t.pageLoads),m=f;if(u&&!u.closed){let b=await u.evaluate("location.href").catch(()=>null);if(b&&b!=="about:blank"&&!b.startsWith("chrome-error:"))m=b}return{url:e,finalUrl:m,apiRequests:i,duplicates:Se(c),loads:c,failed:s,errors:[...u?.errors.slice(w)??[],...g],settled:p,durationMs:Date.now()-r}};try{if(!u)d=await ye(t.launch),u=d.cdp;let p=u;if(h.push(p.on("Network.requestWillBeSent",(c)=>{let m=new URL(c.request.url);if(m.origin===o&&n(m,c.request.method)){let b=`${c.request.method} ${m.pathname}${m.search}`;i.push(b),a.push({type:"request",request:b})}})),h.push(p.on("Network.responseReceived",(c)=>{let m=new URL(c.response.url);if(m.origin===o&&c.response.status>=400)s.push(`${c.response.status} ${m.pathname}`)})),h.push(p.on("Page.frameNavigated",(c)=>{if(c.frame?.parentId)return;l=c.frame?.id??l,f=c.frame?.unreachableUrl||`${c.frame?.url??f}${c.frame?.urlFragment??""}`,a.push({type:"navigated",url:f,sameDocument:!1})})),h.push(p.on("Page.navigatedWithinDocument",(c)=>{if(l&&c.frameId!==l)return;f=c.url,a.push({type:"navigated",url:c.url,sameDocument:!0})})),await p.send("Page.enable"),l=(await p.send("Page.getFrameTree").catch(()=>null))?.frameTree?.frame?.id,t.viewport)await p.send("Emulation.setDeviceMetricsOverride",{deviceScaleFactor:1,mobile:!1,...t.viewport});await t.beforeNavigate?.(p);let g=await Ce(p,e,t);return await v(g)}catch(p){return await v(!1,[`audit: ${p?.message??String(p)}`])}finally{for(let p of h)p();await d?.close().catch(()=>{})}}function G(e){try{let t=new URL(e);return`${t.pathname}${t.search}`}catch{return e}}function Me(e){let t=G(e.url),r=e.finalUrl?G(e.finalUrl):t,o=Object.entries(e.duplicates).map(([n,i])=>`${i}x ${n}`);return`${t}${r!==t?` -> ${r}`:""}: ${e.apiRequests.length} api${o.length?` | dup ${o.join(", ")}`:""}${e.failed.length?` | failed ${e.failed.join(", ")}`:""}${e.errors.length?` | errors ${e.errors.slice(0,3).join(" ; ")}`:""}${e.settled?"":" | never settled"}`}function De(e){return e.settled&&!Object.keys(e.duplicates).length&&!e.failed.length&&!e.errors.length}export{J as Cdp,I as ChromeLaunchError,de as LIVE_RELOAD_PATTERNS,me as SHARED_CHROME_LINGER_MS,Le as auditPage,le as chromeHasUpdater,Fe as closeSharedBrowsers,O as connect,Te as duplicateRequests,ce as findChrome,Me as formatPageAudit,ye as launch,Se as loadDuplicates,se as matchesUrlPattern,Ce as navigate,De as pageAuditClean,B as pausedRequestMatches,ue as prepareMacChrome,he as quietLiveReload,ve as settle,xe as splitPageLoads,K as waitFor};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/testing",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.233",
5
+ "version": "0.75.237",
6
6
  "description": "The Stacks way of testing.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -79,16 +79,16 @@
79
79
  "prepublishOnly": "bun run build"
80
80
  },
81
81
  "dependencies": {
82
- "@stacksjs/auth": "0.75.233",
83
- "@stacksjs/cache": "0.75.233",
84
- "@stacksjs/config": "0.75.233",
85
- "@stacksjs/database": "0.75.233",
86
- "@stacksjs/email": "0.75.233",
87
- "@stacksjs/events": "0.75.233",
88
- "@stacksjs/path": "0.75.233",
89
- "@stacksjs/router": "0.75.233",
90
- "@stacksjs/storage": "0.75.233",
91
- "@stacksjs/strings": "0.75.233",
82
+ "@stacksjs/auth": "0.75.237",
83
+ "@stacksjs/cache": "0.75.237",
84
+ "@stacksjs/config": "0.75.237",
85
+ "@stacksjs/database": "0.75.237",
86
+ "@stacksjs/email": "0.75.237",
87
+ "@stacksjs/events": "0.75.237",
88
+ "@stacksjs/path": "0.75.237",
89
+ "@stacksjs/router": "0.75.237",
90
+ "@stacksjs/storage": "0.75.237",
91
+ "@stacksjs/strings": "0.75.237",
92
92
  "@stacksjs/ts-cloud": "^0.16.58",
93
93
  "very-happy-dom": "^0.3.5"
94
94
  },