highseat 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -15
- package/next.d.ts +4 -2
- package/next.js +9 -3
- package/next.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,14 +23,7 @@ The package loads the tracker from Highseat, or from your site's proxy path, rat
|
|
|
23
23
|
|
|
24
24
|
## Next.js
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
NEXT_PUBLIC_HIGHSEAT_SITE=site_…
|
|
30
|
-
HIGHSEAT_PATH=/3f9a0c1e2b
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Then render `<Highseat />` once, in the root layout:
|
|
26
|
+
Render `<Highseat />` once, in the root layout, with the site id from your site's Installation settings:
|
|
34
27
|
|
|
35
28
|
```tsx
|
|
36
29
|
// app/layout.tsx
|
|
@@ -41,27 +34,66 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
41
34
|
<html lang="en">
|
|
42
35
|
<body>
|
|
43
36
|
{children}
|
|
44
|
-
<Highseat />
|
|
37
|
+
<Highseat site="site_…" />
|
|
45
38
|
</body>
|
|
46
39
|
</html>
|
|
47
40
|
)
|
|
48
41
|
}
|
|
49
42
|
```
|
|
50
43
|
|
|
51
|
-
|
|
44
|
+
To keep the site id out of the code, leave `site` out and set it for the build, with the proxy path for first-party delivery:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
NEXT_PUBLIC_HIGHSEAT_SITE=site_…
|
|
48
|
+
HIGHSEAT_PATH=/3f9a0c1e2b
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Without `HIGHSEAT_PATH`, `<Highseat />` loads the tracker from Highseat's host. The tracker counts each client-side navigation as one page view on its own. With the Pages Router, render it in `pages/_app.tsx` instead.
|
|
52
52
|
|
|
53
53
|
## React
|
|
54
54
|
|
|
55
|
+
Render `<Highseat />` once, beside your root component:
|
|
56
|
+
|
|
55
57
|
```tsx
|
|
56
58
|
import { Highseat } from "highseat/react"
|
|
57
59
|
|
|
58
60
|
<Highseat site="site_…" />
|
|
59
61
|
```
|
|
60
62
|
|
|
61
|
-
`src` takes the site's proxy path (`/3f9a0c1e2b`) for first-party delivery. `outbound={false}` and `downloads={false}` turn off the automatic link and download events, and `hashRouting` counts `#/route` changes as page views.
|
|
63
|
+
With Vite, `site={import.meta.env.VITE_HIGHSEAT_SITE}` reads the site id from the environment instead; with another bundler, read it from that bundler's public variable. `src` takes the site's proxy path (`/3f9a0c1e2b`) for first-party delivery. `outbound={false}` and `downloads={false}` turn off the automatic link and download events, and `hashRouting` counts `#/route` changes as page views.
|
|
62
64
|
|
|
63
65
|
Without React, `inject({ site })` from `highseat` adds the same script tag.
|
|
64
66
|
|
|
67
|
+
## TanStack Start
|
|
68
|
+
|
|
69
|
+
Render `<Highseat />` once, inside `<body>` in the root route's component:
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
// src/routes/__root.tsx
|
|
73
|
+
import { Highseat } from "highseat/react"
|
|
74
|
+
|
|
75
|
+
<Highseat site="site_…" />
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
To serve the tracker from your own domain, see TanStack Start under [First-party proxy](#first-party-proxy).
|
|
79
|
+
|
|
80
|
+
## Astro
|
|
81
|
+
|
|
82
|
+
Add the tracker's tag to the `<head>` of your layout. `is:inline` keeps Astro from bundling it, so the tracker starts before any page script:
|
|
83
|
+
|
|
84
|
+
```astro
|
|
85
|
+
<!-- src/layouts/Layout.astro -->
|
|
86
|
+
<script is:inline defer src="https://cdn.highseat.com/script.js" data-site="site_…"></script>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Any other site
|
|
90
|
+
|
|
91
|
+
A site without a bundler, or a website builder such as Webflow or Shopify, needs no package. Add the tag to the `<head>` of every page, in the builder's head code field:
|
|
92
|
+
|
|
93
|
+
```html
|
|
94
|
+
<script defer src="https://cdn.highseat.com/script.js" data-site="site_…"></script>
|
|
95
|
+
```
|
|
96
|
+
|
|
65
97
|
## Debugging
|
|
66
98
|
|
|
67
99
|
In development, the package logs every `track()` and `identify()` call to the console, with its properties or id, and says whether the call went to the tracker or waits in the queue for it to load. It also warns once per page when nothing can reach Highseat: the page has no tracker script, or the script failed to load, as one an ad blocker stopped does.
|
|
@@ -155,7 +187,11 @@ HIGHSEAT_SECRET=hs_secret_... # the site secret
|
|
|
155
187
|
|
|
156
188
|
A missing variable never stops the site. Without the secret, or without the path on a handler with a `fallback`, `createProxy()` warns once and turns the proxy off: with a `fallback`, every request goes to it, and without one the proxy path answers `503` with a JSON `error` naming the variable. A local server without the variables therefore still starts and serves the site.
|
|
157
189
|
|
|
158
|
-
Then load the tracker from the path. In Next.js, `<Highseat />` reads `HIGHSEAT_PATH` itself. Elsewhere, pass `src="/3f9a0c1e2b"` to `<Highseat />` or `inject()`, or
|
|
190
|
+
Then load the tracker from the path. In Next.js, `<Highseat />` reads `HIGHSEAT_PATH` itself. Elsewhere, pass `src="/3f9a0c1e2b"` to `<Highseat />` or `inject()`, or load it with a script tag in the `<head>` of every page:
|
|
191
|
+
|
|
192
|
+
```html
|
|
193
|
+
<script defer src="/3f9a0c1e2b" data-site="site_…"></script>
|
|
194
|
+
```
|
|
159
195
|
|
|
160
196
|
To rotate the path, rotate it in Installation settings, update `HIGHSEAT_PATH` and the snippet, and redeploy. On a host whose route is a file or a config entry (Netlify, Vercel without Next.js, Astro), move that route too.
|
|
161
197
|
|
|
@@ -191,9 +227,27 @@ export default withHighseat({
|
|
|
191
227
|
})
|
|
192
228
|
```
|
|
193
229
|
|
|
230
|
+
Then render `<Highseat />` in the root layout without `site` or `src`, in place of whatever loads the tracker now. It reads the site id from `NEXT_PUBLIC_HIGHSEAT_SITE` and the path from `HIGHSEAT_PATH`, and a `src` prop would outrank the path:
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
// app/layout.tsx
|
|
234
|
+
import { Highseat } from "highseat/next"
|
|
235
|
+
|
|
236
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
237
|
+
return (
|
|
238
|
+
<html lang="en">
|
|
239
|
+
<body>
|
|
240
|
+
{children}
|
|
241
|
+
<Highseat />
|
|
242
|
+
</body>
|
|
243
|
+
</html>
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
194
248
|
Next.js loads `next.config.ts` with `require`, and the package is ES modules only, so the config needs Node 20.19 or 22.12 and later, the versions that `require` an ES module without a flag.
|
|
195
249
|
|
|
196
|
-
`withHighseat()` rewrites `HIGHSEAT_PATH` to the route handler inside the app, so the visitor's IP survives and the route file never moves. Pass `{ route: "/stats" }` for a handler elsewhere. Without `HIGHSEAT_PATH`, `next dev` and `next build` warn once and leave the rewrite out, and `next typegen` runs as usual. Set the variable for the build as
|
|
250
|
+
`withHighseat()` rewrites `HIGHSEAT_PATH` to the route handler inside the app, so the visitor's IP survives and the route file never moves. Pass `{ route: "/stats" }` for a handler elsewhere. Without `HIGHSEAT_PATH`, `next dev` and `next build` warn once and leave the rewrite out, and `next typegen` runs as usual. Set the variable for the build, as a build argument in a Docker build. `withHighseat()` writes the build's value into the rewrite and into the bundled code of `<Highseat />` and `createProxy()`, so the running App Router server needs only `HIGHSEAT_SECRET`. Off Vercel, the outermost proxy that sets the header decides the preset: `"cloudflare"` behind a proxied Cloudflare record, even with nginx or Traefik in between, and `"nginx"` behind nginx alone.
|
|
197
251
|
|
|
198
252
|
### TanStack Start
|
|
199
253
|
|
|
@@ -268,6 +322,8 @@ export default {
|
|
|
268
322
|
|
|
269
323
|
Route the Worker on `yoursite.com/*`: `fallback: fetch` passes every other request on to your origin. Set the secret with `wrangler secret put HIGHSEAT_SECRET`.
|
|
270
324
|
|
|
325
|
+
A Worker that serves the site's static assets has no origin: pass `fallback: request => env.ASSETS.fetch(request)`, and leave `not_found_handling` out of your Wrangler config. With `not_found_handling` set, the assets answer every path that has no file, the proxy path included, so list the path in `run_worker_first`.
|
|
326
|
+
|
|
271
327
|
### Cloudflare Pages
|
|
272
328
|
|
|
273
329
|
```ts
|
|
@@ -290,7 +346,14 @@ export const onRequest: PagesFunction<Env> = ({ request, env, next }) => {
|
|
|
290
346
|
}
|
|
291
347
|
```
|
|
292
348
|
|
|
293
|
-
|
|
349
|
+
```json
|
|
350
|
+
// public/_routes.json
|
|
351
|
+
{ "version": 1, "include": ["/3f9a0c1e2b"], "exclude": [] }
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
`_routes.json` runs the function on the proxy path alone, so every page stays a static asset; without it the function runs on every one-segment path, and `next` serves your site for each one but the proxy's. Put it in the folder your build copies to its output as is, such as `public/`. Add both variables in the Pages project's settings.
|
|
355
|
+
|
|
356
|
+
Pages ignores `functions/` when the build ships a `_worker.js`, as the Astro, SvelteKit and Remix adapters do, and the proxy path then answers with your site's 404. Remove the adapter from a static site, or use the adapter's own endpoint instead.
|
|
294
357
|
|
|
295
358
|
### Netlify
|
|
296
359
|
|
|
@@ -336,7 +399,14 @@ export const ALL: APIRoute = ({ request, clientAddress }) =>
|
|
|
336
399
|
createProxy({ clientIp: () => clientAddress })(request)
|
|
337
400
|
```
|
|
338
401
|
|
|
339
|
-
|
|
402
|
+
Then load the tracker from the path in your layout:
|
|
403
|
+
|
|
404
|
+
```astro
|
|
405
|
+
<!-- src/layouts/Layout.astro -->
|
|
406
|
+
<script is:inline defer src="/3f9a0c1e2b" data-site={import.meta.env.PUBLIC_HIGHSEAT_SITE}></script>
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Needs a server adapter. The file name carries the path, so set only `HIGHSEAT_SECRET`, and `PUBLIC_HIGHSEAT_SITE` for the site id. On the Cloudflare adapter, pass `secret: locals.runtime.env.HIGHSEAT_SECRET`. `clientAddress` is the address that connected, so behind a proxied Cloudflare record, use `"cloudflare"` instead.
|
|
340
410
|
|
|
341
411
|
### Hono
|
|
342
412
|
|
package/next.d.ts
CHANGED
|
@@ -47,8 +47,10 @@ export interface WithHighseatOptions {
|
|
|
47
47
|
*
|
|
48
48
|
* The proxy is optional. Without a path, `next dev` and `next build` warn once and leave the
|
|
49
49
|
* rewrite out, and `<Highseat />` loads the tracker from Highseat's host. Nothing here throws,
|
|
50
|
-
* so `next typegen` runs without the variable too.
|
|
51
|
-
*
|
|
50
|
+
* so `next typegen` runs without the variable too. With a path, it also sets `HIGHSEAT_PATH` in
|
|
51
|
+
* the config's `env`, so Next.js writes the build's value into the bundled code of `<Highseat />`
|
|
52
|
+
* and `createProxy()`. In the App Router, the script and the rewrite then agree, whatever the
|
|
53
|
+
* server's environment.
|
|
52
54
|
*
|
|
53
55
|
* ```ts
|
|
54
56
|
* // next.config.ts
|
package/next.js
CHANGED
|
@@ -33,8 +33,10 @@ let hasWarnedNoPath = false;
|
|
|
33
33
|
*
|
|
34
34
|
* The proxy is optional. Without a path, `next dev` and `next build` warn once and leave the
|
|
35
35
|
* rewrite out, and `<Highseat />` loads the tracker from Highseat's host. Nothing here throws,
|
|
36
|
-
* so `next typegen` runs without the variable too.
|
|
37
|
-
*
|
|
36
|
+
* so `next typegen` runs without the variable too. With a path, it also sets `HIGHSEAT_PATH` in
|
|
37
|
+
* the config's `env`, so Next.js writes the build's value into the bundled code of `<Highseat />`
|
|
38
|
+
* and `createProxy()`. In the App Router, the script and the rewrite then agree, whatever the
|
|
39
|
+
* server's environment.
|
|
38
40
|
*
|
|
39
41
|
* ```ts
|
|
40
42
|
* // next.config.ts
|
|
@@ -44,7 +46,7 @@ let hasWarnedNoPath = false;
|
|
|
44
46
|
*/
|
|
45
47
|
function withHighseat(config, options = {}) {
|
|
46
48
|
const path = findProxyPath(options.path);
|
|
47
|
-
const { rewrites: ownRewrites } = config;
|
|
49
|
+
const { rewrites: ownRewrites, env } = config;
|
|
48
50
|
async function passRewritesThrough() {
|
|
49
51
|
if (!hasWarnedNoPath) {
|
|
50
52
|
hasWarnedNoPath = true;
|
|
@@ -73,6 +75,10 @@ function withHighseat(config, options = {}) {
|
|
|
73
75
|
}
|
|
74
76
|
return {
|
|
75
77
|
...config,
|
|
78
|
+
env: {
|
|
79
|
+
...env,
|
|
80
|
+
HIGHSEAT_PATH: path
|
|
81
|
+
},
|
|
76
82
|
rewrites
|
|
77
83
|
};
|
|
78
84
|
}
|
package/next.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"next.js","names":["ReactHighseat"],"sources":["../src/next.ts"],"sourcesContent":["import { createElement, type ReactElement } from \"react\"\nimport { findNextSite, findProxyPath } from \"./env\"\nimport { Highseat as ReactHighseat, type HighseatProps as ReactHighseatProps } from \"./react\"\n\nexport { createProxy } from \"./proxy\"\nexport { useIdentify } from \"./react\"\nexport type { ClientIp, ClientIpPreset, ProxyHandler, ProxyOptions } from \"./proxy\"\n\nexport interface HighseatProps extends Partial<ReactHighseatProps> {}\n\n/**\n * Loads the Highseat tracker in a Next.js app. Render it once, in the root layout. The site id\n * comes from `NEXT_PUBLIC_HIGHSEAT_SITE`, and the script loads from the site's proxy path in\n * `HIGHSEAT_PATH` when one is set. `HIGHSEAT_PATH` is a server variable, so in the Pages Router,\n * where the layout also renders in the browser, pass `src` instead. A prop wins over its variable.\n * `userId` takes an id the server already knows. For a session read in the browser, call\n * `useIdentify` in a client component instead.\n */\nexport function Highseat(props: HighseatProps): ReactElement | null {\n const site = props.site ?? findNextSite()\n const src = props.src ?? findProxyPath()\n\n if (!site) {\n console.warn(\"highseat: set NEXT_PUBLIC_HIGHSEAT_SITE, or pass `site` to <Highseat />\")\n return null\n }\n\n return createElement(ReactHighseat, { ...props, site, src })\n}\n\n/** A Next.js rewrite: the subset of its fields this package writes and reads. */\ninterface Rewrite {\n source: string\n destination: string\n}\n\n/** The phases Next.js applies rewrites in, when `rewrites()` returns an object. */\ninterface RewritePhases {\n beforeFiles?: Rewrite[]\n afterFiles?: Rewrite[]\n fallback?: Rewrite[]\n}\n\n/** The part of a Next.js config `withHighseat` reads. */\ninterface NextConfigWithRewrites {\n rewrites?: () => Promise<Rewrite[] | RewritePhases>\n}\n\n/** A Next.js config with the proxy's rewrite added, or its own rewrites when there is no path. */\nexport type WithHighseatConfig<Config> = Config & {\n rewrites: () => Promise<Rewrite[] | RewritePhases>\n}\n\nexport interface WithHighseatOptions {\n /**\n * The site's proxy path from Installation settings. Defaults to the `HIGHSEAT_PATH`\n * environment variable, the one `createProxy()` reads, so rotating the path is an\n * environment change and a redeploy.\n */\n path?: string\n /**\n * Where the app's proxy route handler lives. Defaults to `/api/highseat`, the route of\n * `app/api/highseat/route.ts`.\n */\n route?: string\n}\n\nconst DEFAULT_ROUTE = \"/api/highseat\"\n\n// A dev session can ask for the rewrites more than once, so the warning is printed once.\nlet hasWarnedNoPath = false\n\n/**\n * Adds a rewrite from the site's proxy path to the app's proxy route handler, ahead of every\n * other route. The rewrite stays inside the app, so the handler still sees the visitor's IP, and\n * the route file never moves when the path rotates.\n *\n * The proxy is optional. Without a path, `next dev` and `next build` warn once and leave the\n * rewrite out, and `<Highseat />` loads the tracker from Highseat's host. Nothing here throws,\n * so `next typegen` runs without the variable too.
|
|
1
|
+
{"version":3,"file":"next.js","names":["ReactHighseat"],"sources":["../src/next.ts"],"sourcesContent":["import { createElement, type ReactElement } from \"react\"\nimport { findNextSite, findProxyPath } from \"./env\"\nimport { Highseat as ReactHighseat, type HighseatProps as ReactHighseatProps } from \"./react\"\n\nexport { createProxy } from \"./proxy\"\nexport { useIdentify } from \"./react\"\nexport type { ClientIp, ClientIpPreset, ProxyHandler, ProxyOptions } from \"./proxy\"\n\nexport interface HighseatProps extends Partial<ReactHighseatProps> {}\n\n/**\n * Loads the Highseat tracker in a Next.js app. Render it once, in the root layout. The site id\n * comes from `NEXT_PUBLIC_HIGHSEAT_SITE`, and the script loads from the site's proxy path in\n * `HIGHSEAT_PATH` when one is set. `HIGHSEAT_PATH` is a server variable, so in the Pages Router,\n * where the layout also renders in the browser, pass `src` instead. A prop wins over its variable.\n * `userId` takes an id the server already knows. For a session read in the browser, call\n * `useIdentify` in a client component instead.\n */\nexport function Highseat(props: HighseatProps): ReactElement | null {\n const site = props.site ?? findNextSite()\n const src = props.src ?? findProxyPath()\n\n if (!site) {\n console.warn(\"highseat: set NEXT_PUBLIC_HIGHSEAT_SITE, or pass `site` to <Highseat />\")\n return null\n }\n\n return createElement(ReactHighseat, { ...props, site, src })\n}\n\n/** A Next.js rewrite: the subset of its fields this package writes and reads. */\ninterface Rewrite {\n source: string\n destination: string\n}\n\n/** The phases Next.js applies rewrites in, when `rewrites()` returns an object. */\ninterface RewritePhases {\n beforeFiles?: Rewrite[]\n afterFiles?: Rewrite[]\n fallback?: Rewrite[]\n}\n\n/** The part of a Next.js config `withHighseat` reads. */\ninterface NextConfigWithRewrites {\n rewrites?: () => Promise<Rewrite[] | RewritePhases>\n env?: Record<string, string | undefined>\n}\n\n/** A Next.js config with the proxy's rewrite added, or its own rewrites when there is no path. */\nexport type WithHighseatConfig<Config> = Config & {\n rewrites: () => Promise<Rewrite[] | RewritePhases>\n}\n\nexport interface WithHighseatOptions {\n /**\n * The site's proxy path from Installation settings. Defaults to the `HIGHSEAT_PATH`\n * environment variable, the one `createProxy()` reads, so rotating the path is an\n * environment change and a redeploy.\n */\n path?: string\n /**\n * Where the app's proxy route handler lives. Defaults to `/api/highseat`, the route of\n * `app/api/highseat/route.ts`.\n */\n route?: string\n}\n\nconst DEFAULT_ROUTE = \"/api/highseat\"\n\n// A dev session can ask for the rewrites more than once, so the warning is printed once.\nlet hasWarnedNoPath = false\n\n/**\n * Adds a rewrite from the site's proxy path to the app's proxy route handler, ahead of every\n * other route. The rewrite stays inside the app, so the handler still sees the visitor's IP, and\n * the route file never moves when the path rotates.\n *\n * The proxy is optional. Without a path, `next dev` and `next build` warn once and leave the\n * rewrite out, and `<Highseat />` loads the tracker from Highseat's host. Nothing here throws,\n * so `next typegen` runs without the variable too. With a path, it also sets `HIGHSEAT_PATH` in\n * the config's `env`, so Next.js writes the build's value into the bundled code of `<Highseat />`\n * and `createProxy()`. In the App Router, the script and the rewrite then agree, whatever the\n * server's environment.\n *\n * ```ts\n * // next.config.ts\n * import { withHighseat } from \"highseat/next\"\n * export default withHighseat({ reactStrictMode: true })\n * ```\n */\nexport function withHighseat<Config extends object>(\n config: Config,\n options: WithHighseatOptions = {},\n): WithHighseatConfig<Config> {\n const path = findProxyPath(options.path)\n\n // Typed loosely so any Next.js version's config fits; only `rewrites` and `env` are read.\n const { rewrites: ownRewrites, env } = config as NextConfigWithRewrites\n\n async function passRewritesThrough(): Promise<Rewrite[] | RewritePhases> {\n if (!hasWarnedNoPath) {\n hasWarnedNoPath = true\n console.warn(\n \"highseat/next: no HIGHSEAT_PATH, so this build has no proxy rewrite and the tracker loads from Highseat's host. For first-party delivery, set it to the path from the site's Installation settings, or pass `path`, for the build as well as at runtime: a path set only at runtime points the tracker at a route this build does not have.\",\n )\n }\n return (await ownRewrites?.()) ?? []\n }\n\n if (!path) return { ...config, rewrites: passRewritesThrough }\n\n const rewrite = { source: path, destination: options.route ?? DEFAULT_ROUTE }\n\n async function rewrites(): Promise<RewritePhases> {\n const existing = (await ownRewrites?.()) ?? []\n\n // A plain array is Next.js shorthand for the `afterFiles` phase.\n if (Array.isArray(existing)) return { beforeFiles: [rewrite], afterFiles: existing }\n return { ...existing, beforeFiles: [rewrite, ...(existing.beforeFiles ?? [])] }\n }\n\n return { ...config, env: { ...env, HIGHSEAT_PATH: path }, rewrites }\n}\n"],"mappings":";;;;;;;;;;;;;AAkBA,SAAgB,SAAS,OAA2C;CAClE,MAAM,OAAO,MAAM,QAAQ,aAAa;CACxC,MAAM,MAAM,MAAM,OAAO,cAAc;CAEvC,IAAI,CAAC,MAAM;EACT,QAAQ,KAAK,yEAAyE;EACtF,OAAO;CACT;CAEA,OAAO,cAAcA,YAAe;EAAE,GAAG;EAAO;EAAM;CAAI,CAAC;AAC7D;AAwCA,MAAM,gBAAgB;AAGtB,IAAI,kBAAkB;;;;;;;;;;;;;;;;;;;AAoBtB,SAAgB,aACd,QACA,UAA+B,CAAC,GACJ;CAC5B,MAAM,OAAO,cAAc,QAAQ,IAAI;CAGvC,MAAM,EAAE,UAAU,aAAa,QAAQ;CAEvC,eAAe,sBAA0D;EACvE,IAAI,CAAC,iBAAiB;GACpB,kBAAkB;GAClB,QAAQ,KACN,6UACF;EACF;EACA,OAAQ,MAAM,cAAc,KAAM,CAAC;CACrC;CAEA,IAAI,CAAC,MAAM,OAAO;EAAE,GAAG;EAAQ,UAAU;CAAoB;CAE7D,MAAM,UAAU;EAAE,QAAQ;EAAM,aAAa,QAAQ,SAAS;CAAc;CAE5E,eAAe,WAAmC;EAChD,MAAM,WAAY,MAAM,cAAc,KAAM,CAAC;EAG7C,IAAI,MAAM,QAAQ,QAAQ,GAAG,OAAO;GAAE,aAAa,CAAC,OAAO;GAAG,YAAY;EAAS;EACnF,OAAO;GAAE,GAAG;GAAU,aAAa,CAAC,SAAS,GAAI,SAAS,eAAe,CAAC,CAAE;EAAE;CAChF;CAEA,OAAO;EAAE,GAAG;EAAQ,KAAK;GAAE,GAAG;GAAK,eAAe;EAAK;EAAG;CAAS;AACrE"}
|