@inox-tools/request-state 0.5.0 → 0.7.0

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/plugin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import {createResolver}from'astro-integration-kit';const r="@it-astro:state",s=`\0${r}`,u=()=>{const{resolve:e}=createResolver(import.meta.url);return {name:"@inox-tools/request-state/vite-plugin",resolveId(t){if(t===r)return s},config(t){t.ssr?.external!==true&&(t.ssr={...t.ssr,external:[...t.ssr?.external??[],"node:async_hooks"]});},load(t,o){if(t!==s)return;const n=o?.ssr?"serverState.js":"clientState.js";return `
2
- export {setState, getState} from '${e("runtime",n)}';
2
+ export {setState, getState, hasState} from '${e("runtime",n)}';
3
3
  export {ServerStateLoaded} from '${e("events.js")}';
4
4
  `.trim()}}};export{u as plugin};//# sourceMappingURL=plugin.js.map
5
5
  //# sourceMappingURL=plugin.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/plugin.ts"],"names":["MODULE_ID","RESOLVED_MODULE_ID","plugin","resolve","createResolver","id","config","options","stateSource"],"mappings":"mDAGA,MAAMA,EAAY,iBAAA,CACZC,CAAAA,CAAqB,CAAA,EAAA,EAAOD,CAAS,GAE9BE,CAAAA,CAAS,IAAc,CACnC,KAAM,CAAE,OAAA,CAAAC,CAAQ,CAAA,CAAIC,cAAAA,CAAe,YAAY,GAAG,CAAA,CAElD,OAAO,CACN,KAAM,uCAAA,CACN,SAAA,CAAUC,CAAAA,CAAI,CACb,GAAIA,CAAAA,GAAOL,CAAAA,CACV,OAAOC,CAET,EACA,MAAA,CAAOK,CAAAA,CAAQ,CACVA,CAAAA,CAAO,GAAA,EAAK,QAAA,GAAa,IAAA,GAE7BA,CAAAA,CAAO,IAAM,CACZ,GAAGA,CAAAA,CAAO,GAAA,CACV,SAAU,CAAC,GAAIA,CAAAA,CAAO,GAAA,EAAK,UAAY,EAAC,CAAI,kBAAkB,CAC/D,GACD,CAAA,CACA,IAAA,CAAKD,CAAAA,CAAIE,CAAAA,CAAS,CACjB,GAAIF,CAAAA,GAAOJ,CAAAA,CAAoB,OAE/B,MAAMO,CAAAA,CAAcD,CAAAA,EAAS,GAAA,CAAM,gBAAA,CAAmB,iBAGtD,OAAO;AAAA,kCAAA,EAFYJ,CAAAA,CAAQ,SAAA,CAAWK,CAAW,CAGN,CAAA;AAAA,iCAAA,EACXL,CAAAA,CAAQ,WAAW,CAAC,CAAA;AAAA,CAAA,CACrD,IAAA,EACA,CACD,CACD","file":"plugin.js","sourcesContent":["import { createResolver } from 'astro-integration-kit';\nimport type { Plugin } from 'vite';\n\nconst MODULE_ID = '@it-astro:state';\nconst RESOLVED_MODULE_ID = `\\x00${MODULE_ID}`;\n\nexport const plugin = (): Plugin => {\n\tconst { resolve } = createResolver(import.meta.url);\n\n\treturn {\n\t\tname: '@inox-tools/request-state/vite-plugin',\n\t\tresolveId(id) {\n\t\t\tif (id === MODULE_ID) {\n\t\t\t\treturn RESOLVED_MODULE_ID;\n\t\t\t}\n\t\t},\n\t\tconfig(config) {\n\t\t\tif (config.ssr?.external === true) return;\n\n\t\t\tconfig.ssr = {\n\t\t\t\t...config.ssr,\n\t\t\t\texternal: [...(config.ssr?.external ?? []), 'node:async_hooks'],\n\t\t\t};\n\t\t},\n\t\tload(id, options) {\n\t\t\tif (id !== RESOLVED_MODULE_ID) return;\n\n\t\t\tconst stateSource = options?.ssr ? 'serverState.js' : 'clientState.js';\n\t\t\tconst importPath = resolve('runtime', stateSource);\n\n\t\t\treturn `\nexport {setState, getState} from '${importPath}';\nexport {ServerStateLoaded} from '${resolve('events.js')}';\n`.trim();\n\t\t},\n\t};\n};\n"]}
