From 4e10ee5e8d4dc9e00280f203a9345bf1908c0c6c Mon Sep 17 00:00:00 2001 From: harlan Date: Fri, 31 Jan 2025 07:23:22 +1100 Subject: [PATCH 1/3] doc: tidy up --- docs/0.nuxt/guides/1.components.md | 36 ++++++++++++++++++------------ docs/0.vue/1.installation.md | 27 ++++++++++++---------- docs/0.vue/guides/1.components.md | 10 +++++++-- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/docs/0.nuxt/guides/1.components.md b/docs/0.nuxt/guides/1.components.md index c9193082..3515972e 100644 --- a/docs/0.nuxt/guides/1.components.md +++ b/docs/0.nuxt/guides/1.components.md @@ -1,27 +1,35 @@ --- -title: Component -description: Use the component to manage your head tags. +title: Components +description: Use component to manage your head tags. navigation: - title: ' Component' + title: 'Components' --- -The Unhead Vue package exports a ``{lang="html"} component that can be used to manage your head tags. +## Introduction -While it's recommended to use the `useHead` composable as it offers a more flexible API with full TypeScript support, -the ``{lang="html"} component may make more sense for your project. +Nuxt exports several Vue components that can be used to manage your head tags. -The component will takes any child elements that you would normally put in your actual ``{lang="html"} and renders them -with Unhead. +While it's recommended to use the `useHead()`{lang="ts"} composable as it offers a more flexible API with full TypeScript support, +the Vue component may make more sense for your project. + +## Usage + +For full usage instructions please refer to the [Nuxt documentation](https://nuxt.com/docs/getting-started/seo-meta#components). ```vue - ``` diff --git a/docs/0.vue/1.installation.md b/docs/0.vue/1.installation.md index 9f8fc1f0..df38f19e 100644 --- a/docs/0.vue/1.installation.md +++ b/docs/0.vue/1.installation.md @@ -20,19 +20,21 @@ Using [Nuxt](https://nuxt.com/docs/getting-started/seo-meta)? Unhead is already ### Demos -- [StackBlitz - Vite - Vue SPA](https://stackblitz.com/edit/vitejs-vite-uijgqa?file=package.json) +- [StackBlitz - Unhead - Vite + Vue SSR](https://stackblitz.com/edit/github-uesntxts) +- [StackBlitz - Unhead - Vue SPA](https://stackblitz.com/edit/github-uesntxts) ## Setup ### 1. Add Dependency -Install `@unhead/vue`{lang="bash"} dependency to your project: +Install `@unhead/vue`{lang="bash"} dependency to your project. The `next` tag is for v2 of Unhead. -:ModuleInstall{name="@unhead/vue"} +:ModuleInstall{name="@unhead/vue@next"} #### Using Vue 2 -In Unhead v2, official support for Vue 2 has been dropped. If you're using Vue 2, you will need to install v1 of `@unhead/vue@^1`{lang="bash"} instead. +In Unhead v2, official support for Vue 2 has been dropped. If you're using Vue 2, you will need to install v1 of `@unhead/vue@^1`{lang="bash"} instead +and follow the [v1 documentation](/docs/0.vue/1.installation-v1). :ModuleInstall{name="@unhead/vue@^1"} @@ -87,18 +89,16 @@ export async function render(_url: string) { Next we'll update our template `index.html`{lang="bash"} file. We'll be removing any head tags that should be dynamic and replacing them with placeholders. -```html {1,3,5,7} [./index.html] +```html {1,3,5,7} [index.html] - > + - + - > - +
- ``` @@ -106,7 +106,8 @@ and replacing them with placeholders. Now we need to render out the head tags _after_ vue has rendered the app and replace these placeholders with the actual head tags. -To start with we need to modify the `render` function to return the template data. +To start with we need to modify the `render` function to return the template data. Make +sure you add the `renderSSRHead` import. ```ts {1,14-15} [src/entry-server.ts] import { createHead, renderSSRHead } from '@unhead/vue/server' @@ -140,7 +141,9 @@ export async function createServer( // ... const [appHtml, templateData] = await render(url, manifest) // replace placeholders - let html = template.replace('', appHtml) + let html = template + .replace('', appHtml) + Object.entries(templateData).forEach(([key, value]) => { html = html.replace(``{lang="html"}, value) }) diff --git a/docs/0.vue/guides/1.components.md b/docs/0.vue/guides/1.components.md index c9193082..8fdf1934 100644 --- a/docs/0.vue/guides/1.components.md +++ b/docs/0.vue/guides/1.components.md @@ -5,12 +5,16 @@ navigation: title: ' Component' --- +## Introduction + The Unhead Vue package exports a ``{lang="html"} component that can be used to manage your head tags. -While it's recommended to use the `useHead` composable as it offers a more flexible API with full TypeScript support, +While it's recommended to use the `useHead()`{lang="ts"} composable as it offers a more flexible API with full TypeScript support, the ``{lang="html"} component may make more sense for your project. -The component will takes any child elements that you would normally put in your actual ``{lang="html"} and renders them +## Usage + +The component will takes any child elements that you would normally put in your actual ``{lang="html"} and renders them with Unhead. ```vue @@ -25,3 +29,5 @@ import { Head } from '@unhead/vue/components' ``` + +When the head component is unmounted, it will remove all the tags that were added. From b22fc890b9415bfb863bc3c80d6cac7f1821323d Mon Sep 17 00:00:00 2001 From: harlan Date: Fri, 31 Jan 2025 07:37:16 +1100 Subject: [PATCH 2/3] feat: @unhead/solid --- examples/vite-ssr-solid/index.html | 14 + examples/vite-ssr-solid/package.json | 24 + examples/vite-ssr-solid/server.js | 74 +++ examples/vite-ssr-solid/src/App.css | 26 + examples/vite-ssr-solid/src/App.tsx | 34 ++ examples/vite-ssr-solid/src/assets/solid.svg | 1 + examples/vite-ssr-solid/src/entry-client.tsx | 12 + examples/vite-ssr-solid/src/entry-server.tsx | 9 + examples/vite-ssr-solid/src/index.css | 69 +++ examples/vite-ssr-solid/src/vite-env.d.ts | 1 + examples/vite-ssr-solid/tsconfig.json | 27 + examples/vite-ssr-solid/tsconfig.node.json | 24 + examples/vite-ssr-solid/vite.config.ts | 7 + pnpm-lock.yaml | 514 ++++++++++++++++--- 14 files changed, 771 insertions(+), 65 deletions(-) create mode 100644 examples/vite-ssr-solid/index.html create mode 100644 examples/vite-ssr-solid/package.json create mode 100644 examples/vite-ssr-solid/server.js create mode 100644 examples/vite-ssr-solid/src/App.css create mode 100644 examples/vite-ssr-solid/src/App.tsx create mode 100644 examples/vite-ssr-solid/src/assets/solid.svg create mode 100644 examples/vite-ssr-solid/src/entry-client.tsx create mode 100644 examples/vite-ssr-solid/src/entry-server.tsx create mode 100644 examples/vite-ssr-solid/src/index.css create mode 100644 examples/vite-ssr-solid/src/vite-env.d.ts create mode 100644 examples/vite-ssr-solid/tsconfig.json create mode 100644 examples/vite-ssr-solid/tsconfig.node.json create mode 100644 examples/vite-ssr-solid/vite.config.ts diff --git a/examples/vite-ssr-solid/index.html b/examples/vite-ssr-solid/index.html new file mode 100644 index 00000000..2069499a --- /dev/null +++ b/examples/vite-ssr-solid/index.html @@ -0,0 +1,14 @@ + + + + + + + Vite + Solid + + + +
+ + + diff --git a/examples/vite-ssr-solid/package.json b/examples/vite-ssr-solid/package.json new file mode 100644 index 00000000..701c3226 --- /dev/null +++ b/examples/vite-ssr-solid/package.json @@ -0,0 +1,24 @@ +{ + "name": "vite-solid-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "node server", + "build": "npm run build:client && npm run build:server", + "build:client": "vite build --outDir dist/client", + "build:server": "vite build --ssr src/entry-server.jsx --outDir dist/server", + "preview": "cross-env NODE_ENV=production node server" + }, + "dependencies": { + "compression": "^1.7.5", + "express": "^5.0.1", + "sirv": "^3.0.0", + "solid-js": "^1.9.3" + }, + "devDependencies": { + "cross-env": "^7.0.3", + "vite": "^6.0.1", + "vite-plugin-solid": "^2.11.0" + } +} diff --git a/examples/vite-ssr-solid/server.js b/examples/vite-ssr-solid/server.js new file mode 100644 index 00000000..20a32f8d --- /dev/null +++ b/examples/vite-ssr-solid/server.js @@ -0,0 +1,74 @@ +import fs from 'node:fs/promises' +import express from 'express' +import { generateHydrationScript } from 'solid-js/web' + +// Constants +const isProduction = process.env.NODE_ENV === 'production' +const port = process.env.PORT || 5173 +const base = process.env.BASE || '/' + +// Cached production assets +const templateHtml = isProduction + ? await fs.readFile('./dist/client/index.html', 'utf-8') + : '' + +// Create http server +const app = express() + +// Add Vite or respective production middlewares +/** @type {import('vite').ViteDevServer | undefined} */ +let vite +if (!isProduction) { + const { createServer } = await import('vite') + vite = await createServer({ + server: { middlewareMode: true }, + appType: 'custom', + base, + }) + app.use(vite.middlewares) +} else { + const compression = (await import('compression')).default + const sirv = (await import('sirv')).default + app.use(compression()) + app.use(base, sirv('./dist/client', { extensions: [] })) +} + +// Serve HTML +app.use('*all', async (req, res) => { + try { + const url = req.originalUrl.replace(base, '') + + /** @type {string} */ + let template + /** @type {import('./src/entry-server.js').render} */ + let render + if (!isProduction) { + // Always read fresh template in development + template = await fs.readFile('./index.html', 'utf-8') + template = await vite.transformIndexHtml(url, template) + render = (await vite.ssrLoadModule('/src/entry-server.tsx')).render + } else { + template = templateHtml + render = (await import('./dist/server/entry-server.js')).render + } + + const rendered = await render(url) + + const head = (rendered.head ?? '') + generateHydrationScript() + + const html = template + .replace(``, head) + .replace(``, rendered.html ?? '') + + res.status(200).set({ 'Content-Type': 'text/html' }).send(html) + } catch (e) { + vite?.ssrFixStacktrace(e) + console.log(e.stack) + res.status(500).end(e.stack) + } +}) + +// Start http server +app.listen(port, () => { + console.log(`Server started at http://localhost:${port}`) +}) diff --git a/examples/vite-ssr-solid/src/App.css b/examples/vite-ssr-solid/src/App.css new file mode 100644 index 00000000..1565d406 --- /dev/null +++ b/examples/vite-ssr-solid/src/App.css @@ -0,0 +1,26 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.solid:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/examples/vite-ssr-solid/src/App.tsx b/examples/vite-ssr-solid/src/App.tsx new file mode 100644 index 00000000..b5722d3f --- /dev/null +++ b/examples/vite-ssr-solid/src/App.tsx @@ -0,0 +1,34 @@ +import './App.css' +import { createSignal } from 'solid-js' +import solidLogo from './assets/solid.svg' + +function App() { + const [count, setCount] = createSignal(0) + + return ( +
+
+ + + + + + +
+

Vite + Solid

+
+ +

+ Edit src/App.jsx and save to test HMR +

+
+

+ Click on the Vite and Solid logos to learn more +

+
+ ) +} + +export default App diff --git a/examples/vite-ssr-solid/src/assets/solid.svg b/examples/vite-ssr-solid/src/assets/solid.svg new file mode 100644 index 00000000..88498771 --- /dev/null +++ b/examples/vite-ssr-solid/src/assets/solid.svg @@ -0,0 +1 @@ + diff --git a/examples/vite-ssr-solid/src/entry-client.tsx b/examples/vite-ssr-solid/src/entry-client.tsx new file mode 100644 index 00000000..84e61637 --- /dev/null +++ b/examples/vite-ssr-solid/src/entry-client.tsx @@ -0,0 +1,12 @@ +/* @refresh reload */ +import './index.css' +import { hydrate } from 'solid-js/web' +import App from './App' +import { createHead } from 'unhead/client' +import { createContext, useContext } from 'solid-js'; + +hydrate(() => { + const head = createHead() + const context = createContext({ head }) + return +}, document.getElementById('root')) diff --git a/examples/vite-ssr-solid/src/entry-server.tsx b/examples/vite-ssr-solid/src/entry-server.tsx new file mode 100644 index 00000000..ac4cc65f --- /dev/null +++ b/examples/vite-ssr-solid/src/entry-server.tsx @@ -0,0 +1,9 @@ +import { renderToString } from 'solid-js/web' +import App from './App' +import { createHead } from 'unhead/server' + +export function render(_url: string) { + const head = createHead() + const html = renderToString(() => ) + return { html } +} diff --git a/examples/vite-ssr-solid/src/index.css b/examples/vite-ssr-solid/src/index.css new file mode 100644 index 00000000..2c3fac68 --- /dev/null +++ b/examples/vite-ssr-solid/src/index.css @@ -0,0 +1,69 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/examples/vite-ssr-solid/src/vite-env.d.ts b/examples/vite-ssr-solid/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/examples/vite-ssr-solid/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/vite-ssr-solid/tsconfig.json b/examples/vite-ssr-solid/tsconfig.json new file mode 100644 index 00000000..6713569d --- /dev/null +++ b/examples/vite-ssr-solid/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/examples/vite-ssr-solid/tsconfig.node.json b/examples/vite-ssr-solid/tsconfig.node.json new file mode 100644 index 00000000..71385d00 --- /dev/null +++ b/examples/vite-ssr-solid/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/vite-ssr-solid/vite.config.ts b/examples/vite-ssr-solid/vite.config.ts new file mode 100644 index 00000000..69fcc847 --- /dev/null +++ b/examples/vite-ssr-solid/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import solid from 'vite-plugin-solid' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [solid({ ssr: true })], +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23a54b69..4cef8acd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -156,6 +156,31 @@ importers: specifier: ^1.16.2 version: 1.16.2 + examples/vite-ssr-solid: + dependencies: + compression: + specifier: ^1.7.5 + version: 1.7.5 + express: + specifier: ^5.0.1 + version: 5.0.1 + sirv: + specifier: ^3.0.0 + version: 3.0.0 + solid-js: + specifier: ^1.9.3 + version: 1.9.4 + devDependencies: + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + vite: + specifier: ^6.0.1 + version: 6.0.11(@types/node@22.12.0)(jiti@2.4.2)(less@4.2.2)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.1)(yaml@2.7.0) + vite-plugin-solid: + specifier: ^2.11.0 + version: 2.11.0(solid-js@1.9.4)(vite@6.0.11(@types/node@22.12.0)(jiti@2.4.2)(less@4.2.2)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.1)(yaml@2.7.0)) + examples/vite-ssr-vue: dependencies: '@unhead/dom': @@ -728,6 +753,10 @@ packages: resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.18.6': + resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.25.9': resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} engines: {node: '>=6.9.0'} @@ -2102,6 +2131,9 @@ packages: resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@polka/url@1.0.0-next.28': + resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} + '@rollup/plugin-alias@5.1.1': resolution: {integrity: sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==} engines: {node: '>=14.0.0'} @@ -2692,6 +2724,10 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2811,6 +2847,9 @@ packages: array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + array-flatten@3.0.0: + resolution: {integrity: sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -2836,6 +2875,11 @@ packages: '@babel/core': ^7.12.0 webpack: '>=5' + babel-plugin-jsx-dom-expressions@0.39.6: + resolution: {integrity: sha512-HMkTn5A3NyydEgG7HKmm48YcnsQQyqeT6SKNWh2TrS6nn5rOLeHDfg5hPbrRUCFUqaT9WGn5NInQfMc3qne3Dg==} + peerDependencies: + '@babel/core': ^7.20.12 + babel-plugin-polyfill-corejs2@0.4.12: resolution: {integrity: sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==} peerDependencies: @@ -2851,6 +2895,11 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + babel-preset-solid@1.9.3: + resolution: {integrity: sha512-jvlx5wDp8s+bEF9sGFw/84SInXOA51ttkUEroQziKMbxplXThVKt83qB6bDTa1HuLNatdU9FHpFOiQWs1tLQIg==} + peerDependencies: + '@babel/core': ^7.0.0 + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -2882,6 +2931,10 @@ packages: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + body-parser@2.0.2: + resolution: {integrity: sha512-SNMk0OONlQ01uk8EPeiBvTW7W4ovpL5b1O3t1sjpPgfxOQ6BqQJ6XjxinDPR79Z6HdcD5zBBwr5ssiTlgdNztQ==} + engines: {node: '>=18'} + bonjour-service@1.3.0: resolution: {integrity: sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==} @@ -3131,6 +3184,10 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} @@ -3144,6 +3201,10 @@ packages: cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + cookie@0.7.1: resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} @@ -3184,6 +3245,11 @@ packages: typescript: optional: true + cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -3277,6 +3343,14 @@ packages: supports-color: optional: true + debug@3.1.0: + resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -3285,6 +3359,15 @@ packages: supports-color: optional: true + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.3.7: resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} @@ -3499,10 +3582,6 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -3779,6 +3858,10 @@ packages: resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} + express@5.0.1: + resolution: {integrity: sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==} + engines: {node: '>= 18'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -3837,6 +3920,10 @@ packages: resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} + finalhandler@2.0.0: + resolution: {integrity: sha512-MX6Zo2adDViYh+GcxxB1dpO43eypOGUOL12rLCOTMQv/DfIbpSJUy4oQIIZhVZkH9e+bZWKMon0XHFEju16tkQ==} + engines: {node: '>= 0.8'} + find-cache-dir@3.3.2: resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} engines: {node: '>=8'} @@ -3900,6 +3987,10 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fs-extra@11.3.0: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} @@ -3939,10 +4030,6 @@ packages: resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} engines: {node: '>=18'} - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} - get-intrinsic@1.2.7: resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} engines: {node: '>= 0.4'} @@ -4001,9 +4088,6 @@ packages: resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} engines: {node: '>=18'} - gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -4024,14 +4108,6 @@ packages: has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} - - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -4065,6 +4141,9 @@ packages: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} + html-entities@2.3.3: + resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} + html-tags@3.3.1: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} @@ -4126,6 +4205,10 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + iconv-lite@0.5.2: + resolution: {integrity: sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==} + engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -4279,6 +4362,9 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -4297,6 +4383,10 @@ packages: is-what@3.14.1: resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + is-wsl@3.1.0: resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} engines: {node: '>=16'} @@ -4644,13 +4734,25 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + memfs@4.17.0: resolution: {integrity: sha512-4eirfZ7thblFmqFjywlTmuWVSvccHAJbn1r8qQLzmTO11qcqpohOjmY2mFce6x7x7WtskzRqApPD0hv+Oa74jg==} engines: {node: '>= 4.0.0'} + merge-anything@5.1.7: + resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} + engines: {node: '>=12.13'} + merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -4768,6 +4870,10 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime-types@3.0.0: + resolution: {integrity: sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==} + engines: {node: '>= 0.6'} + mime@1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} @@ -4895,6 +5001,9 @@ packages: ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -5235,6 +5344,10 @@ packages: path-to-regexp@0.1.12: resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + path-type@5.0.0: resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} engines: {node: '>=12'} @@ -5635,6 +5748,10 @@ packages: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + rc9@2.1.2: resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} @@ -5815,6 +5932,10 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + router@2.0.0: + resolution: {integrity: sha512-dIM5zVoG8xhC6rnSN8uoAgFARwTE7BQs8YwHEvK0VCmfxQXMaOuA1uiR1IPwsW7JyK5iTt7Od/TC9StasS2NPQ==} + engines: {node: '>= 0.10'} + rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} @@ -5921,9 +6042,23 @@ packages: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} + send@1.1.0: + resolution: {integrity: sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==} + engines: {node: '>= 18'} + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + seroval-plugins@1.2.0: + resolution: {integrity: sha512-hULTbfzSe81jGWLH8TAJjkEvw6JWMqOo9Uq+4V4vg+HNq53hyHldM9ZOfjdzokcFysiTp9aFdV2vJpZFqKeDjQ==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.2.0: + resolution: {integrity: sha512-GURoU99ko2UiAgUC3qDCk59Jb3Ss4Po8VIMGkG8j5PFo2Q7y0YSMP8QG9NuL/fJCoTz9V1XZUbpNIMXPOfaGpA==} + engines: {node: '>=10'} + serve-index@1.9.1: resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} engines: {node: '>= 0.8.0'} @@ -5932,6 +6067,10 @@ packages: resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} + serve-static@2.1.0: + resolution: {integrity: sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==} + engines: {node: '>= 18'} + set-cookie-parser@2.7.1: resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} @@ -5978,6 +6117,10 @@ packages: resolution: {integrity: sha512-PHMifhh3EN4loMcHCz6l3v/luzgT3za+9f8subGgeMNjbJjzH4Ij/YoX3Gvu+kaouJRIlVdTHHCREADYf+ZteA==} engines: {node: ^18.17.0 || >=20.5.0} + sirv@3.0.0: + resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} + engines: {node: '>=18'} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -6022,6 +6165,14 @@ packages: resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + solid-js@1.9.4: + resolution: {integrity: sha512-ipQl8FJ31bFUoBNScDQTG3BjN6+9Rg+Q+f10bUbnO6EOTTf5NGerJeHc7wyu5I4RMHEl/WwZwUmy/PTRgxxZ8g==} + + solid-refresh@0.6.3: + resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==} + peerDependencies: + solid-js: ^1.3 + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -6286,6 +6437,10 @@ packages: resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + tough-cookie@5.0.0: resolution: {integrity: sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==} engines: {node: '>=16'} @@ -6352,6 +6507,10 @@ packages: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} + type-is@2.0.0: + resolution: {integrity: sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==} + engines: {node: '>= 0.6'} + typed-assert@1.0.9: resolution: {integrity: sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==} @@ -6506,6 +6665,9 @@ packages: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true + validate-html-nesting@1.2.2: + resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==} + validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -6522,6 +6684,16 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true + vite-plugin-solid@2.11.0: + resolution: {integrity: sha512-G+NiwDj4EAeUE0wt3Ur9f+Lt9oMUuLd0FIxYuqwJSqRacKQRteCwUFzNy8zMEt88xWokngQhiFjfJMhjc1fDXw==} + peerDependencies: + '@testing-library/jest-dom': ^5.16.6 || ^5.17.0 || ^6.* + solid-js: ^1.7.2 + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + '@testing-library/jest-dom': + optional: true + vite@6.0.11: resolution: {integrity: sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -6562,6 +6734,14 @@ packages: yaml: optional: true + vitefu@1.0.5: + resolution: {integrity: sha512-h4Vflt9gxODPFNGPwp4zAMZRpZR7eslzwH2c5hn5kNZ5rhnKyRJ50U+yGCdc2IRaBs8O4haIgLNGrV5CrpMsCA==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + vite: + optional: true + vitest@3.0.4: resolution: {integrity: sha512-6XG8oTKy2gnJIFTHP6LD7ExFeNLxiTkK3CfMvT7IfR8IN+BYICCf0lXUQmX7i7JoxUP8QmeP4mTnWXgflu4yjw==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -7316,6 +7496,10 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-imports@7.18.6': + dependencies: + '@babel/types': 7.26.7 + '@babel/helper-module-imports@7.25.9': dependencies: '@babel/traverse': 7.25.9 @@ -8662,6 +8846,8 @@ snapshots: '@pkgr/core@0.1.1': {} + '@polka/url@1.0.0-next.28': {} + '@rollup/plugin-alias@5.1.1(rollup@4.30.1)': optionalDependencies: rollup: 4.30.1 @@ -8840,7 +9026,7 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.26.2 + '@babel/parser': 7.26.7 '@babel/types': 7.26.7 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 @@ -8852,7 +9038,7 @@ snapshots: '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.26.2 + '@babel/parser': 7.26.7 '@babel/types': 7.26.7 '@types/babel__traverse@7.20.6': @@ -9394,6 +9580,11 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 + accepts@2.0.0: + dependencies: + mime-types: 3.0.0 + negotiator: 1.0.0 + acorn-jsx@5.3.2(acorn@8.14.0): dependencies: acorn: 8.14.0 @@ -9492,6 +9683,8 @@ snapshots: array-flatten@1.1.1: {} + array-flatten@3.0.0: {} + assertion-error@2.0.1: {} ast-kit@1.3.2: @@ -9518,6 +9711,16 @@ snapshots: schema-utils: 4.3.0 webpack: 5.97.1(esbuild@0.24.2) + babel-plugin-jsx-dom-expressions@0.39.6(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.18.6 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.26.0) + '@babel/types': 7.26.7 + html-entities: 2.3.3 + parse5: 7.2.1 + validate-html-nesting: 1.2.2 + babel-plugin-polyfill-corejs2@0.4.12(@babel/core@7.26.0): dependencies: '@babel/compat-data': 7.26.2 @@ -9542,6 +9745,11 @@ snapshots: transitivePeerDependencies: - supports-color + babel-preset-solid@1.9.3(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + babel-plugin-jsx-dom-expressions: 0.39.6(@babel/core@7.26.0) + balanced-match@1.0.2: {} base64-js@1.5.1: {} @@ -9589,6 +9797,21 @@ snapshots: transitivePeerDependencies: - supports-color + body-parser@2.0.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 3.1.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.5.2 + on-finished: 2.4.1 + qs: 6.13.0 + raw-body: 3.0.0 + type-is: 1.6.18 + transitivePeerDependencies: + - supports-color + bonjour-service@1.3.0: dependencies: fast-deep-equal: 3.1.3 @@ -9684,14 +9907,13 @@ snapshots: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - optional: true call-bind@1.0.7: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 + get-intrinsic: 1.2.7 set-function-length: 1.2.2 call-bound@1.0.3: @@ -9874,6 +10096,10 @@ snapshots: dependencies: safe-buffer: 5.2.1 + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + content-type@1.0.5: {} convert-source-map@1.9.0: {} @@ -9882,6 +10108,8 @@ snapshots: cookie-signature@1.0.6: {} + cookie-signature@1.2.2: {} + cookie@0.7.1: {} cookie@0.7.2: @@ -9924,6 +10152,10 @@ snapshots: optionalDependencies: typescript: 5.7.3 + cross-env@7.0.3: + dependencies: + cross-spawn: 7.0.6 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -10042,10 +10274,18 @@ snapshots: dependencies: ms: 2.0.0 + debug@3.1.0: + dependencies: + ms: 2.0.0 + debug@3.2.7: dependencies: ms: 2.1.3 + debug@4.3.6: + dependencies: + ms: 2.1.2 + debug@4.3.7: dependencies: ms: 2.1.3 @@ -10080,9 +10320,9 @@ snapshots: define-data-property@1.1.4: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 es-errors: 1.3.0 - gopd: 1.0.1 + gopd: 1.2.0 define-lazy-prop@3.0.0: {} @@ -10166,7 +10406,6 @@ snapshots: call-bind-apply-helpers: 1.0.1 es-errors: 1.3.0 gopd: 1.2.0 - optional: true eastasianwidth@0.2.0: {} @@ -10241,12 +10480,7 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 - - es-define-property@1.0.1: - optional: true + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -10255,7 +10489,6 @@ snapshots: es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 - optional: true esbuild-wasm@0.24.2: {} @@ -10683,6 +10916,43 @@ snapshots: transitivePeerDependencies: - supports-color + express@5.0.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.0.2 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.2.2 + debug: 4.3.6 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.0.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + methods: 1.1.2 + mime-types: 3.0.0 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.13.0 + range-parser: 1.2.1 + router: 2.0.0 + safe-buffer: 5.2.1 + send: 1.1.0 + serve-static: 2.1.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 2.0.0 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + extend@3.0.2: optional: true @@ -10761,6 +11031,18 @@ snapshots: transitivePeerDependencies: - supports-color + finalhandler@2.0.0: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + find-cache-dir@3.3.2: dependencies: commondir: 1.0.1 @@ -10819,6 +11101,8 @@ snapshots: fresh@0.5.2: {} + fresh@2.0.0: {} + fs-extra@11.3.0: dependencies: graceful-fs: 4.2.11 @@ -10854,14 +11138,6 @@ snapshots: get-east-asian-width@1.3.0: {} - get-intrinsic@1.2.4: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - get-intrinsic@1.2.7: dependencies: call-bind-apply-helpers: 1.0.1 @@ -10874,13 +11150,11 @@ snapshots: has-symbols: 1.1.0 hasown: 2.0.2 math-intrinsics: 1.1.0 - optional: true get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - optional: true get-stream@8.0.1: {} @@ -10947,12 +11221,7 @@ snapshots: slash: 5.1.0 unicorn-magic: 0.1.0 - gopd@1.0.1: - dependencies: - get-intrinsic: 1.2.4 - - gopd@1.2.0: - optional: true + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -10964,14 +11233,9 @@ snapshots: has-property-descriptors@1.0.2: dependencies: - es-define-property: 1.0.0 - - has-proto@1.0.3: {} - - has-symbols@1.0.3: {} + es-define-property: 1.0.1 - has-symbols@1.1.0: - optional: true + has-symbols@1.1.0: {} has-tostringtag@1.0.2: dependencies: @@ -11004,6 +11268,8 @@ snapshots: dependencies: whatwg-encoding: 3.1.1 + html-entities@2.3.3: {} + html-tags@3.3.1: {} htmlparser2@9.1.0: @@ -11087,6 +11353,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.5.2: + dependencies: + safer-buffer: 2.1.2 + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -11201,6 +11471,8 @@ snapshots: is-potential-custom-element-name@1.0.1: {} + is-promise@4.0.0: {} + is-reference@1.2.1: dependencies: '@types/estree': 1.0.6 @@ -11219,6 +11491,8 @@ snapshots: is-what@3.14.1: {} + is-what@4.1.16: {} + is-wsl@3.1.0: dependencies: is-inside-container: 1.0.0 @@ -11592,8 +11866,7 @@ snapshots: markdown-table@3.0.3: {} - math-intrinsics@1.1.0: - optional: true + math-intrinsics@1.1.0: {} mdast-util-find-and-replace@3.0.1: dependencies: @@ -11703,6 +11976,8 @@ snapshots: media-typer@0.3.0: {} + media-typer@1.1.0: {} + memfs@4.17.0: dependencies: '@jsonjoy.com/json-pack': 1.1.1(tslib@2.8.1) @@ -11710,8 +11985,14 @@ snapshots: tree-dump: 1.0.2(tslib@2.8.1) tslib: 2.8.1 + merge-anything@5.1.7: + dependencies: + is-what: 4.1.16 + merge-descriptors@1.0.3: {} + merge-descriptors@2.0.0: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -11930,6 +12211,10 @@ snapshots: dependencies: mime-db: 1.52.0 + mime-types@3.0.0: + dependencies: + mime-db: 1.53.0 + mime@1.6.0: {} mime@2.6.0: @@ -12045,6 +12330,8 @@ snapshots: ms@2.0.0: {} + ms@2.1.2: {} + ms@2.1.3: {} msgpackr-extract@3.0.3: @@ -12265,7 +12552,6 @@ snapshots: once@1.4.0: dependencies: wrappy: 1.0.2 - optional: true onetime@5.1.2: dependencies: @@ -12438,6 +12724,8 @@ snapshots: path-to-regexp@0.1.12: {} + path-to-regexp@8.2.0: {} + path-type@5.0.0: {} pathe@1.1.2: {} @@ -12802,6 +13090,13 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + rc9@2.1.2: dependencies: defu: 6.1.4 @@ -13008,6 +13303,16 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.30.1 fsevents: 2.3.3 + router@2.0.0: + dependencies: + array-flatten: 3.0.0 + is-promise: 4.0.0 + methods: 1.1.2 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + setprototypeof: 1.2.0 + utils-merge: 1.0.1 + rrweb-cssom@0.8.0: {} run-applescript@7.0.0: {} @@ -13117,10 +13422,33 @@ snapshots: transitivePeerDependencies: - supports-color + send@1.1.0: + dependencies: + debug: 4.4.0 + destroy: 1.2.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime-types: 2.1.35 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 + seroval-plugins@1.2.0(seroval@1.2.0): + dependencies: + seroval: 1.2.0 + + seroval@1.2.0: {} + serve-index@1.9.1: dependencies: accepts: 1.3.8 @@ -13142,6 +13470,15 @@ snapshots: transitivePeerDependencies: - supports-color + serve-static@2.1.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.1.0 + transitivePeerDependencies: + - supports-color + set-cookie-parser@2.7.1: {} set-function-length@1.2.2: @@ -13149,8 +13486,8 @@ snapshots: define-data-property: 1.1.4 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 + get-intrinsic: 1.2.7 + gopd: 1.2.0 has-property-descriptors: 1.0.2 setprototypeof@1.1.0: {} @@ -13173,7 +13510,7 @@ snapshots: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 - get-intrinsic: 1.2.4 + get-intrinsic: 1.2.7 object-inspect: 1.13.2 siginfo@2.0.0: {} @@ -13193,6 +13530,12 @@ snapshots: transitivePeerDependencies: - supports-color + sirv@3.0.0: + dependencies: + '@polka/url': 1.0.0-next.28 + mrmime: 2.0.0 + totalist: 3.0.1 + sisteransi@1.0.5: {} slash@5.1.0: {} @@ -13263,6 +13606,21 @@ snapshots: ip-address: 9.0.5 smart-buffer: 4.2.0 + solid-js@1.9.4: + dependencies: + csstype: 3.1.3 + seroval: 1.2.0 + seroval-plugins: 1.2.0(seroval@1.2.0) + + solid-refresh@0.6.3(solid-js@1.9.4): + dependencies: + '@babel/generator': 7.26.3 + '@babel/helper-module-imports': 7.25.9 + '@babel/types': 7.26.7 + solid-js: 1.9.4 + transitivePeerDependencies: + - supports-color + source-map-js@1.2.1: {} source-map-loader@5.0.0(webpack@5.97.1(esbuild@0.24.2)): @@ -13568,6 +13926,8 @@ snapshots: dependencies: eslint-visitor-keys: 3.4.3 + totalist@3.0.1: {} + tough-cookie@5.0.0: dependencies: tldts: 6.1.48 @@ -13626,6 +13986,12 @@ snapshots: media-typer: 0.3.0 mime-types: 2.1.35 + type-is@2.0.0: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.0 + typed-assert@1.0.9: {} typescript@5.7.3: {} @@ -13851,6 +14217,8 @@ snapshots: uuid@8.3.2: {} + validate-html-nesting@1.2.2: {} + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 @@ -13881,6 +14249,19 @@ snapshots: - tsx - yaml + vite-plugin-solid@2.11.0(solid-js@1.9.4)(vite@6.0.11(@types/node@22.12.0)(jiti@2.4.2)(less@4.2.2)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.1)(yaml@2.7.0)): + dependencies: + '@babel/core': 7.26.0 + '@types/babel__core': 7.20.5 + babel-preset-solid: 1.9.3(@babel/core@7.26.0) + merge-anything: 5.1.7 + solid-js: 1.9.4 + solid-refresh: 0.6.3(solid-js@1.9.4) + vite: 6.0.11(@types/node@22.12.0)(jiti@2.4.2)(less@4.2.2)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.1)(yaml@2.7.0) + vitefu: 1.0.5(vite@6.0.11(@types/node@22.12.0)(jiti@2.4.2)(less@4.2.2)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.1)(yaml@2.7.0)) + transitivePeerDependencies: + - supports-color + vite@6.0.11(@types/node@22.12.0)(jiti@2.4.2)(less@4.2.1)(sass@1.83.1)(terser@5.37.0)(tsx@4.19.1)(yaml@2.7.0): dependencies: esbuild: 0.24.2 @@ -13911,6 +14292,10 @@ snapshots: tsx: 4.19.1 yaml: 2.7.0 + vitefu@1.0.5(vite@6.0.11(@types/node@22.12.0)(jiti@2.4.2)(less@4.2.2)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.1)(yaml@2.7.0)): + optionalDependencies: + vite: 6.0.11(@types/node@22.12.0)(jiti@2.4.2)(less@4.2.2)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.1)(yaml@2.7.0) + vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.12.0)(jiti@2.4.2)(jsdom@26.0.0)(less@4.2.2)(sass@1.83.4)(terser@5.37.0)(tsx@4.19.1)(yaml@2.7.0): dependencies: '@vitest/expect': 3.0.4 @@ -14172,8 +14557,7 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.1.0 - wrappy@1.0.2: - optional: true + wrappy@1.0.2: {} ws@8.17.1: optional: true From 3c32f696721d2f83a32da1983ee6c802fa7129b8 Mon Sep 17 00:00:00 2001 From: harlan Date: Sat, 1 Feb 2025 16:42:00 +1100 Subject: [PATCH 3/3] doc: sync script doc --- docs/scripts/0.introduction.md | 33 ---- docs/scripts/0.nuxt/1.installation.md | 156 +-------------- docs/scripts/0.typescript/1.installation.md | 183 ++---------------- docs/scripts/0.vue/1.installation.md | 161 ++------------- .../1.guides/{privacy.md => 0.defaults.md} | 17 +- docs/scripts/1.guides/0.key-concepts.md | 81 -------- docs/scripts/1.guides/3.page-events.md | 33 ---- docs/scripts/1.guides/4.global.md | 127 ------------ docs/scripts/1.guides/migrating.md | 106 ---------- docs/scripts/1.guides/proxy.md | 54 ++++-- 10 files changed, 93 insertions(+), 858 deletions(-) rename docs/scripts/1.guides/{privacy.md => 0.defaults.md} (66%) delete mode 100644 docs/scripts/1.guides/0.key-concepts.md delete mode 100644 docs/scripts/1.guides/3.page-events.md delete mode 100644 docs/scripts/1.guides/4.global.md delete mode 100644 docs/scripts/1.guides/migrating.md diff --git a/docs/scripts/0.introduction.md b/docs/scripts/0.introduction.md index 17e062a0..9d043c57 100644 --- a/docs/scripts/0.introduction.md +++ b/docs/scripts/0.introduction.md @@ -62,36 +62,3 @@ Source: [HTTP Archive's Web Almanac](https://almanac.httparchive.org/en/2023/per - [Add Types to Scripts →](/unhead/scripts/typed-scripts) - [Control Loading →](/unhead/scripts/load-triggers) - [Handle Errors →](/unhead/scripts/load-failures) - ---- - -title: Introduction to Unhead -description: Manage page metadata for modern JavaScript applications -navigation: -title: Introduction ---- - -## What is head manager? - -Unhead is built for modern JavaScript applications that need to render code outside their `
`{lang="html"} entry in both a -server-rendered and client-rendered environment. - -JavaScript applications need different metadata for each page or view. Without proper management, applications encounter several issues: - -Pages show incorrect titles and descriptions during navigation. Social media previews display wrong information. Search engines receive duplicate or conflicting meta tags. Server-rendered pages break when JavaScript takes over in the browser. - -These issues affect SEO, social sharing, and user experience. They become more significant in server-rendered applications where metadata must work correctly during the initial page load and subsequent JavaScript updates. - -```html - - - - - - - -
- - - -``` diff --git a/docs/scripts/0.nuxt/1.installation.md b/docs/scripts/0.nuxt/1.installation.md index c6fbd7f0..49520b6d 100644 --- a/docs/scripts/0.nuxt/1.installation.md +++ b/docs/scripts/0.nuxt/1.installation.md @@ -1,165 +1,23 @@ --- -title: Installing Unhead with Vue -description: Learn how to start using Unhead with Vue. +title: 'Install Unhead Scripts on Nuxt Projects' +description: 'Get started with Unhead Scripts by installing the dependency to your project.' navigation: title: 'Installation' --- ## Introduction -Unhead is the official head management library for Vue. It provides a simple and intuitive API for managing the head of your application, including the title, meta tags, and other elements that are important for SEO and user experience. +To use Unhead Scripts with Nuxt, you need to install the Nuxt Scripts module. ## Setup -1. Install `@unhead/vue` dependency to your project: - -::code-group - -```bash [yarn] -yarn add @unhead/vue -``` - -```bash [npm] -npm install @unhead/vue -``` - -```bash [pnpm] -pnpm add @unhead/vue -``` - -:: - -If you're using Vue 2, you will need to install v1 of `@unhead/vue` instead. - -::code-group - -```bash [yarn] -yarn add @unhead/vue@^1 -``` - -```bash [npm] -npm install @unhead/vue@^1 -``` - -```bash [pnpm] -pnpm add @unhead/vue@^1 -``` - -:: - -### Demos - -- [StackBlitz - Vite - Vue SPA](https://stackblitz.com/edit/vitejs-vite-uijgqa?file=package.json) - -2. Register the Vue plugin: - -```ts [Vue 3] -import { createHead } from '@unhead/vue' -import { createApp } from 'vue' - -const app = createApp() - -const head = createHead() -app.use(head) - -app.mount('#app') -``` - -```ts [Vue 2] -// You must use v1 of Unhead for Vue 2 support -import { createHead } from '@unhead/vue' -import { UnheadPlugin } from '@unhead/vue/vue2' -import Vue from 'vue' - -const head = createHead() -Vue.use(UnheadPlugin) -``` - -:: - -3. Done! Now you can use the `useHead` composable to manage your head. - -::code-group - -```vue [useHead] - -``` - -```vue [Options API] - -``` - -:: - -## Optional: Auto-Imports - -Unhead provides out-of-the-box configuration for [unplugin-auto-import](https://github.com/antfu/unplugin-auto-import). - -```ts [vite.config.ts] -import { unheadVueComposablesImports } from '@unhead/vue' - -export default defineConfig({ - plugins: [ - AutoImport({ - imports: [ - unheadVueComposablesImports, - ], - }), - ] -}) -``` - -## Optional: Vue 3 Options API - -The options API functionality is only provided out-of-the-box for Vue 2, if you'd like to use in Vue 3 you will need to install the mixin. - -```ts -import { createHead, VueHeadMixin } from '@unhead/vue' -import { createApp } from 'vue' - -const app = createApp() -const head = createHead() -app.mixin(VueHeadMixin) -// ... -``` - -This key can either be a function or a plain object or reactive data. See [Reactivity](/setup/vue/how-it-works) for more details. - -```vue - - - -``` +Please follow the [installation documentation](https://scripts.nuxt.com/) to get started. ## Next Steps -Your Vue app is now setup for head management, congrats! 🎉 +Your Nuxt app is now serving basic Scripts, congrats! 🎉 Try next: -1. Optional: [Setup SSR](/setup/ssr/installation) -2. Explore the [Composables](/usage/composables/use-head) +1. Add some [recipes](/addons/recipes) +2. Consider using the [Vite plugin](/addons/vite-plugin) diff --git a/docs/scripts/0.typescript/1.installation.md b/docs/scripts/0.typescript/1.installation.md index e373f635..33db202c 100644 --- a/docs/scripts/0.typescript/1.installation.md +++ b/docs/scripts/0.typescript/1.installation.md @@ -1,183 +1,32 @@ --- -title: 'Install Unhead' -description: 'Get started with Unhead by installing the dependency to your project.' +title: 'Install Unhead Scripts on TypeScript Projects' +description: 'Get started with Unhead Scripts by installing the dependency to your project.' navigation: title: 'Installation' --- -Using :Icon{name="logos:vue"} Vue? Check out the [Vue integration](/setup/vue/installation). +## Introduction -Using :Icon{name="logos:nuxt-icon"} Nuxt? Unhead is already built-in! Check out the [Nuxt docs](https://nuxt.com/docs/getting-started/seo-meta). +Unhead is built for JavaScript applications that need to manage the head of their document in both server and client-rendered environments. -## Setup +This guide is for installing Unhead Scripts with just TypeScript and no framework. Using a JavaScript framework? Select your framework below or continue with the TypeScript setup below. -1. Install `unhead` dependency to your project: +:FrameworkSelector -::code-group +## 1. Install Dependency -```bash [yarn] -yarn add unhead -``` - -```bash [npm] -npm install unhead -``` - -```bash [pnpm] -pnpm add unhead -``` - -:: - -### Setup Client-Side Rendering (CSR) - -Create a head instance somewhere in your apps client entry. - -```ts [main.ts] -import { createHead } from 'unhead/client' - -// Create a global head instance -const head = createHead() -``` - -Done! Now you can use the `useHead` composable to manage your head. - -```ts -import { useHead } from 'unhead' - -useHead({ - title: 'My awesome site' -}) -``` - -### Setup Server-Side Rendering (SSR) - -If you app does not SSR, you can skip this step. +:ModuleInstall{name="@unhead/scripts"} -**1. Create a head instance** - -Somewhere in your server entry, create a head instance. - -```ts [main.ts] -import { createHead } from 'unhead/server' - -// Create a global head instance -const head = createHead() -``` +## 2. Use the composables -**2. Manage context** - -To make use of the `useHead()`{lang="ts"} composables, Unhead uses [unctx](https://github.com/unjs/unctx) under the hood which requires you to manually -set and unset the context when handling requests to avoid cross-request pollution. - -This is usually done by attaching setting the context as part of handling the request. +You now have access to the `useScript()`{lang="ts"} composable which can be used to manage your script tags. ```ts -import { unheadCtx } from 'unhead' -import { createHead } from 'unhead/server' - -function handleRequest(req, res) { - const head = createHead() - unheadCtx.set(head) - // Your SSR logic here - unheadCtx.unset() -} -``` - -**3. Generate SSR Payload** - -Once you're ready to start displaying tags on the server, you'll need to generate the SSR payload. - -```ts -import { renderSSRHead } from 'unhead/server' - -// head is from createHead() -const payload = await renderSSRHead(head) -``` - -The payload schema looks like the following: - -```ts -export interface SSRHeadPayload { - headTags: string - bodyTags: string - bodyTagsOpen: string - htmlAttrs: string - bodyAttrs: string -} -``` - -**4. Update your app template** - -You will need to update your app template to add in the templates for -the SSR tags. - -Different frameworks differ in how they handle this template. - -**Simple string replace** - -```html - -> - - - - - > - -
- - - - -``` - -To handle this type of template you can use this code - -```ts -const headPayload = await renderSSRHead(head) - -Object.entries(headPayload).forEach(([key, value]) => { - html = html.replace(``{lang="html"}, value) -}) -``` - -## 3. Done! How hydration works - -When your client-side app hydrates the server head tags, it will attempt to hydrate each -element based on the nodes being equal `$el.isEqualNode($newEl)` or them sharing the same -dedupe key (see [Tag Deduping](/usage/guides/handling-duplicates)). - -If you're rendering content that differs between client and server, you should -specify a `key` attribute if the element does not have a unique dedupe key. - -```ts -useHead({ - script: [ - { - // key is needed to avoid seperate scripts being created - key: 'my-script', - innerHTML: process.server ? '' : 'console.log("hello world")', - } - ] -}) -``` - -## Optional: Auto-Imports - -Unhead provides out-of-the-box configuration for [unplugin-auto-import](https://github.com/antfu/unplugin-auto-import). - -```ts [vite.config.ts] -import { unheadComposablesImports } from 'unhead' +import { useScript } from '@unhead/scripts' -export default defineConfig({ - plugins: [ - AutoImport({ - imports: [ - unheadComposablesImports[0], - ], - }), - ] +useScript(unhead, { + src: 'https://example.com/script.js', + async: true, }) ``` @@ -187,5 +36,5 @@ Your app is now setup for head management, congrats! 🎉 Try next: -1. Optional: [Setup SSR](/setup/ssr/installation) -2. Add some [recipes](/addons/recipes) +1. Add some [recipes](/addons/recipes) +2. Consider using the [Vite plugin](/addons/vite-plugin) diff --git a/docs/scripts/0.vue/1.installation.md b/docs/scripts/0.vue/1.installation.md index c6fbd7f0..d7176b1b 100644 --- a/docs/scripts/0.vue/1.installation.md +++ b/docs/scripts/0.vue/1.installation.md @@ -1,165 +1,40 @@ --- -title: Installing Unhead with Vue -description: Learn how to start using Unhead with Vue. +title: 'Install Unhead Scripts on Vue Projects' +description: 'Get started with Unhead Scripts by installing the dependency to your project.' navigation: title: 'Installation' --- ## Introduction -Unhead is the official head management library for Vue. It provides a simple and intuitive API for managing the head of your application, including the title, meta tags, and other elements that are important for SEO and user experience. +Unhead is built for JavaScript applications that need to manage the head of their document in both server and client-rendered environments. -## Setup +This guide is for installing Unhead Scripts with just Vue and no framework. Using a JavaScript framework? Select your framework below or continue with the Vue setup below. -1. Install `@unhead/vue` dependency to your project: +:FrameworkSelector -::code-group +## 1. Install Dependency -```bash [yarn] -yarn add @unhead/vue -``` - -```bash [npm] -npm install @unhead/vue -``` - -```bash [pnpm] -pnpm add @unhead/vue -``` - -:: - -If you're using Vue 2, you will need to install v1 of `@unhead/vue` instead. - -::code-group - -```bash [yarn] -yarn add @unhead/vue@^1 -``` - -```bash [npm] -npm install @unhead/vue@^1 -``` +:ModuleInstall{name="@unhead/scripts"} -```bash [pnpm] -pnpm add @unhead/vue@^1 -``` - -:: - -### Demos - -- [StackBlitz - Vite - Vue SPA](https://stackblitz.com/edit/vitejs-vite-uijgqa?file=package.json) - -2. Register the Vue plugin: - -```ts [Vue 3] -import { createHead } from '@unhead/vue' -import { createApp } from 'vue' +## 2. Use the composables -const app = createApp() - -const head = createHead() -app.use(head) - -app.mount('#app') -``` - -```ts [Vue 2] -// You must use v1 of Unhead for Vue 2 support -import { createHead } from '@unhead/vue' -import { UnheadPlugin } from '@unhead/vue/vue2' -import Vue from 'vue' - -const head = createHead() -Vue.use(UnheadPlugin) -``` - -:: - -3. Done! Now you can use the `useHead` composable to manage your head. - -::code-group - -```vue [useHead] - -``` - -```vue [Options API] - -``` - -:: - -## Optional: Auto-Imports - -Unhead provides out-of-the-box configuration for [unplugin-auto-import](https://github.com/antfu/unplugin-auto-import). - -```ts [vite.config.ts] -import { unheadVueComposablesImports } from '@unhead/vue' - -export default defineConfig({ - plugins: [ - AutoImport({ - imports: [ - unheadVueComposablesImports, - ], - }), - ] -}) -``` - -## Optional: Vue 3 Options API - -The options API functionality is only provided out-of-the-box for Vue 2, if you'd like to use in Vue 3 you will need to install the mixin. +You now have access to the `useScript()`{lang="ts"} composable which can be used to manage your script tags. ```ts -import { createHead, VueHeadMixin } from '@unhead/vue' -import { createApp } from 'vue' +import { useScript } from '@unhead/scripts/vue' -const app = createApp() -const head = createHead() -app.mixin(VueHeadMixin) -// ... -``` - -This key can either be a function or a plain object or reactive data. See [Reactivity](/setup/vue/how-it-works) for more details. - -```vue - - - +useScript({ + src: 'https://example.com/script.js', + async: true, +}) ``` -## Next Steps +### Next Steps -Your Vue app is now setup for head management, congrats! 🎉 +Your app is now setup for head management, congrats! 🎉 Try next: -1. Optional: [Setup SSR](/setup/ssr/installation) -2. Explore the [Composables](/usage/composables/use-head) +1. Add some [recipes](/addons/recipes) +2. Consider using the [Vite plugin](/addons/vite-plugin) diff --git a/docs/scripts/1.guides/privacy.md b/docs/scripts/1.guides/0.defaults.md similarity index 66% rename from docs/scripts/1.guides/privacy.md rename to docs/scripts/1.guides/0.defaults.md index d4b55381..a54b6c50 100644 --- a/docs/scripts/1.guides/privacy.md +++ b/docs/scripts/1.guides/0.defaults.md @@ -1,8 +1,23 @@ --- -title: "Respect User Privacy" +title: "Script Defaults" description: "Load third-party scripts without compromising user privacy" --- +## Introduction + + +Nuxt Scripts does not insert script tags within the SSR response. This is a performance decision to minimise the interruptions +to the hydration process. Instead, scripts are loaded by default when Nuxt is fully hydrated on the client side. + +You can change this behavior by modifying the [defaultScriptOptions](docs/api/nuxt-config#defaultscriptoptions). + +Nuxt Scripts will also insert several extra tags to the script element to help with performance and privacy. + +- `async` - Scripts are loaded asynchronously to prevent blocking the rendering of the page. +- `defer` - Scripts are deferred to ensure they are executed in the order they are loaded. +- `crossorigin="anonymous"` - Scripts are loaded with the `anonymous` attribute to prevent them from accessing cookies. +- `referrerpolicy="no-referrer"` - Scripts are loaded with the `no-referrer` policy to prevent them from sending the referrer header. + Third-party scripts collect user data. Do it ethically. ## Privacy Defaults diff --git a/docs/scripts/1.guides/0.key-concepts.md b/docs/scripts/1.guides/0.key-concepts.md deleted file mode 100644 index 43fa3cc0..00000000 --- a/docs/scripts/1.guides/0.key-concepts.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Key Concepts -description: Learn about the key concepts of Nuxt Scripts. ---- - -The [useScript](/docs/api/use-script) composable is the core of Nuxt Scripts and is used to load all scripts. - -There are additional layers of abstraction built on top of `useScript` to make it easier to load scripts in different ways. - -1. [Registry Scripts](/docs/guides/registry-scripts) - Preconfigured third-party scripts that can be loaded through Nuxt Config, composables and components. -2. [Global Scripts](/docs/guides/global) - Load scripts through your Nuxt Config file. - -## Unhead Abstraction - -The Nuxt Scripts `useScript` composable is an abstraction of Unhead's [useScript](https://unhead.unjs.io/usage/composables/use-script), which in turn is -an abstraction on top of [useHead](https://unhead.unjs.io/usage/composables/use-head). Many of the features available to you -through `useHead` are also available in Nuxt Scripts `useScript`. - -## Script Singleton - -With Nuxt Scripts, it's not possible to load a script with the same `src` (or `key`) multiple times. This is because the script is loaded globally and is shared across all components. - -This means that a script will only go through the initialization process once, and any subsequent calls to `useScript` will return the same instance. - -For this reason, you may consider wrapping your `useScript` calls in their own composable to allow for easier instantiation of the script. - -```ts [useMyScript.ts] -export function useMyScript() { - return useScript({ - src: 'https://example.com/script.js', - }) -} -``` - -## Default Behavior - -Nuxt Scripts does not insert script tags within the SSR response. This is a performance decision to minimise the interruptions -to the hydration process. Instead, scripts are loaded by default when Nuxt is fully hydrated on the client side. - -You can change this behavior by modifying the [defaultScriptOptions](docs/api/nuxt-config#defaultscriptoptions). - -Nuxt Scripts will also insert several extra tags to the script element to help with performance and privacy. - -- `async` - Scripts are loaded asynchronously to prevent blocking the rendering of the page. -- `defer` - Scripts are deferred to ensure they are executed in the order they are loaded. -- `crossorigin="anonymous"` - Scripts are loaded with the `anonymous` attribute to prevent them from accessing cookies. -- `referrerpolicy="no-referrer"` - Scripts are loaded with the `no-referrer` policy to prevent them from sending the referrer header. - -## Understanding proxied functions - -You may wonder how the `useScript` composable can return SSR safe functions that can be called before the script is loaded. - -```ts -const { proxy } = useScript('/script.js') -// just works as you'd expect - magic? -proxy.gtag('event', 'page_view') -``` - -The `gtag` function call is a proxy that queues the function to be called when the script is loaded. If -the script never loads then the function is never called. - -This has several benefits: - -- SSR safe -- Won't break your site if the script never loads (blocked by adblockers) -- Allows you to load the script whenever you want without worrying about the order of the script and function calls - -But it also has some downsides: - -- It only works for functions where you don't need the return value. You can await the function call to get the return value, but this will block the rendering of the page. -- It can be confusing to debug if you're not aware of how it works. - -It's recommended to await the script load if you want to access the script's API directly. - -```ts -const { onLoaded } = useScript('/script.js') -// use the script instance directly, not proxied -onLoaded(({ gtag }) => { - gtag('event', 'page_view') -}) -``` diff --git a/docs/scripts/1.guides/3.page-events.md b/docs/scripts/1.guides/3.page-events.md deleted file mode 100644 index c7dad1a9..00000000 --- a/docs/scripts/1.guides/3.page-events.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Script Event Page -description: Learn how to send page events to your analytics provider. ---- - -## Background - -When using tracking scripts, it's common to send an event when the page changes. Due to Nuxt's head implementation being -async, the page title is not always available on route change immediately. - -Nuxt Scripts provides the [useScriptEventPage](/docs/api/use-script-event-page) composable to solve this problem. - -See the [API](/docs/api/use-script-event-page) docs for full details on the available options. - -### Usage - -The composable works by providing a function that will be invoked whenever the page changes, providing the newly resolved -title and path. - -You can use this with any analytics provider where you're seeing the page title not being accurate on route change. - -```ts -const { proxy } = useScriptGoogleAnalytics() - -useScriptEventPage(({ title, path }) => { - // triggered on route change - proxy.gtag('event', 'page_view', { - page_title: title, - page_location: 'https://example.com', - page_path: path - }) -}) -``` diff --git a/docs/scripts/1.guides/4.global.md b/docs/scripts/1.guides/4.global.md deleted file mode 100644 index 0798cd39..00000000 --- a/docs/scripts/1.guides/4.global.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: Global Scripts -description: Load global third-party scripts and optimize them for your Nuxt app. ---- - -## Background - -While the `app.head` method in Nuxt Config allows for loading global scripts, it can be cumbersome and requires manual optimization. - -```ts -export default defineNuxtConfig({ - head: { - script: [ { src: 'https://analytics.com/tracker.js', async: true } ] - } -}) -``` - -A simpler method is now available: directly input the script URL into `scripts.globals`. You can also include optional settings to tailor the script loading process with specific optimizations in mind. - -You may consider using global scripts when: - -- The script isn't a supported [Registry Script](/docs/api/use-script#registry-script). -- You don't care about interacting with the API provided by the third-party script (e.g. you don't need to use `gtag` from Google Analytics). -- You are interacting with the API provided by the third-party script, but you don't care about type safety. - -Otherwise, it's recommended to use [useScript](/docs/api/use-script) to load scripts in a safer way. - -## Usage - -The `globals` key supports strings, objects and arrays. - -**Example: Load a script using just the src** - -```ts -export default defineNuxtConfig({ - scripts: { - globals: { - myScript: 'https://analytics.com/tracker.js', - } - } -}) -``` - -**Example: Load a script while providing extra script attributes** - -```ts -export default defineNuxtConfig({ - scripts: { - globals: { - myScript: { - src: 'https://example.com/script.js', - integrity: 'sha256-abc123', - } - } - } -}) -``` - -You can optionally provide the script as an array which allows you to provide [Script Options](/docs/api/use-script#NuxtUseScriptOptions). - -```ts -export default defineNuxtConfig({ - scripts: { - globals: { - myScript: [ - { src: 'https://example.com/script.js' }, - // load the script as part of the hydration process instead of on idle - { trigger: 'client' } - ] - } - } -}) -``` - -### Accessing a global script - -All Nuxt Scripts are registered on the `$scripts` Nuxt App property. - -For scripts registered through nuxt.config, type autocompletion is available. - -```vue - -``` - -## How it Works - -The `globals` configuration will be used to create a virtual Nuxt plugin that loads in the script using the `useScript` composable. - -As `useScript` is being used under the hood, it's important to understand the defaults and behavior of the [useScript](/api/use-script) composable.. - -::code-group - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - scripts: { - globals: { - tracker: 'https://analytics.com/tracker.js', - } - } -}) -``` - -```vue [components/Tracking.vue] - - - -``` - -:: diff --git a/docs/scripts/1.guides/migrating.md b/docs/scripts/1.guides/migrating.md deleted file mode 100644 index 216777bb..00000000 --- a/docs/scripts/1.guides/migrating.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -title: "Migration Guide" -description: "Move from other script loaders to Unhead Scripts" ---- - -Move from other script loaders to Unhead Scripts safely. - -## From Script Tags - -```html - - - - - -```ts -const analytics = useScript('analytics.js', { - beforeInit() { - window.analytics = window.analytics || [] - window.analytics.push(['init']) - } -}) -``` - -## From Vue-Meta - -```ts -// Before -metaInfo: { - script: [ - { src: 'widget.js', async: true }, - { innerHTML: 'widget.init()' } - ] -} - -// After -useScript('widget.js', { - beforeInit() { - widget.init() - } -}) -``` - -## From React Helmet - -```tsx -// Before - - - - -// After -useScript('chat.js', { - beforeInit() { - window.$crisp = [] - } -}) -``` - -## Common Issues - -### Script Order - -```ts -// Before: Scripts load in order - - -// After: Pass through beforeInit -useScript('app.js', { - beforeInit() { - window.CONFIG = { api: '/api' } - } -}) -``` - -## External Resources - -- [Script Loading Patterns](https://www.patterns.dev/vanilla/loading-javascript) -- [Vue Meta Migration](https://vue-meta.nuxtjs.org/migrating/from-v1) -- [React Helmet Alternative](https://github.com/nfl/react-helmet#migrate) - -## Next Steps - -- [Test Scripts →](/unhead/scripts/testing-scripts) -- [Monitor Performance →](/unhead/scripts/performance-monitoring) -- [Handle Errors →](/unhead/scripts/load-failures) diff --git a/docs/scripts/1.guides/proxy.md b/docs/scripts/1.guides/proxy.md index d3780c52..f79ee253 100644 --- a/docs/scripts/1.guides/proxy.md +++ b/docs/scripts/1.guides/proxy.md @@ -3,6 +3,40 @@ title: "Script Proxy API" description: "Use third-party script functions before they load" --- +## Introduction + +You may wonder how the `useScript()`{lang="ts"} composable can return SSR safe functions that can be called before the script is loaded. + +```ts +const { proxy } = useScript('/script.js') +// just works as you'd expect - magic? +proxy.gtag('event', 'page_view') +``` + +The `gtag` function call is a proxy that queues the function to be called when the script is loaded. If +the script never loads then the function is never called. + +This has several benefits: + +- SSR safe +- Won't break your site if the script never loads (blocked by adblockers) +- Allows you to load the script whenever you want without worrying about the order of the script and function calls + +But it also has some downsides: + +- It only works for functions where you don't need the return value. You can await the function call to get the return value, but this will block the rendering of the page. +- It can be confusing to debug if you're not aware of how it works. + +It's recommended to await the script load if you want to access the script's API directly. + +```ts +const { onLoaded } = useScript('/script.js') +// use the script instance directly, not proxied +onLoaded(({ gtag }) => { + gtag('event', 'page_view') +}) +``` + Use script functions before they load. All calls queue until the script is ready. ## Basic Usage @@ -18,11 +52,9 @@ const analytics = useScript('analytics.js', { analytics.proxy.trackEvent('page_view') ``` -## What to Proxy - ### Good Use Cases - ```ts +```ts // Fire and forget events analytics.proxy.event('click') @@ -46,9 +78,7 @@ const data = script.proxy.getData() // Always void await script.proxy.fetch() // Never resolves ``` -## Real Examples - -### Google Analytics +### Example: Google Analytics ```ts const ga = useScript('gtag.js', { @@ -76,15 +106,3 @@ const crisp = useScript('crisp.js', { // Safe to call anytime crisp.proxy.push(['do', 'chat:open']) ``` - -## External Resources - -- [JavaScript Proxy Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) -- [Command Pattern](https://www.patterns.dev/vanilla/command-pattern) -- [Queue Theory](https://web.dev/articles/network-connections) - -## Next Steps - -- [Respect Privacy →](/unhead/scripts/respecting-privacy) -- [Load Triggers →](/unhead/scripts/load-triggers) -- [Handle Errors →](/unhead/scripts/load-failures)