@lomray/vite-ssr-boost 8.4.0-beta.1 → 8.4.0-beta.3
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/README.md +2 -0
- package/adapters/express/entry.d.ts +4 -1
- package/adapters/express/entry.js +1 -1
- package/adapters/express/entry.js.map +1 -1
- package/adapters/express/render.d.ts +9 -2
- package/adapters/express/render.js +1 -1
- package/adapters/express/render.js.map +1 -1
- package/adapters/express/server.js +1 -1
- package/adapters/express/server.js.map +1 -1
- package/browser/entry.js +1 -1
- package/browser/entry.js.map +1 -1
- package/core/compose-html.d.ts +2 -1
- package/core/compose-html.js +1 -1
- package/core/compose-html.js.map +1 -1
- package/core/data-stream.d.ts +3 -1
- package/core/data-stream.js +1 -1
- package/core/data-stream.js.map +1 -1
- package/core/handler.d.ts +12 -3
- package/core/handler.js +1 -1
- package/core/handler.js.map +1 -1
- package/core/render.d.ts +11 -2
- package/core/render.js +1 -1
- package/core/render.js.map +1 -1
- package/core/spa-html.d.ts +6 -0
- package/core/spa-html.js +2 -0
- package/core/spa-html.js.map +1 -0
- package/core/spa-shell.d.ts +7 -0
- package/core/spa-shell.js +2 -0
- package/core/spa-shell.js.map +1 -0
- package/core/ssr-policy.d.ts +51 -0
- package/core/ssr-policy.js +2 -0
- package/core/ssr-policy.js.map +1 -0
- package/package.json +31 -1
- package/plugins/create-spa-index.js +1 -1
- package/plugins/create-spa-index.js.map +1 -1
- package/services/diagnostics.d.ts +11 -3
- package/services/diagnostics.js +1 -1
- package/services/diagnostics.js.map +1 -1
- package/services/request-timeline.d.ts +30 -0
- package/services/request-timeline.js +2 -0
- package/services/request-timeline.js.map +1 -0
- package/services/route-assets.d.ts +4 -2
- package/services/route-assets.js +1 -1
- package/services/route-assets.js.map +1 -1
- package/services/ssr-manifest.d.ts +7 -3
- package/services/ssr-manifest.js +1 -1
- package/services/ssr-manifest.js.map +1 -1
- package/testing/browser-observer.d.ts +30 -0
- package/testing/browser-observer.js +2 -0
- package/testing/browser-observer.js.map +1 -0
- package/testing/create-handler.d.ts +32 -0
- package/testing/create-handler.js +2 -0
- package/testing/create-handler.js.map +1 -0
- package/testing/deferred.d.ts +9 -0
- package/testing/deferred.js +2 -0
- package/testing/deferred.js.map +1 -0
- package/testing/edge.d.ts +9 -0
- package/testing/edge.js +2 -0
- package/testing/edge.js.map +1 -0
- package/testing/parse-response.d.ts +12 -0
- package/testing/parse-response.js +2 -0
- package/testing/parse-response.js.map +1 -0
- package/testing/playwright.d.ts +17 -0
- package/testing/playwright.js +2 -0
- package/testing/playwright.js.map +1 -0
- package/testing/requests.d.ts +6 -0
- package/testing/requests.js +2 -0
- package/testing/requests.js.map +1 -0
- package/testing/response.d.ts +46 -0
- package/testing/response.js +2 -0
- package/testing/response.js.map +1 -0
- package/testing.d.ts +9 -0
- package/testing.js +2 -0
- package/testing.js.map +1 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface IDeferred<T> {
|
|
2
|
+
promise: Promise<T>;
|
|
3
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
4
|
+
reject: (reason?: unknown) => void;
|
|
5
|
+
}
|
|
6
|
+
/** Control loader timing without sleeps or a running HTTP server. */
|
|
7
|
+
declare const createDeferred: <T>() => IDeferred<T>;
|
|
8
|
+
export type { IDeferred };
|
|
9
|
+
export default createDeferred;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deferred.js","sources":["../../src/testing/deferred.ts"],"sourcesContent":[null],"names":["createDeferred","resolve","reject","promise","Promise","res","rej","catch"],"mappings":"AAOA,MAAMA,EAAiB,KACrB,IAAIC,EACAC,EACJ,MAAMC,EAAU,IAAIC,QAAW,CAACC,EAAKC,KACnCL,EAAUI,EACVH,EAASI,IAMX,OAFKH,EAAQI,MAAM,QAEZ,CAAEJ,UAASF,UAASC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare const createTestHandler: <TAppProps = Record<string, any>>({ routes, App, shell, routerOptions, renderToStream, signal: defaultSignal, timeout: defaultTimeout, onContext, onRouterReady, ...options }: import("./create-handler.js").ITestHandlerOptions<TAppProps>) => import("./create-handler.js").ITestHandler;
|
|
2
|
+
export { createTestHandler };
|
|
3
|
+
export { default as createDeferred } from './deferred.js';
|
|
4
|
+
export { browserRequest, crawlerRequest } from './requests.js';
|
|
5
|
+
export { default as TestResponse } from './response.js';
|
|
6
|
+
export type { ITestHandler, ITestHandlerOptions, ITestRequestInit } from './create-handler.js';
|
|
7
|
+
export type { ITestChunk, ITestCookie, ITestResponseOptions } from './response.js';
|
|
8
|
+
export type { IRouterState, TStreamFrame } from './parse-response.js';
|
|
9
|
+
export type { ITimelineEvent, TRequestStage } from '../services/request-timeline.js';
|
package/testing/edge.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"../edge/render-to-stream.js";import r from"./create-handler.js";export{default as createDeferred}from"./deferred.js";export{browserRequest,crawlerRequest}from"./requests.js";export{default as TestResponse}from"./response.js";const s=r(e,()=>{throw new Error("shell.indexFile requires Node. In edge tests pass shell: { header, footer }.")});export{s as createTestHandler};
|
|
2
|
+
//# sourceMappingURL=edge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge.js","sources":["../../src/testing/edge.ts"],"sourcesContent":[null],"names":["createTestHandler","testHandlerFactory","renderToStream","Error"],"mappings":"+OAGA,MAAMA,EAAoBC,EAAmBC,EAAgB,KAC3D,MAAM,IAAIC,MAAM"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type TStreamFrame = ['init', string, boolean] | ['resolve' | 'reject', number, string] | ['shell'];
|
|
2
|
+
interface IRouterState {
|
|
3
|
+
loaderData: Record<string, any>;
|
|
4
|
+
actionData: Record<string, any> | null;
|
|
5
|
+
errors: Record<string, any> | null;
|
|
6
|
+
}
|
|
7
|
+
/** Frames contain only primitive tuple fields; encoded values are JSON strings. */
|
|
8
|
+
declare const streamFrames: (html: string) => TStreamFrame[];
|
|
9
|
+
/** Resolve nested identities and preserve native collections, holes and own keys. */
|
|
10
|
+
declare const routerState: (html: string) => IRouterState | undefined;
|
|
11
|
+
export { routerState, streamFrames };
|
|
12
|
+
export type { IRouterState, TStreamFrame };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{parse as e}from"devalue";import{parse as t}from"parse5";const r=e=>{const r=[],o=e=>{"tagName"in e&&"script"===e.tagName?r.push(e.childNodes.map(e=>"value"in e?e.value:"").join("")):"childNodes"in e&&e.childNodes.forEach(o)};return o(t(e)),r},o=e=>{const t=[];for(const o of r(e)){const e=/\(window\.__ssrBoostStream\s*=\s*window\.__ssrBoostStream\s*\|\|\s*\[\]\)\.push\((\[(?:"(?:\\.|[^"\\])*"|[^\]"\\])*\])\)/g;for(const r of o.matchAll(e)){const e=JSON.parse(r[1]);if(!Array.isArray(e)||!("shell"===e[0]&&1===e.length||"init"===e[0]&&3===e.length&&"string"==typeof e[1]&&"boolean"==typeof e[2]||["resolve","reject"].includes(e[0])&&3===e.length&&Number.isInteger(e[1])&&"string"==typeof e[2]))throw new Error("Invalid SSR Boost stream frame.");t.push(e)}}return t};class s{id;constructor(e){this.id=e}}const n=t=>e(t,{SSRBPromise:([e])=>new s(e),SSRBObject:e=>Object.fromEntries(e),SSRBError:e=>{const t=Object.fromEntries(e),r=Object.assign(new Error("Streamed loader/action rejection"),t);return t.stack||delete r.stack,r},SSRBUndefined:()=>{}}),a=e=>{const t=o(e),a=t.filter(e=>"init"===e[0]);if(!a.length){for(const t of r(e)){const e=t.match(/window\.__staticRouterHydrationData\s*=\s*JSON\.parse\(("(?:\\.|[^"\\])*")\)/);if(e)return JSON.parse(JSON.parse(e[1]))}return}if(1!==a.length)throw new Error("Expected exactly one SSR Boost init frame.");const i=new Map,c=new Set;for(const e of t)if("resolve"===e[0]||"reject"===e[0]){if(i.has(e[1]))throw new Error(`Duplicate settlement for SSR promise ${e[1]}.`);i.set(e[1],n(e[2])),"reject"===e[0]&&c.add(e[1])}const f=new WeakSet,l=new Set,d=new Map,h=e=>{if(e instanceof s){const{id:t}=e;if(!i.has(t))throw new Error(`Missing settlement for SSR promise ${t}.`);if(c.has(t))throw i.get(t);if(l.has(t))throw new Error(`Circular SSR promise ${t}.`);return d.has(t)||(l.add(t),d.set(t,h(i.get(t))),l.delete(t)),d.get(t)}if(!e||"object"!=typeof e||f.has(e))return e;if(f.add(e),e instanceof Map){const t=[...e];e.clear(),t.forEach(([t,r])=>e.set(h(t),h(r)))}else if(e instanceof Set){const t=[...e];e.clear(),t.forEach(t=>e.add(h(t)))}else for(const t of Object.keys(e))Object.defineProperty(e,t,{configurable:!0,enumerable:!0,writable:!0,value:h(Reflect.get(e,t))});return e};return h(n(a[0][1]))};export{a as routerState,o as streamFrames};
|
|
2
|
+
//# sourceMappingURL=parse-response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-response.js","sources":["../../src/testing/parse-response.ts"],"sourcesContent":[null],"names":["scripts","html","result","visit","node","tagName","push","childNodes","map","child","value","join","forEach","parse","streamFrames","frames","source","pattern","match","matchAll","frame","JSON","Array","isArray","length","includes","Number","isInteger","Error","Placeholder","id","constructor","this","payload","decode","SSRBPromise","SSRBObject","entries","Object","fromEntries","SSRBError","properties","error","assign","stack","SSRBUndefined","routerState","initial","filter","legacy","settlements","Map","rejected","Set","has","set","add","visited","WeakSet","resolving","resolved","resolve","item","get","delete","clear","key","keys","defineProperty","configurable","enumerable","writable","Reflect"],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Page } from '@playwright/test';
|
|
2
|
+
import type { IBrowserTimelineEvent } from './browser-observer.js';
|
|
3
|
+
interface IHydratedOptions {
|
|
4
|
+
root?: string;
|
|
5
|
+
timeout?: number;
|
|
6
|
+
}
|
|
7
|
+
interface IStreamTimelineCollector {
|
|
8
|
+
read: () => Promise<IBrowserTimelineEvent[]>;
|
|
9
|
+
}
|
|
10
|
+
/** Install before navigation so console failures and the original SSR DOM cannot be missed. */
|
|
11
|
+
declare const collectStreamTimeline: (page: Page) => Promise<IStreamTimelineCollector>;
|
|
12
|
+
/** Wait for router creation, all streamed boundaries and a browser rendering opportunity. */
|
|
13
|
+
declare const expectHydrated: (page: Page, { root, timeout }?: IHydratedOptions) => Promise<void>;
|
|
14
|
+
/** Require an observed shell before this element first became visible. */
|
|
15
|
+
declare const expectStreamed: (page: Page, selector: string) => Promise<void>;
|
|
16
|
+
export { collectStreamTimeline, expectHydrated, expectStreamed };
|
|
17
|
+
export type { IBrowserTimelineEvent, IHydratedOptions, IStreamTimelineCollector };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{installBrowserObserver as e,inspectHydration as t}from"./browser-observer.js";const a=new WeakSet,o=async t=>(a.has(t)||(await t.addInitScript(e),a.add(t)),{read:()=>t.evaluate(()=>window.__ssrBoostTest?.events??[])}),s=async(e,{root:o="#root",timeout:s=5e3}={})=>{const{expect:r}=await(import("@playwright/test"));r(a.has(e),"Call collectStreamTimeline(page) before page.goto().").toBe(!0),await r.poll(async()=>(await e.evaluate(t,o)).ready,{timeout:s,message:`SSR router was not created for ${o}. Use the SSR Boost browser entry.`}).toBe(!0),await e.waitForLoadState("load",{timeout:s}),await r.poll(async()=>(await e.evaluate(t,o)).pending,{timeout:s,message:"Streamed Suspense boundaries did not settle."}).toBe(0),await e.evaluate(()=>new Promise(e=>{requestAnimationFrame(()=>requestAnimationFrame(()=>e()))}));const i=await e.evaluate(t,o);r(i.consumed,"Router hydration state was not consumed.").toBe(!0),r(i.errors,"React reported hydration errors.").toEqual([]),r(i.duplicates,"Server text was duplicated during hydration.").toEqual([])},r=async(e,t)=>{const{expect:o}=await(import("@playwright/test"));o(a.has(e),"Call collectStreamTimeline(page) before page.goto().").toBe(!0);const s=e.locator(t);await o(s).toBeVisible();const r=await s.evaluate(e=>({shell:window.__ssrBoostTest?.shellAt,element:window.__ssrBoostTest?.seen.get(e)}));o(r.shell,"No server shell was observed.").toBeDefined(),o(r.element,"The element was not observed during this navigation.").toBeDefined(),o(r.element,"The element must appear after the shell.").toBeGreaterThan(r.shell)};export{o as collectStreamTimeline,s as expectHydrated,r as expectStreamed};
|
|
2
|
+
//# sourceMappingURL=playwright.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright.js","sources":["../../src/testing/playwright.ts"],"sourcesContent":[null],"names":["installed","WeakSet","collectStreamTimeline","async","page","has","addInitScript","installBrowserObserver","add","read","evaluate","window","__ssrBoostTest","events","expectHydrated","root","timeout","expect","toBe","poll","inspectHydration","ready","message","waitForLoadState","pending","Promise","resolve","requestAnimationFrame","result","consumed","errors","toEqual","duplicates","expectStreamed","selector","element","locator","toBeVisible","timing","node","shell","shellAt","seen","get","toBeDefined","toBeGreaterThan"],"mappings":"qFAaA,MAAMA,EAAY,IAAIC,QAGhBC,EAAwBC,MAAOC,IAC9BJ,EAAUK,IAAID,WACXA,EAAKE,cAAcC,GACzBP,EAAUQ,IAAIJ,IAGT,CACLK,KAAM,IAAML,EAAKM,SAAS,IAAMC,OAAOC,gBAAgBC,QAAU,MAK/DC,EAAiBX,MACrBC,GACEW,OAAO,QAASC,UAAU,KAA4B,MAExD,MAAMC,OAAEA,gBAAwB,qBAEhCA,EAAOjB,EAAUK,IAAID,GAAO,wDAAwDc,MAAK,SACnFD,EACHE,KAAKhB,gBAAmBC,EAAKM,SAASU,EAAkBL,IAAOM,MAAO,CACrEL,UACAM,QAAS,kCAAkCP,wCAE5CG,MAAK,SACFd,EAAKmB,iBAAiB,OAAQ,CAAEP,kBAChCC,EACHE,KAAKhB,gBAAmBC,EAAKM,SAASU,EAAkBL,IAAOS,QAAS,CACvER,UACAM,QAAS,iDAEVJ,KAAK,SACFd,EAAKM,SACT,IACE,IAAIe,QAAeC,IACjBC,sBAAsB,IAAMA,sBAAsB,IAAMD,SAG9D,MAAME,QAAexB,EAAKM,SAASU,EAAkBL,GAErDE,EAAOW,EAAOC,SAAU,4CAA4CX,MAAK,GACzED,EAAOW,EAAOE,OAAQ,oCAAoCC,QAAQ,IAClEd,EAAOW,EAAOI,WAAY,gDAAgDD,QAAQ,KAI9EE,EAAiB9B,MAAOC,EAAY8B,KACxC,MAAMjB,OAAEA,gBAAwB,qBAEhCA,EAAOjB,EAAUK,IAAID,GAAO,wDAAwDc,MAAK,GACzF,MAAMiB,EAAU/B,EAAKgC,QAAQF,SAEvBjB,EAAOkB,GAASE,cACtB,MAAMC,QAAeH,EAAQzB,SAAU6B,IAAI,CACzCC,MAAO7B,OAAOC,gBAAgB6B,QAC9BN,QAASxB,OAAOC,gBAAgB8B,KAAKC,IAAIJ,MAG3CtB,EAAOqB,EAAOE,MAAO,iCAAiCI,cACtD3B,EAAOqB,EAAOH,QAAS,wDAAwDS,cAC/E3B,EAAOqB,EAAOH,QAAU,4CAA4CU,gBAClEP,EAAOE"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
declare const TEST_ORIGIN = "http://localhost";
|
|
2
|
+
/** Give relative test paths the same stable origin as createTestHandler.fetch. */
|
|
3
|
+
declare const browserRequest: (path: string) => Request;
|
|
4
|
+
/** Identification only: the application's onRouterReady hook chooses buffering. */
|
|
5
|
+
declare const crawlerRequest: (path: string) => Request;
|
|
6
|
+
export { browserRequest, crawlerRequest, TEST_ORIGIN };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e="http://localhost",t=t=>new Request(new URL(t,e),{headers:{"User-Agent":"Mozilla/5.0 (SSR Boost route test)"}}),o=t=>new Request(new URL(t,e),{headers:{"User-Agent":"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"}});export{e as TEST_ORIGIN,t as browserRequest,o as crawlerRequest};
|
|
2
|
+
//# sourceMappingURL=requests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requests.js","sources":["../../src/testing/requests.ts"],"sourcesContent":[null],"names":["TEST_ORIGIN","browserRequest","path","Request","URL","headers","crawlerRequest"],"mappings":"AAAA,MAAMA,EAAc,mBAGdC,EAAkBC,GACtB,IAAIC,QAAQ,IAAIC,IAAIF,EAAMF,GAAc,CACtCK,QAAS,CAAE,aAAc,wCAIvBC,EAAkBJ,GACtB,IAAIC,QAAQ,IAAIC,IAAIF,EAAMF,GAAc,CACtCK,QAAS,CACP,aAAc"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ITimelineEvent } from '../services/request-timeline.js';
|
|
2
|
+
import type RequestTimeline from '../services/request-timeline.js';
|
|
3
|
+
import type { IRouterState, TStreamFrame } from './parse-response.js';
|
|
4
|
+
interface ITestChunk {
|
|
5
|
+
text: string;
|
|
6
|
+
at: number;
|
|
7
|
+
}
|
|
8
|
+
interface ITestCookie {
|
|
9
|
+
name: string;
|
|
10
|
+
value: string;
|
|
11
|
+
/** Lowercase attribute names; flags are true, other values remain strings. */
|
|
12
|
+
attributes: Record<string, string | true>;
|
|
13
|
+
}
|
|
14
|
+
interface ITestResponseOptions {
|
|
15
|
+
started?: number;
|
|
16
|
+
timeline?: RequestTimeline;
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
onComplete?: () => void;
|
|
19
|
+
}
|
|
20
|
+
/** A single eager stream reader with repeatable, order-independent assertions. */
|
|
21
|
+
declare class TestResponse {
|
|
22
|
+
readonly response: Response;
|
|
23
|
+
protected readonly options: ITestResponseOptions;
|
|
24
|
+
protected readonly parts: ITestChunk[];
|
|
25
|
+
protected readonly completed: Promise<void>;
|
|
26
|
+
protected readonly started: number;
|
|
27
|
+
constructor(response: Response, options?: ITestResponseOptions);
|
|
28
|
+
get status(): number;
|
|
29
|
+
get headers(): Headers;
|
|
30
|
+
/** Decode incrementally so multibyte characters survive arbitrary transport cuts. */
|
|
31
|
+
protected read(): Promise<void>;
|
|
32
|
+
text(): Promise<string>;
|
|
33
|
+
/** The raw full document, including React boundary instructions and data scripts. */
|
|
34
|
+
html(): Promise<string>;
|
|
35
|
+
chunks(): Promise<ITestChunk[]>;
|
|
36
|
+
routerState(): Promise<IRouterState | undefined>;
|
|
37
|
+
/** Count raw markers: streamed HTML retains them even after replacement scripts arrive. */
|
|
38
|
+
pendingBoundaries(): Promise<number>;
|
|
39
|
+
streamFrames(): Promise<TStreamFrame[]>;
|
|
40
|
+
/** A failed/cancelled read still has a useful diagnostic timeline. */
|
|
41
|
+
timeline(): Promise<ITimelineEvent[]>;
|
|
42
|
+
/** Preserve separate cookies and commas in Expires, without running a browser cookie jar. */
|
|
43
|
+
cookies(): ITestCookie[];
|
|
44
|
+
}
|
|
45
|
+
export type { ITestChunk, ITestCookie, ITestResponseOptions };
|
|
46
|
+
export default TestResponse;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{routerState as t,streamFrames as e}from"./parse-response.js";class s{response;options;parts=[];completed;started;constructor(t,e={}){this.response=t,this.options=e,this.started=e.started??performance.now(),this.completed=this.read(),this.completed.catch(()=>{})}get status(){return this.response.status}get headers(){return this.response.headers}async read(){const t=this.response.body?.getReader(),e=new TextDecoder,{signal:s,onComplete:r}=this.options,a=()=>{t?.cancel(s?.reason).catch(()=>{})};s?.addEventListener("abort",a,{once:!0});try{if(s?.throwIfAborted(),t)for(;;){const r=await t.read();s?.throwIfAborted();const a=r.done?e.decode():e.decode(r.value,{stream:!0});if(a&&this.parts.push({text:a,at:performance.now()-this.started}),r.done)break}}catch(e){throw await(t?.cancel(e).catch(()=>{})),e}finally{t?.releaseLock(),s?.removeEventListener("abort",a),r?.()}}async text(){return await this.completed,this.parts.map(t=>t.text).join("")}html(){return this.text()}async chunks(){return await this.completed,this.parts.map(t=>({...t}))}async routerState(){return t(await this.text())}async pendingBoundaries(){return(await this.text()).split("\x3c!--$?--\x3e").length-1}async streamFrames(){return e(await this.text())}async timeline(){return await this.completed.catch(()=>{}),this.options.timeline?.events.map(t=>({...t}))??[]}cookies(){const t=this.headers;return(t.getSetCookie?.()??t.get("set-cookie")?.split(/,(?=\s*[^=;,\s]+=)/)??[]).map(t=>{const[e,...s]=t.split(";"),r=e.indexOf("=");return{name:e.slice(0,r).trim(),value:e.slice(r+1).trim(),attributes:Object.fromEntries(s.map(t=>{const e=t.indexOf("=");return e<0?[t.trim().toLowerCase(),!0]:[t.slice(0,e).trim().toLowerCase(),t.slice(e+1).trim()]}))}})}}export{s as default};
|
|
2
|
+
//# sourceMappingURL=response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.js","sources":["../../src/testing/response.ts"],"sourcesContent":[null],"names":["TestResponse","response","options","parts","completed","started","constructor","this","performance","now","read","catch","status","headers","reader","body","getReader","decoder","TextDecoder","signal","onComplete","onAbort","cancel","reason","addEventListener","once","throwIfAborted","chunk","text","done","decode","value","stream","push","at","error","releaseLock","removeEventListener","map","join","html","chunks","routerState","pendingBoundaries","split","length","streamFrames","timeline","events","event","cookies","getSetCookie","get","cookie","pair","attributes","separator","indexOf","name","slice","trim","Object","fromEntries","attribute","index","toLowerCase"],"mappings":"oEAyBA,MAAMA,EAMcC,SACGC,QANFC,MAAsB,GACtBC,UACAC,QAEnB,WAAAC,CACkBL,EACGC,EAAgC,IADnCK,KAAAN,SAAAA,EACGM,KAAAL,QAAAA,EAEnBK,KAAKF,QAAUH,EAAQG,SAAWG,YAAYC,MAC9CF,KAAKH,UAAYG,KAAKG,OACjBH,KAAKH,UAAUO,MAAM,OAC5B,CAEA,UAAWC,GACT,OAAOL,KAAKN,SAASW,MACvB,CAEA,WAAWC,GACT,OAAON,KAAKN,SAASY,OACvB,CAGU,UAAMH,GACd,MAAMI,EAASP,KAAKN,SAASc,MAAMC,YAC7BC,EAAU,IAAIC,aACdC,OAAEA,EAAMC,WAAEA,GAAeb,KAAKL,QAC9BmB,EAAU,KACTP,GAAQQ,OAAOH,GAAQI,QAAQZ,MAAM,SAG5CQ,GAAQK,iBAAiB,QAASH,EAAS,CAAEI,MAAM,IAEnD,IAGE,GAFAN,GAAQO,iBAEJZ,EACF,OAAa,CACX,MAAMa,QAAcb,EAAOJ,OAE3BS,GAAQO,iBAER,MAAME,EAAOD,EAAME,KACfZ,EAAQa,SACRb,EAAQa,OAAOH,EAAMI,MAAO,CAAEC,QAAQ,IAM1C,GAJIJ,GACFrB,KAAKJ,MAAM8B,KAAK,CAAEL,OAAMM,GAAI1B,YAAYC,MAAQF,KAAKF,UAGnDsB,EAAME,KACR,KAEJ,CAEJ,CAAE,MAAOM,GAEP,YADMrB,GAAQQ,OAAOa,GAAOxB,MAAM,SAC5BwB,CACR,SACErB,GAAQsB,cACRjB,GAAQkB,oBAAoB,QAAShB,GACrCD,KACF,CACF,CAEO,UAAMQ,GAGX,aAFMrB,KAAKH,UAEJG,KAAKJ,MAAMmC,IAAKX,GAAUA,EAAMC,MAAMW,KAAK,GACpD,CAGO,IAAAC,GACL,OAAOjC,KAAKqB,MACd,CAEO,YAAMa,GAGX,aAFMlC,KAAKH,UAEJG,KAAKJ,MAAMmC,IAAKX,QAAgBA,IACzC,CAEO,iBAAMe,GACX,OAAOA,QAAkBnC,KAAKqB,OAChC,CAGO,uBAAMe,GACX,aAAcpC,KAAKqB,QAAQgB,MAAM,mBAAaC,OAAS,CACzD,CAEO,kBAAMC,GACX,OAAOA,QAAmBvC,KAAKqB,OACjC,CAGO,cAAMmB,GAGX,aAFMxC,KAAKH,UAAUO,MAAM,QAEpBJ,KAAKL,QAAQ6C,UAAUC,OAAOV,IAAKW,QAAgBA,MAAa,EACzE,CAGO,OAAAC,GACL,MAAMrC,EAAUN,KAAKM,QAIrB,OAFEA,EAAQsC,kBAAoBtC,EAAQuC,IAAI,eAAeR,MAAM,uBAAyB,IAE1EN,IAAKe,IACjB,MAAOC,KAASC,GAAcF,EAAOT,MAAM,KACrCY,EAAYF,EAAKG,QAAQ,KAE/B,MAAO,CACLC,KAAMJ,EAAKK,MAAM,EAAGH,GAAWI,OAC/B7B,MAAOuB,EAAKK,MAAMH,EAAY,GAAGI,OACjCL,WAAYM,OAAOC,YACjBP,EAAWjB,IAAKyB,IACd,MAAMC,EAAQD,EAAUN,QAAQ,KAEhC,OAAOO,EAAQ,EACX,CAACD,EAAUH,OAAOK,eAAe,GACjC,CAACF,EAAUJ,MAAM,EAAGK,GAAOJ,OAAOK,cAAeF,EAAUJ,MAAMK,EAAQ,GAAGJ,aAK1F"}
|
package/testing.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare const createTestHandler: <TAppProps = Record<string, any>>({ routes, App, shell, routerOptions, renderToStream, signal: defaultSignal, timeout: defaultTimeout, onContext, onRouterReady, ...options }: import("./testing.js").ITestHandlerOptions<TAppProps>) => import("./testing.js").ITestHandler;
|
|
2
|
+
export { createTestHandler };
|
|
3
|
+
export { default as createDeferred } from './testing/deferred.js';
|
|
4
|
+
export { browserRequest, crawlerRequest } from './testing/requests.js';
|
|
5
|
+
export { default as TestResponse } from './testing/response.js';
|
|
6
|
+
export type { ITestHandler, ITestHandlerOptions, ITestRequestInit } from './testing/create-handler.js';
|
|
7
|
+
export type { ITestChunk, ITestCookie, ITestResponseOptions } from './testing/response.js';
|
|
8
|
+
export type { IRouterState, TStreamFrame } from './testing/parse-response.js';
|
|
9
|
+
export type { ITimelineEvent, TRequestStage } from './services/request-timeline.js';
|
package/testing.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"./node/render-to-stream.js";import r from"./testing/create-handler.js";export{default as createDeferred}from"./testing/deferred.js";export{browserRequest,crawlerRequest}from"./testing/requests.js";export{default as TestResponse}from"./testing/response.js";const t=r(e,async e=>(await(import("./node/production.js"))).loadHtmlShell(e));export{t as createTestHandler};
|
|
2
|
+
//# sourceMappingURL=testing.js.map
|
package/testing.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.js","sources":["../src/testing.ts"],"sourcesContent":[null],"names":["createTestHandler","testHandlerFactory","renderToStream","async","options","loadHtmlShell"],"mappings":"8QAGA,MAAMA,EAAoBC,EAAmBC,EAAgBC,MAAOC,iBACpD,0BAAqBC,cAAcD"}
|