1
+ {"version":3,"sources":["../src/plugin.ts"],"names":["MODULE_ID","RESOLVED_MODULE_ID","plugin","resolve","createResolver","id","config","options","stateSource"],"mappings":"mDAGA,MAAMA,EAAY,iBAAA,CACZC,CAAAA,CAAqB,CAAA,EAAA,EAAOD,CAAS,GAE9BE,CAAAA,CAAS,IAAc,CACnC,KAAM,CAAE,OAAA,CAAAC,CAAQ,CAAA,CAAIC,cAAAA,CAAe,YAAY,GAAG,CAAA,CAElD,OAAO,CACN,KAAM,uCAAA,CACN,SAAA,CAAUC,CAAAA,CAAI,CACb,GAAIA,CAAAA,GAAOL,CAAAA,CACV,OAAOC,CAET,EACA,MAAA,CAAOK,CAAAA,CAAQ,CACVA,CAAAA,CAAO,GAAA,EAAK,QAAA,GAAa,IAAA,GAE7BA,CAAAA,CAAO,IAAM,CACZ,GAAGA,CAAAA,CAAO,GAAA,CACV,SAAU,CAAC,GAAIA,CAAAA,CAAO,GAAA,EAAK,UAAY,EAAC,CAAI,kBAAkB,CAC/D,GACD,CAAA,CACA,IAAA,CAAKD,CAAAA,CAAIE,CAAAA,CAAS,CACjB,GAAIF,CAAAA,GAAOJ,CAAAA,CAAoB,OAE/B,MAAMO,CAAAA,CAAcD,CAAAA,EAAS,GAAA,CAAM,gBAAA,CAAmB,iBAGtD,OAAO;AAAA,4CAAA,EAFYJ,CAAAA,CAAQ,SAAA,CAAWK,CAAW,CAGI,CAAA;AAAA,iCAAA,EACrBL,CAAAA,CAAQ,WAAW,CAAC,CAAA;AAAA,CAAA,CACrD,IAAA,EACA,CACD,CACD","file":"plugin.js","sourcesContent":["import { createResolver } from 'astro-integration-kit';\nimport type { Plugin } from 'vite';\n\nconst MODULE_ID = '@it-astro:state';\nconst RESOLVED_MODULE_ID = `\\x00${MODULE_ID}`;\n\nexport const plugin = (): Plugin => {\n\tconst { resolve } = createResolver(import.meta.url);\n\n\treturn {\n\t\tname: '@inox-tools/request-state/vite-plugin',\n\t\tresolveId(id) {\n\t\t\tif (id === MODULE_ID) {\n\t\t\t\treturn RESOLVED_MODULE_ID;\n\t\t\t}\n\t\t},\n\t\tconfig(config) {\n\t\t\tif (config.ssr?.external === true) return;\n\n\t\t\tconfig.ssr = {\n\t\t\t\t...config.ssr,\n\t\t\t\texternal: [...(config.ssr?.external ?? []), 'node:async_hooks'],\n\t\t\t};\n\t\t},\n\t\tload(id, options) {\n\t\t\tif (id !== RESOLVED_MODULE_ID) return;\n\n\t\t\tconst stateSource = options?.ssr ? 'serverState.js' : 'clientState.js';\n\t\t\tconst importPath = resolve('runtime', stateSource);\n\n\t\t\treturn `\nexport {setState, getState, hasState} from '${importPath}';\nexport {ServerStateLoaded} from '${resolve('events.js')}';\n`.trim();\n\t\t},\n\t};\n};\n"]}
@@ -1,2 +1,2 @@
1
- import {parse}from'devalue';import {ServerStateLoaded}from'../events.js';const i={URL:e=>new URL(e),Date:e=>new Date(e),GlobalSymbol:e=>Symbol.for(e),WellKnownSymbol:e=>Symbol[e]},a=e=>{const t=e.getElementById("it-astro-state");if(t?.textContent){const o=parse(t.textContent,i);return t.remove(),o}};let r=a(document);const n=new Map,s=()=>{const e=new ServerStateLoaded(new Map(n),r??new Map);if(document.dispatchEvent(e)){n.clear();for(const[t,o]of e.serverState.entries())n.set(t,o);}};s(),document.addEventListener("astro:before-swap",e=>{r=a(e.newDocument);}),document.addEventListener("astro:after-swap",s);const f=(e,t)=>(n.has(e)||t!==void 0&&n.set(e,t),n.get(e)),p=(e,t)=>{t===void 0?n.delete(e):n.set(e,t);};export{f as getState,p as setState};//# sourceMappingURL=clientState.js.map
1
+ import {parse}from'devalue';import {ServerStateLoaded}from'../events.js';const u={URL:t=>new URL(t),Date:t=>new Date(t),GlobalSymbol:t=>Symbol.for(t),WellKnownSymbol:t=>Symbol[t]},r=t=>Array.from(t.querySelectorAll("script.it-astro-state")).map(o=>{if(o?.textContent){const a=parse(o.textContent,u);return o.remove(),a}}).filter(o=>!!o);let i=r(document);const d=()=>{const t="__@it-astro:request-state-data";return t in window||(window[t]=new Map),window[t]},n=d(),f=(t,e)=>{for(const[s,o]of e.entries())t.has(s)||t.set(s,o);return t},c=(t=false)=>()=>{const e=t?new Map:new Map(n);i?.reduce(f,e);const s=new ServerStateLoaded(new Map(n),e,{cancelable:true});if(document.dispatchEvent(s)){n.clear();for(const[o,a]of s.serverState.entries())n.set(o,a);}};c()(),document.addEventListener("astro:before-swap",t=>{i=r(t.newDocument);}),document.addEventListener("astro:after-swap",c(true));const m=(t,e)=>(n.has(t)||e!==void 0&&n.set(t,e),n.get(t)),y=t=>n.has(t),v=(t,e)=>{e===void 0?n.delete(t):n.set(t,e);};export{m as getState,y as hasState,v as setState,n as state};//# sourceMappingURL=clientState.js.map
2
2
  //# sourceMappingURL=clientState.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/runtime/clientState.ts"],"names":["revivers","value","loadState","doc","element","state","parse","nextState","applyState","event","ServerStateLoaded","key","getState","valueIfMissing","setState"],"mappings":"yEAGA,MAAMA,CAAAA,CAAgD,CACrD,IAAMC,CAAAA,EAAU,IAAI,IAAIA,CAAK,CAAA,CAC7B,KAAOA,CAAAA,EAAU,IAAI,IAAA,CAAKA,CAAK,EAC/B,YAAA,CAAeA,CAAAA,EAAU,MAAA,CAAO,GAAA,CAAIA,CAAK,CAAA,CACzC,eAAA,CAAkBA,CAAAA,EAAU,MAAA,CAAOA,CAA4B,CAChE,CAAA,CAEMC,EAAaC,CAAAA,EAAqC,CACvD,MAAMC,CAAAA,CAAUD,CAAAA,CAAI,cAAA,CAAe,gBAAgB,EAEnD,GAAIC,CAAAA,EAAS,WAAA,CAAa,CACzB,MAAMC,CAAAA,CAAQC,KAAAA,CAAMF,CAAAA,CAAQ,WAAA,CAAaJ,CAAQ,CAAA,CACjD,OAAAI,EAAQ,MAAA,EAAO,CACRC,CACR,CACD,CAAA,CAEA,IAAIE,CAAAA,CAAYL,EAAU,QAAQ,CAAA,CAClC,MAAMG,CAAAA,CAAQ,IAAI,GAAA,CAEZG,CAAAA,CAAa,IAAM,CACxB,MAAMC,CAAAA,CAAQ,IAAIC,kBAAkB,IAAI,GAAA,CAAIL,CAAK,CAAA,CAAGE,CAAAA,EAAa,IAAI,GAAK,EAE1E,GAAI,QAAA,CAAS,aAAA,CAAcE,CAAK,EAAG,CAClCJ,CAAAA,CAAM,KAAA,EAAM,CACZ,SAAW,CAACM,CAAAA,CAAKV,CAAK,CAAA,GAAKQ,CAAAA,CAAM,YAAY,OAAA,EAAQ,CACpDJ,CAAAA,CAAM,GAAA,CAAIM,EAAKV,CAAK,EAEtB,CACD,CAAA,CAEAO,GAAW,CAEX,QAAA,CAAS,gBAAA,CAAiB,mBAAA,CAAsBC,GAAU,CACzDF,CAAAA,CAAYL,EAAUO,CAAAA,CAAM,WAAW,EACxC,CAAC,CAAA,CACD,QAAA,CAAS,gBAAA,CAAiB,mBAAoBD,CAAU,CAAA,CAEjD,MAAMI,CAAAA,CAAW,CAACD,CAAAA,CAAaE,CAAAA,IAChCR,CAAAA,CAAM,GAAA,CAAIM,CAAG,CAAA,EACbE,CAAAA,GAAmB,QACtBR,CAAAA,CAAM,GAAA,CAAIM,EAAKE,CAAc,CAAA,CAGxBR,CAAAA,CAAM,GAAA,CAAIM,CAAG,CAAA,CAAA,CAGRG,CAAAA,CAAW,CAACH,CAAAA,CAAaV,IAAyB,CAC1DA,CAAAA,GAAU,MAAA,CACbI,CAAAA,CAAM,OAAOM,CAAG,CAAA,CAEhBN,EAAM,GAAA,CAAIM,CAAAA,CAAKV,CAAK,EAEtB","file":"clientState.js","sourcesContent":["import { parse } from 'devalue';\nimport { type State, ServerStateLoaded } from '../events.js';\n\nconst revivers: Record<string, (value: any) => any> = {\n\tURL: (value) => new URL(value),\n\tDate: (value) => new Date(value),\n\tGlobalSymbol: (value) => Symbol.for(value),\n\tWellKnownSymbol: (value) => Symbol[value as keyof typeof Symbol],\n};\n\nconst loadState = (doc: Document): State | undefined => {\n\tconst element = doc.getElementById('it-astro-state');\n\n\tif (element?.textContent) {\n\t\tconst state = parse(element.textContent, revivers);\n\t\telement.remove();\n\t\treturn state;\n\t}\n};\n\nlet nextState = loadState(document);\nconst state = new Map();\n\nconst applyState = () => {\n\tconst event = new ServerStateLoaded(new Map(state), nextState ?? new Map());\n\n\tif (document.dispatchEvent(event)) {\n\t\tstate.clear();\n\t\tfor (const [key, value] of event.serverState.entries()) {\n\t\t\tstate.set(key, value);\n\t\t}\n\t}\n};\n\napplyState();\n\ndocument.addEventListener('astro:before-swap', (event) => {\n\tnextState = loadState(event.newDocument);\n});\ndocument.addEventListener('astro:after-swap', applyState);\n\nexport const getState = (key: string, valueIfMissing?: unknown): unknown => {\n\tif (!state.has(key)) {\n\t\tif (valueIfMissing !== undefined) {\n\t\t\tstate.set(key, valueIfMissing);\n\t\t}\n\t}\n\treturn state.get(key);\n};\n\nexport const setState = (key: string, value: unknown): void => {\n\tif (value === undefined) {\n\t\tstate.delete(key);\n\t} else {\n\t\tstate.set(key, value);\n\t}\n};\n"]}
1
+ {"version":3,"sources":["../../src/runtime/clientState.ts"],"names":["revivers","value","loadState","doc","element","state","parse","nextStates","initialiseState","STATE_WINDOW_NAMESPACE","mergeState","oldState","newState","newKey","newValue","applyState","isViewTransition","startingState","event","ServerStateLoaded","key","getState","valueIfMissing","hasState","setState"],"mappings":"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","file":"clientState.js","sourcesContent":["import { parse } from 'devalue';\nimport { type State, ServerStateLoaded } from '../events.js';\n\nconst revivers: Record<string, (value: any) => any> = {\n\tURL: (value) => new URL(value),\n\tDate: (value) => new Date(value),\n\tGlobalSymbol: (value) => Symbol.for(value),\n\tWellKnownSymbol: (value) => Symbol[value as keyof typeof Symbol],\n};\n\nconst loadState = (doc: Document): State[] => {\n\tconst elements = Array.from(doc.querySelectorAll('script.it-astro-state'));\n\tconst scripts = elements\n\t\t.map((element) => {\n\t\t\tif (element?.textContent) {\n\t\t\t\tconst state = parse(element.textContent, revivers);\n\t\t\t\telement.remove();\n\t\t\t\treturn state;\n\t\t\t}\n\t\t})\n\t\t.filter((state): state is State => !!state);\n\treturn scripts;\n};\n\nlet nextStates = loadState(document);\n\nconst initialiseState = (): State => {\n\tconst STATE_WINDOW_NAMESPACE = '__@it-astro:request-state-data' as const;\n\tif (!(STATE_WINDOW_NAMESPACE in window)) {\n\t\twindow[STATE_WINDOW_NAMESPACE] = new Map();\n\t}\n\treturn window[STATE_WINDOW_NAMESPACE] as State;\n};\n\nexport const state = initialiseState();\n\nconst mergeState = (oldState: State, newState: State) => {\n\tfor (const [newKey, newValue] of newState.entries()) {\n\t\tconst isHit = oldState.has(newKey);\n\t\tif (isHit) {\n\t\t\tcontinue;\n\t\t}\n\t\toldState.set(newKey, newValue);\n\t}\n\treturn oldState;\n};\n\nconst applyState =\n\t(isViewTransition: boolean = false) =>\n\t() => {\n\t\t/*\n\t\tIf it's an uncancelled view transition, we want a completely new state \n\t\t(otherwise the new view will 'lose' to the old view in the case of a conflict). \n\t\tIf it's not a view transition, we want to shallow clone the state object.\n\t\tmergeState will update the top-level keys of the state passed to it, but if \n\t\tthe user calls preventDefault on the serverStateLoaded we're supposed to abort \n\t\tthe update. So it won't do to have already updated the underlying global state \n\t\tmap. And on the other hand we do a shallow, not deep, clone because the user \n\t\tcan mutate their own stored values any time they want.\n\t\t*/\n\t\tconst startingState = isViewTransition ? new Map() : new Map(state);\n\t\tnextStates?.reduce(mergeState, startingState);\n\t\tconst event = new ServerStateLoaded(new Map(state), startingState, {\n\t\t\tcancelable: true,\n\t\t});\n\n\t\tif (document.dispatchEvent(event)) {\n\t\t\tstate.clear();\n\t\t\tfor (const [key, value] of event.serverState.entries()) {\n\t\t\t\tstate.set(key, value);\n\t\t\t}\n\t\t}\n\t};\n\napplyState()();\n\ndocument.addEventListener('astro:before-swap', (event) => {\n\tnextStates = loadState(event.newDocument);\n});\ndocument.addEventListener('astro:after-swap', applyState(true));\n\nexport const getState = (key: string, valueIfMissing?: unknown): unknown => {\n\tif (!state.has(key)) {\n\t\tif (valueIfMissing !== undefined) {\n\t\t\tstate.set(key, valueIfMissing);\n\t\t}\n\t}\n\treturn state.get(key);\n};\n\nexport const hasState = (key: string) => state.has(key);\n\nexport const setState = (key: string, value: unknown): void => {\n\tif (value === undefined) {\n\t\tstate.delete(key);\n\t} else {\n\t\tstate.set(key, value);\n\t}\n};\n"]}
@@ -1,2 +1,2 @@
1
- import {defineMiddleware}from'astro/middleware';import {collectState}from'./serverState.js';import {parse}from'content-type';const h=defineMiddleware(async(f,r)=>{const{getState:a,result:t}=await collectState(r),s=t.headers.get("Content-Type");if(s===null)return t;const{type:o}=parse(s);if(o!=="text/html"&&!o.startsWith("text/html+"))return t;const e=await t.text(),n=e.indexOf("</head>");if(n>-1){const i=a();if(i){const c=`<script id="it-astro-state" type="application/json+devalue">${i}</script>`;return new Response(e.slice(0,n)+c+e.slice(n),t)}}return new Response(e,t)});export{h as onRequest};//# sourceMappingURL=middleware.js.map
1
+ import {defineMiddleware}from'astro/middleware';import {collectState}from'./serverState.js';import {parse}from'content-type';const h=defineMiddleware(async(u,a)=>{const{getState:c,result:t}=await collectState(a),o=t.headers.get("Content-Type");if(o===null)return t;const{type:r}=parse(o);if(r!=="text/html"&&!r.startsWith("text/html+"))return t;const e=await t.text(),i=c(),s=i?`<script class="it-astro-state" type="application/json+devalue">${i}</script>`:null;if(s){const n=e.indexOf("</head>");return n>-1?new Response(e.slice(0,n)+s+e.slice(n),t):new Response(s+e,t)}return new Response(e,t)});export{h as onRequest};//# sourceMappingURL=middleware.js.map
2
2
  //# sourceMappingURL=middleware.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/runtime/middleware.ts"],"names":["onRequest","defineMiddleware","_","next","getState","result","collectState","contentType","mediaType","parse","originalBody","headCloseIndex","state","stateScript"],"mappings":"6HAIO,MAAMA,EAAYC,gBAAAA,CAAiB,MAAOC,CAAAA,CAAGC,CAAAA,GAAS,CAC5D,KAAM,CAAE,QAAA,CAAAC,CAAAA,CAAU,MAAA,CAAAC,CAAO,CAAA,CAAI,MAAMC,aAAaH,CAAI,CAAA,CAE9CI,CAAAA,CAAcF,CAAAA,CAAO,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,CAErD,GAAIE,CAAAA,GAAgB,IAAA,CAAM,OAAOF,CAAAA,CAEjC,KAAM,CAAE,IAAA,CAAMG,CAAU,CAAA,CAAIC,KAAAA,CAAMF,CAAW,EAE7C,GAAIC,CAAAA,GAAc,WAAA,EAAe,CAACA,CAAAA,CAAU,UAAA,CAAW,YAAY,CAAA,CAAG,OAAOH,CAAAA,CAE7E,MAAMK,CAAAA,CAAe,MAAML,EAAO,IAAA,EAAK,CAEjCM,CAAAA,CAAiBD,CAAAA,CAAa,OAAA,CAAQ,SAAS,EACrD,GAAIC,CAAAA,CAAiB,EAAA,CAAI,CACxB,MAAMC,CAAAA,CAAQR,GAAS,CACvB,GAAIQ,CAAAA,CAAO,CACV,MAAMC,CAAAA,CAAc,+DAA+DD,CAAK,CAAA,SAAA,CAAA,CAExF,OAAO,IAAI,QAAA,CACVF,CAAAA,CAAa,MAAM,CAAA,CAAGC,CAAc,CAAA,CAAIE,CAAAA,CAAcH,CAAAA,CAAa,KAAA,CAAMC,CAAc,CAAA,CACvFN,CACD,CACD,CACD,CAEA,OAAO,IAAI,QAAA,CAASK,CAAAA,CAAcL,CAAM,CACzC,CAAC","file":"middleware.js","sourcesContent":["import { defineMiddleware } from 'astro/middleware';\nimport { collectState } from './serverState.js';\nimport { parse } from 'content-type';\n\nexport const onRequest = defineMiddleware(async (_, next) => {\n\tconst { getState, result } = await collectState(next);\n\n\tconst contentType = result.headers.get('Content-Type');\n\n\tif (contentType === null) return result;\n\n\tconst { type: mediaType } = parse(contentType);\n\n\tif (mediaType !== 'text/html' && !mediaType.startsWith('text/html+')) return result;\n\n\tconst originalBody = await result.text();\n\n\tconst headCloseIndex = originalBody.indexOf('</head>');\n\tif (headCloseIndex > -1) {\n\t\tconst state = getState();\n\t\tif (state) {\n\t\t\tconst stateScript = `<script id=\"it-astro-state\" type=\"application/json+devalue\">${state}</script>`;\n\n\t\t\treturn new Response(\n\t\t\t\toriginalBody.slice(0, headCloseIndex) + stateScript + originalBody.slice(headCloseIndex),\n\t\t\t\tresult\n\t\t\t);\n\t\t}\n\t}\n\n\treturn new Response(originalBody, result);\n});\n"]}
1
+ {"version":3,"sources":["../../src/runtime/middleware.ts"],"names":["onRequest","defineMiddleware","_","next","getState","result","collectState","contentType","mediaType","parse","originalBody","state","stateScript","headCloseIndex"],"mappings":"6HAIO,MAAMA,EAAYC,gBAAAA,CAAiB,MAAOC,CAAAA,CAAGC,CAAAA,GAAS,CAC5D,KAAM,CAAE,QAAA,CAAAC,CAAAA,CAAU,MAAA,CAAAC,CAAO,CAAA,CAAI,MAAMC,YAAAA,CAAaH,CAAI,CAAA,CAE9CI,CAAAA,CAAcF,CAAAA,CAAO,OAAA,CAAQ,GAAA,CAAI,cAAc,EAErD,GAAIE,CAAAA,GAAgB,IAAA,CAAM,OAAOF,CAAAA,CAEjC,KAAM,CAAE,IAAA,CAAMG,CAAU,CAAA,CAAIC,KAAAA,CAAMF,CAAW,CAAA,CAE7C,GAAIC,CAAAA,GAAc,WAAA,EAAe,CAACA,CAAAA,CAAU,UAAA,CAAW,YAAY,CAAA,CAAG,OAAOH,CAAAA,CAE7E,MAAMK,CAAAA,CAAe,MAAML,CAAAA,CAAO,IAAA,GAE5BM,CAAAA,CAAQP,CAAAA,EAAS,CACjBQ,CAAAA,CAAcD,CAAAA,CACjB,CAAA,+DAAA,EAAkEA,CAAK,CAAA,SAAA,CAAA,CACvE,IAAA,CACH,GAAIC,CAAAA,CAAa,CAChB,MAAMC,CAAAA,CAAiBH,CAAAA,CAAa,OAAA,CAAQ,SAAS,CAAA,CACrD,OAAIG,CAAAA,CAAiB,EAAA,CACb,IAAI,QAAA,CACVH,CAAAA,CAAa,KAAA,CAAM,CAAA,CAAGG,CAAc,CAAA,CAAID,EAAcF,CAAAA,CAAa,KAAA,CAAMG,CAAc,CAAA,CACvFR,CACD,CAAA,CAEO,IAAI,QAAA,CAASO,CAAAA,CAAcF,CAAAA,CAAcL,CAAM,CAExD,CAEA,OAAO,IAAI,QAAA,CAASK,CAAAA,CAAcL,CAAM,CACzC,CAAC","file":"middleware.js","sourcesContent":["import { defineMiddleware } from 'astro/middleware';\nimport { collectState } from './serverState.js';\nimport { parse } from 'content-type';\n\nexport const onRequest = defineMiddleware(async (_, next) => {\n\tconst { getState, result } = await collectState(next);\n\n\tconst contentType = result.headers.get('Content-Type');\n\n\tif (contentType === null) return result;\n\n\tconst { type: mediaType } = parse(contentType);\n\n\tif (mediaType !== 'text/html' && !mediaType.startsWith('text/html+')) return result;\n\n\tconst originalBody = await result.text();\n\n\tconst state = getState();\n\tconst stateScript = state\n\t\t? `<script class=\"it-astro-state\" type=\"application/json+devalue\">${state}</script>`\n\t\t: null;\n\tif (stateScript) {\n\t\tconst headCloseIndex = originalBody.indexOf('</head>');\n\t\tif (headCloseIndex > -1) {\n\t\t\treturn new Response(\n\t\t\t\toriginalBody.slice(0, headCloseIndex) + stateScript + originalBody.slice(headCloseIndex),\n\t\t\t\tresult\n\t\t\t);\n\t\t} else {\n\t\t\treturn new Response(stateScript + originalBody, result);\n\t\t}\n\t}\n\n\treturn new Response(originalBody, result);\n});\n"]}
@@ -1,2 +1,2 @@
1
- import {AsyncLocalStorage}from'node:async_hooks';import {stringify}from'devalue';const o=new AsyncLocalStorage,l=(t,e)=>{const n=o.getStore();if(n!==void 0)return n.has(t)||e!==void 0&&n.set(t,e),n.get(t)},p=(t,e)=>{const n=o.getStore();e===void 0?n?.delete(t):n?.set(t,e);},i=new Map(Object.entries(Symbol).filter(([t,e])=>typeof e=="symbol"&&typeof t=="string").map(([t,e])=>[e,t])),c={URL:t=>t instanceof URL&&t.href,Date:t=>t instanceof Date&&t.valueOf(),GlobalSymbol:t=>typeof t=="symbol"&&t.description!==void 0&&t===Symbol.for(t.description)&&t.description,WellKnownSymbol:t=>typeof t=="symbol"&&i.get(t)},d=async t=>{const e=new Map;return {result:await o.run(e,t),getState:()=>e.size>0&&stringify(e,c)}};export{d as collectState,l as getState,p as setState};//# sourceMappingURL=serverState.js.map
1
+ import {AsyncLocalStorage}from'node:async_hooks';import {stringify}from'devalue';const o=new AsyncLocalStorage,l=(t,e)=>{const n=o.getStore();if(n!==void 0)return n.has(t)||e!==void 0&&n.set(t,e),n.get(t)},p=t=>o.getStore()?.has(t)||false,S=(t,e)=>{const n=o.getStore();e===void 0?n?.delete(t):n?.set(t,e);},i=new Map(Object.entries(Symbol).filter(([t,e])=>typeof e=="symbol"&&typeof t=="string").map(([t,e])=>[e,t])),a={URL:t=>t instanceof URL&&t.href,Date:t=>t instanceof Date&&t.valueOf(),GlobalSymbol:t=>typeof t=="symbol"&&t.description!==void 0&&t===Symbol.for(t.description)&&t.description,WellKnownSymbol:t=>typeof t=="symbol"&&i.get(t)},d=async t=>{const e=new Map;return {result:await o.run(e,t),getState:()=>e.size>0&&stringify(e,a)}};export{d as collectState,l as getState,p as hasState,S as setState};//# sourceMappingURL=serverState.js.map
2
2
  //# sourceMappingURL=serverState.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/runtime/serverState.ts"],"names":["store","AsyncLocalStorage","getState","key","valueIfMissing","state","setState","value","wellKnownSymbols","reducers","collectState","cb","stringify"],"mappings":"iFAKA,MAAMA,CAAAA,CAAQ,IAAIC,kBAELC,CAAAA,CAAW,CAACC,CAAAA,CAAaC,CAAAA,GAAsC,CAC3E,MAAMC,CAAAA,CAAQL,CAAAA,CAAM,QAAA,GACpB,GAAIK,CAAAA,GAAU,MAAA,CAEd,OAAKA,EAAM,GAAA,CAAIF,CAAG,CAAA,EACbC,CAAAA,GAAmB,QACtBC,CAAAA,CAAM,GAAA,CAAIF,EAAKC,CAAc,CAAA,CAGxBC,EAAM,GAAA,CAAIF,CAAG,CACrB,CAAA,CAEaG,EAAW,CAACH,CAAAA,CAAaI,CAAAA,GAAyB,CAC9D,MAAMF,CAAAA,CAAQL,CAAAA,CAAM,QAAA,EAAS,CACzBO,IAAU,MAAA,CACbF,CAAAA,EAAO,OAAOF,CAAG,CAAA,CAEjBE,GAAO,GAAA,CAAIF,CAAAA,CAAKI,CAAK,EAEvB,EAOMC,CAAAA,CAAmB,IAAI,GAAA,CAC5B,MAAA,CAAO,QAAQ,MAAM,CAAA,CACnB,MAAA,CAAO,CAAC,CAACL,CAAAA,CAAKI,CAAK,IAAM,OAAOA,CAAAA,EAAU,UAAY,OAAOJ,CAAAA,EAAQ,QAAQ,CAAA,CAC7E,IAAI,CAAC,CAACA,CAAAA,CAAKI,CAAK,IAAM,CAACA,CAAAA,CAAOJ,CAAG,CAAC,CACrC,CAAA,CAEMM,CAAAA,CAAgD,CACrD,GAAA,CAAMF,GAAUA,CAAAA,YAAiB,GAAA,EAAOA,CAAAA,CAAM,IAAA,CAC9C,KAAOA,CAAAA,EAAUA,CAAAA,YAAiB,IAAA,EAAQA,CAAAA,CAAM,SAAQ,CACxD,YAAA,CAAeA,CAAAA,EACd,OAAOA,GAAU,QAAA,EACjBA,CAAAA,CAAM,cAAgB,MAAA,EACtBA,CAAAA,GAAU,OAAO,GAAA,CAAIA,CAAAA,CAAM,WAAW,CAAA,EACtCA,EAAM,WAAA,CACP,eAAA,CAAkBA,CAAAA,EAAU,OAAOA,GAAU,QAAA,EAAYC,CAAAA,CAAiB,GAAA,CAAID,CAAK,CACpF,CAAA,CAEaG,CAAAA,CAAe,MAAUC,CAAAA,EAAqD,CAC1F,MAAMN,CAAAA,CAAQ,IAAI,GAAA,CAElB,OAAO,CACN,MAAA,CAFc,MAAML,CAAAA,CAAM,GAAA,CAAIK,EAAOM,CAAE,CAAA,CAGvC,QAAA,CAAU,IAAMN,EAAM,IAAA,CAAO,CAAA,EAAKO,UAAUP,CAAAA,CAAOI,CAAQ,CAC5D,CACD","file":"serverState.js","sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport { stringify } from 'devalue';\n\ntype State = Map<string, unknown>;\n\nconst store = new AsyncLocalStorage<State>();\n\nexport const getState = (key: string, valueIfMissing?: unknown): unknown => {\n\tconst state = store.getStore();\n\tif (state === undefined) return;\n\n\tif (!state.has(key)) {\n\t\tif (valueIfMissing !== undefined) {\n\t\t\tstate.set(key, valueIfMissing);\n\t\t}\n\t}\n\treturn state.get(key);\n};\n\nexport const setState = (key: string, value: unknown): void => {\n\tconst state = store.getStore();\n\tif (value === undefined) {\n\t\tstate?.delete(key);\n\t} else {\n\t\tstate?.set(key, value);\n\t}\n};\n\nexport type CollectedState<T> = {\n\tgetState: () => string | false;\n\tresult: T;\n};\n\nconst wellKnownSymbols = new Map(\n\tObject.entries(Symbol)\n\t\t.filter(([key, value]) => typeof value === 'symbol' && typeof key === 'string')\n\t\t.map(([key, value]) => [value, key])\n);\n\nconst reducers: Record<string, (value: any) => any> = {\n\tURL: (value) => value instanceof URL && value.href,\n\tDate: (value) => value instanceof Date && value.valueOf(),\n\tGlobalSymbol: (value) =>\n\t\ttypeof value === 'symbol' &&\n\t\tvalue.description !== undefined &&\n\t\tvalue === Symbol.for(value.description) &&\n\t\tvalue.description,\n\tWellKnownSymbol: (value) => typeof value === 'symbol' && wellKnownSymbols.get(value),\n};\n\nexport const collectState = async <R>(cb: () => Promise<R>): Promise<CollectedState<R>> => {\n\tconst state = new Map();\n\tconst result = await store.run(state, cb);\n\treturn {\n\t\tresult,\n\t\tgetState: () => state.size > 0 && stringify(state, reducers),\n\t};\n};\n"]}
1
+ {"version":3,"sources":["../../src/runtime/serverState.ts"],"names":["store","AsyncLocalStorage","getState","key","valueIfMissing","state","hasState","setState","value","wellKnownSymbols","reducers","collectState","cb","stringify"],"mappings":"iFAKA,MAAMA,CAAAA,CAAQ,IAAIC,kBAELC,CAAAA,CAAW,CAACC,EAAaC,CAAAA,GAAsC,CAC3E,MAAMC,CAAAA,CAAQL,CAAAA,CAAM,QAAA,EAAS,CAC7B,GAAIK,CAAAA,GAAU,MAAA,CAEd,OAAKA,CAAAA,CAAM,GAAA,CAAIF,CAAG,CAAA,EACbC,CAAAA,GAAmB,MAAA,EACtBC,CAAAA,CAAM,IAAIF,CAAAA,CAAKC,CAAc,EAGxBC,CAAAA,CAAM,GAAA,CAAIF,CAAG,CACrB,CAAA,CAEaG,CAAAA,CAAYH,CAAAA,EAAgBH,EAAM,QAAA,EAAS,EAAG,IAAIG,CAAG,CAAA,EAAK,MAE1DI,CAAAA,CAAW,CAACJ,CAAAA,CAAaK,CAAAA,GAAyB,CAC9D,MAAMH,CAAAA,CAAQL,EAAM,QAAA,EAAS,CACzBQ,IAAU,MAAA,CACbH,CAAAA,EAAO,MAAA,CAAOF,CAAG,EAEjBE,CAAAA,EAAO,GAAA,CAAIF,EAAKK,CAAK,EAEvB,EAOMC,CAAAA,CAAmB,IAAI,GAAA,CAC5B,MAAA,CAAO,QAAQ,MAAM,CAAA,CACnB,OAAO,CAAC,CAACN,EAAKK,CAAK,CAAA,GAAM,OAAOA,CAAAA,EAAU,UAAY,OAAOL,CAAAA,EAAQ,QAAQ,CAAA,CAC7E,GAAA,CAAI,CAAC,CAACA,CAAAA,CAAKK,CAAK,CAAA,GAAM,CAACA,CAAAA,CAAOL,CAAG,CAAC,CACrC,CAAA,CAEMO,EAAgD,CACrD,GAAA,CAAMF,CAAAA,EAAUA,CAAAA,YAAiB,KAAOA,CAAAA,CAAM,IAAA,CAC9C,KAAOA,CAAAA,EAAUA,CAAAA,YAAiB,MAAQA,CAAAA,CAAM,OAAA,EAAQ,CACxD,YAAA,CAAeA,GACd,OAAOA,CAAAA,EAAU,UACjBA,CAAAA,CAAM,WAAA,GAAgB,QACtBA,CAAAA,GAAU,MAAA,CAAO,GAAA,CAAIA,CAAAA,CAAM,WAAW,CAAA,EACtCA,CAAAA,CAAM,YACP,eAAA,CAAkBA,CAAAA,EAAU,OAAOA,CAAAA,EAAU,QAAA,EAAYC,CAAAA,CAAiB,GAAA,CAAID,CAAK,CACpF,CAAA,CAEaG,EAAe,MAAUC,CAAAA,EAAqD,CAC1F,MAAMP,CAAAA,CAAQ,IAAI,GAAA,CAElB,OAAO,CACN,MAAA,CAFc,MAAML,CAAAA,CAAM,GAAA,CAAIK,EAAOO,CAAE,CAAA,CAGvC,QAAA,CAAU,IAAMP,EAAM,IAAA,CAAO,CAAA,EAAKQ,UAAUR,CAAAA,CAAOK,CAAQ,CAC5D,CACD","file":"serverState.js","sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport { stringify } from 'devalue';\n\ntype State = Map<string, unknown>;\n\nconst store = new AsyncLocalStorage<State>();\n\nexport const getState = (key: string, valueIfMissing?: unknown): unknown => {\n\tconst state = store.getStore();\n\tif (state === undefined) return;\n\n\tif (!state.has(key)) {\n\t\tif (valueIfMissing !== undefined) {\n\t\t\tstate.set(key, valueIfMissing);\n\t\t}\n\t}\n\treturn state.get(key);\n};\n\nexport const hasState = (key: string) => store.getStore()?.has(key) || false;\n\nexport const setState = (key: string, value: unknown): void => {\n\tconst state = store.getStore();\n\tif (value === undefined) {\n\t\tstate?.delete(key);\n\t} else {\n\t\tstate?.set(key, value);\n\t}\n};\n\nexport type CollectedState<T> = {\n\tgetState: () => string | false;\n\tresult: T;\n};\n\nconst wellKnownSymbols = new Map(\n\tObject.entries(Symbol)\n\t\t.filter(([key, value]) => typeof value === 'symbol' && typeof key === 'string')\n\t\t.map(([key, value]) => [value, key])\n);\n\nconst reducers: Record<string, (value: any) => any> = {\n\tURL: (value) => value instanceof URL && value.href,\n\tDate: (value) => value instanceof Date && value.valueOf(),\n\tGlobalSymbol: (value) =>\n\t\ttypeof value === 'symbol' &&\n\t\tvalue.description !== undefined &&\n\t\tvalue === Symbol.for(value.description) &&\n\t\tvalue.description,\n\tWellKnownSymbol: (value) => typeof value === 'symbol' && wellKnownSymbols.get(value),\n};\n\nexport const collectState = async <R>(cb: () => Promise<R>): Promise<CollectedState<R>> => {\n\tconst state = new Map();\n\tconst result = await store.run(state, cb);\n\treturn {\n\t\tresult,\n\t\tgetState: () => state.size > 0 && stringify(state, reducers),\n\t};\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inox-tools/request-state",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Shared request state between server and client",
5
5
  "keywords": [
6
6
  "astro-integration",
@@ -23,21 +23,21 @@
23
23
  "virtual.d.ts"
24
24
  ],
25
25
  "dependencies": {
26
- "astro-integration-kit": "~0.18.0",
26
+ "astro-integration-kit": "~0.19.0",
27
27
  "content-type": "^1.0.5",
28
- "devalue": "^5.1.1",
29
- "@inox-tools/utils": "^0.6.0"
28
+ "devalue": "^5.3.2",
29
+ "@inox-tools/utils": "^0.7.0"
30
30
  },
31
31
  "devDependencies": {
32
- "@playwright/test": "^1.54.1",
32
+ "@playwright/test": "^1.55.0",
33
33
  "@types/content-type": "^1.1.9",
34
- "@types/node": "^24.0.15",
35
- "astro": "^5.12.0",
34
+ "@types/node": "^24.3.0",
35
+ "astro": "^5.13.3",
36
36
  "jest-extended": "^6.0.0",
37
37
  "tsup": "8.5.0",
38
- "typescript": "^5.8.3",
39
- "vite": "^7.0.5",
40
- "@inox-tools/astro-tests": "^0.7.0"
38
+ "typescript": "^5.9.2",
39
+ "vite": "^7.1.3",
40
+ "@inox-tools/astro-tests": "^0.8.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "astro": "^5"
package/src/plugin.ts CHANGED
@@ -29,7 +29,7 @@ export const plugin = (): Plugin => {
29
29
  const importPath = resolve('runtime', stateSource);
30
30
 
31
31
  return `
32
- export {setState, getState} from '${importPath}';
32
+ export {setState, getState, hasState} from '${importPath}';
33
33
  export {ServerStateLoaded} from '${resolve('events.js')}';
34
34
  `.trim();
35
35
  },
@@ -8,36 +8,76 @@ const revivers: Record<string, (value: any) => any> = {
8
8
  WellKnownSymbol: (value) => Symbol[value as keyof typeof Symbol],
9
9
  };
10
10
 
11
- const loadState = (doc: Document): State | undefined => {
12
- const element = doc.getElementById('it-astro-state');
11
+ const loadState = (doc: Document): State[] => {
12
+ const elements = Array.from(doc.querySelectorAll('script.it-astro-state'));
13
+ const scripts = elements
14
+ .map((element) => {
15
+ if (element?.textContent) {
16
+ const state = parse(element.textContent, revivers);
17
+ element.remove();
18
+ return state;
19
+ }
20
+ })
21
+ .filter((state): state is State => !!state);
22
+ return scripts;
23
+ };
24
+
25
+ let nextStates = loadState(document);
13
26
 
14
- if (element?.textContent) {
15
- const state = parse(element.textContent, revivers);
16
- element.remove();
17
- return state;
27
+ const initialiseState = (): State => {
28
+ const STATE_WINDOW_NAMESPACE = '__@it-astro:request-state-data' as const;
29
+ if (!(STATE_WINDOW_NAMESPACE in window)) {
30
+ window[STATE_WINDOW_NAMESPACE] = new Map();
18
31
  }
32
+ return window[STATE_WINDOW_NAMESPACE] as State;
19
33
  };
20
34
 
21
- let nextState = loadState(document);
22
- const state = new Map();
35
+ export const state = initialiseState();
23
36
 
24
- const applyState = () => {
25
- const event = new ServerStateLoaded(new Map(state), nextState ?? new Map());
26
-
27
- if (document.dispatchEvent(event)) {
28
- state.clear();
29
- for (const [key, value] of event.serverState.entries()) {
30
- state.set(key, value);
37
+ const mergeState = (oldState: State, newState: State) => {
38
+ for (const [newKey, newValue] of newState.entries()) {
39
+ const isHit = oldState.has(newKey);
40
+ if (isHit) {
41
+ continue;
31
42
  }
43
+ oldState.set(newKey, newValue);
32
44
  }
45
+ return oldState;
33
46
  };
34
47
 
35
- applyState();
48
+ const applyState =
49
+ (isViewTransition: boolean = false) =>
50
+ () => {
51
+ /*
52
+ If it's an uncancelled view transition, we want a completely new state
53
+ (otherwise the new view will 'lose' to the old view in the case of a conflict).
54
+ If it's not a view transition, we want to shallow clone the state object.
55
+ mergeState will update the top-level keys of the state passed to it, but if
56
+ the user calls preventDefault on the serverStateLoaded we're supposed to abort
57
+ the update. So it won't do to have already updated the underlying global state
58
+ map. And on the other hand we do a shallow, not deep, clone because the user
59
+ can mutate their own stored values any time they want.
60
+ */
61
+ const startingState = isViewTransition ? new Map() : new Map(state);
62
+ nextStates?.reduce(mergeState, startingState);
63
+ const event = new ServerStateLoaded(new Map(state), startingState, {
64
+ cancelable: true,
65
+ });
66
+
67
+ if (document.dispatchEvent(event)) {
68
+ state.clear();
69
+ for (const [key, value] of event.serverState.entries()) {
70
+ state.set(key, value);
71
+ }
72
+ }
73
+ };
74
+
75
+ applyState()();
36
76
 
37
77
  document.addEventListener('astro:before-swap', (event) => {
38
- nextState = loadState(event.newDocument);
78
+ nextStates = loadState(event.newDocument);
39
79
  });
40
- document.addEventListener('astro:after-swap', applyState);
80
+ document.addEventListener('astro:after-swap', applyState(true));
41
81
 
42
82
  export const getState = (key: string, valueIfMissing?: unknown): unknown => {
43
83
  if (!state.has(key)) {
@@ -48,6 +88,8 @@ export const getState = (key: string, valueIfMissing?: unknown): unknown => {
48
88
  return state.get(key);
49
89
  };
50
90
 
91
+ export const hasState = (key: string) => state.has(key);
92
+
51
93
  export const setState = (key: string, value: unknown): void => {
52
94
  if (value === undefined) {
53
95
  state.delete(key);
@@ -15,16 +15,19 @@ export const onRequest = defineMiddleware(async (_, next) => {
15
15
 
16
16
  const originalBody = await result.text();
17
17
 
18
- const headCloseIndex = originalBody.indexOf('</head>');
19
- if (headCloseIndex > -1) {
20
- const state = getState();
21
- if (state) {
22
- const stateScript = `<script id="it-astro-state" type="application/json+devalue">${state}</script>`;
23
-
18
+ const state = getState();
19
+ const stateScript = state
20
+ ? `<script class="it-astro-state" type="application/json+devalue">${state}</script>`
21
+ : null;
22
+ if (stateScript) {
23
+ const headCloseIndex = originalBody.indexOf('</head>');
24
+ if (headCloseIndex > -1) {
24
25
  return new Response(
25
26
  originalBody.slice(0, headCloseIndex) + stateScript + originalBody.slice(headCloseIndex),
26
27
  result
27
28
  );
29
+ } else {
30
+ return new Response(stateScript + originalBody, result);
28
31
  }
29
32
  }
30
33
 
@@ -17,6 +17,8 @@ export const getState = (key: string, valueIfMissing?: unknown): unknown => {
17
17
  return state.get(key);
18
18
  };
19
19
 
20
+ export const hasState = (key: string) => store.getStore()?.has(key) || false;
21
+
20
22
  export const setState = (key: string, value: unknown): void => {
21
23
  const state = store.getStore();
22
24
  if (value === undefined) {
package/virtual.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  declare module '@it-astro:state' {
2
2
  export const getState: (key: string, valueIfMissing?: unknown) => unknown;
3
+ export const hasState: (key: string) => boolean;
3
4
  export const setState: (key: string, value: unknown) => void;
4
5
 
5
6
  export { ServerStateLoaded } from './src/events.js';
@@ -9,4 +10,8 @@ declare global {
9
10
  interface DocumentEventMap {
10
11
  [ServerStateLoaded.NAME]: import('./src/events.js').ServerStateLoaded;
11
12
  }
13
+
14
+ interface Window {
15
+ '__@it-astro:request-state-data'?: import('./src/events.js').State;
16
+ }
12
17
  }