highseat 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -3
- package/api.d.ts +123 -0
- package/api.gen.d.ts +5155 -0
- package/api.js +84 -0
- package/api.js.map +1 -0
- package/embed.d.ts +15 -0
- package/embed.js +49 -0
- package/embed.js.map +1 -0
- package/index.d.ts +0 -2
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/ingest.d.ts +1 -1
- package/ingest.js +3 -7
- package/ingest.js.map +1 -1
- package/next.d.ts +3 -2
- package/next.js +2 -2
- package/next.js.map +1 -1
- package/package.json +5 -1
- package/proxy.d.ts +3 -3
- package/proxy.js.map +1 -1
- package/react.d.ts +58 -8
- package/react.js +69 -2
- package/react.js.map +1 -1
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ npm install highseat
|
|
|
13
13
|
| Import | Contents | Needs |
|
|
14
14
|
| --- | --- | --- |
|
|
15
15
|
| `highseat` | `track()`, `identify()`, `getAttributionId()`, `inject()` and `setBeforeSend()` | Nothing |
|
|
16
|
-
| `highseat/react` | `<Highseat />`, `useIdentify()` and `useBeforeSend()` | React |
|
|
17
|
-
| `highseat/next` | `<Highseat />`, `useIdentify()`, `useBeforeSend()`, `createProxy()` and `withHighseat()` for Next.js | Next.js |
|
|
16
|
+
| `highseat/react` | `<Highseat />`, `<Embed />`, `useIdentify()` and `useBeforeSend()` | React |
|
|
17
|
+
| `highseat/next` | `<Highseat />`, `<Embed />`, `useIdentify()`, `useBeforeSend()`, `createProxy()` and `withHighseat()` for Next.js | Next.js |
|
|
18
18
|
| `highseat/proxy` | `createProxy()`, a first-party proxy for any runtime with `fetch` | Nothing |
|
|
19
19
|
| `highseat/server` | `track()`, `flush()`, `close()` and `createTracker()`, for events sent from your backend | Nothing |
|
|
20
20
|
| `highseat/mcp` | `trackToolCalls()` and `mcpLink()`, for the tool calls AI assistants make to your MCP server | The MCP TypeScript SDK v2 |
|
|
@@ -96,6 +96,47 @@ A site without a bundler, or a website builder such as Webflow or Shopify, needs
|
|
|
96
96
|
<script defer src="https://cdn.highseat.com/script.js" data-site="site_…"></script>
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
## Embeds
|
|
100
|
+
|
|
101
|
+
`<Embed />` shows a share link or a widget on your page. Its `token` is the last part of the `/share/` address in the embed code, and `title` names the frame for screen readers. It needs no script tag: the component fits the frame to the embed and follows it as it resizes.
|
|
102
|
+
|
|
103
|
+
Pass your page's theme when the page has its own switch, so the frame's colours match the page:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
"use client"
|
|
107
|
+
|
|
108
|
+
import { Embed } from "highseat/react"
|
|
109
|
+
import { useTheme } from "next-themes"
|
|
110
|
+
|
|
111
|
+
export function VisitorsChart() {
|
|
112
|
+
const { resolvedTheme } = useTheme()
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<Embed
|
|
116
|
+
token="YOUR_WIDGET_TOKEN"
|
|
117
|
+
title="Visitors in the last 30 days"
|
|
118
|
+
widget="chart"
|
|
119
|
+
theme={resolvedTheme === "dark" ? "dark" : "light"}
|
|
120
|
+
/>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The server renders an empty box at the frame's height, and the frame replaces it after mount. So a theme known only in the browser needs no mounted check of your own and causes no hydration mismatch. If the theme is still unknown after mount, pass `theme={null}`: the box stays empty until you pass a theme, and the frame loads once. Leave `theme` out to follow the reader's own light or dark setting.
|
|
126
|
+
|
|
127
|
+
| Prop | Default | Does |
|
|
128
|
+
| --- | --- | --- |
|
|
129
|
+
| `token` | Required | The share link or widget to show |
|
|
130
|
+
| `title` | Required | The frame's accessible name |
|
|
131
|
+
| `theme` | `"system"` | `"system"`, `"light"`, `"dark"`, or `null` while your page's theme is unknown |
|
|
132
|
+
| `widget` | None, for a whole board | `"chart"` or `"figure"`, which sets the height held before the frame loads: 360px, 80px, or 1600px for a board |
|
|
133
|
+
| `background` | `"transparent"` for a widget, otherwise `"default"` | `"transparent"` lets your page's background show through |
|
|
134
|
+
| `height` | Set by `widget` | The height in pixels to hold until the embed reports its own |
|
|
135
|
+
| `credit` | `true` | Shows the "Analytics by Highseat" link under the frame |
|
|
136
|
+
| `creditClassName` | None | A class for the credit link, which replaces its default spacing and size |
|
|
137
|
+
|
|
138
|
+
`className` and `style` go to the box around the frame and the credit link. Other attributes, such as `id` or `loading`, go to the frame. The credit link is a plain, followed link in the server's HTML, and it is yours to remove: pass `credit={false}`.
|
|
139
|
+
|
|
99
140
|
## Debugging
|
|
100
141
|
|
|
101
142
|
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.
|
|
@@ -400,7 +441,7 @@ To rotate the path, rotate it in Installation settings, update `HIGHSEAT_PATH` a
|
|
|
400
441
|
| --- | --- | --- |
|
|
401
442
|
| `clientIp` | Required | Where the visitor's IP comes from: `"vercel"`, `"cloudflare"`, `"netlify"`, `"nginx"` (the `X-Real-IP` header your nginx sets), or a function `(request) => string`. The outermost proxy that sets the header decides the preset: behind a proxied Cloudflare record, use `"cloudflare"` on any origin (Next.js, TanStack Start, a Node server, with or without Traefik or nginx in between), whose `X-Real-IP` holds a Cloudflare edge address. A visitor can write any header the host does not overwrite, so there is no default and no fallback to `X-Forwarded-For`. |
|
|
402
443
|
| `path` | `HIGHSEAT_PATH` | The proxy path. Any other path answers 404, or goes to `fallback`. Leave it unset where your router already sends only the proxy path to the handler. |
|
|
403
|
-
| `secret` | `HIGHSEAT_SECRET` | The site secret.
|
|
444
|
+
| `secret` | `HIGHSEAT_SECRET` | The site secret. A value that does not start with `hs_secret_` throws. |
|
|
404
445
|
| `upstream` | `https://cdn.highseat.com` | The Highseat ingest origin. |
|
|
405
446
|
| `fallback` | None | Answers every other path, for a handler that sees every request on the site. Needs `path`. |
|
|
406
447
|
|
package/api.d.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { paths } from "./api.gen.js";
|
|
2
|
+
//#region src/api.d.ts
|
|
3
|
+
type Method = "get" | "post" | "patch" | "delete";
|
|
4
|
+
/** A path of the API that answers a method. */
|
|
5
|
+
type PathOf<Name extends Method> = { [Path in keyof paths]: paths[Path] extends Record<Name, object> ? Path : never; }[keyof paths];
|
|
6
|
+
type Operation<Path extends keyof paths, Name extends Method> = paths[Path][Name & keyof paths[Path]];
|
|
7
|
+
type PathOptions<Route> = Route extends {
|
|
8
|
+
parameters: {
|
|
9
|
+
path: infer Values;
|
|
10
|
+
};
|
|
11
|
+
} ? {
|
|
12
|
+
path: Values;
|
|
13
|
+
} : {
|
|
14
|
+
path?: undefined;
|
|
15
|
+
};
|
|
16
|
+
type QueryOptions<Route> = Route extends {
|
|
17
|
+
parameters: {
|
|
18
|
+
query: infer Values;
|
|
19
|
+
};
|
|
20
|
+
} ? {
|
|
21
|
+
query: Values;
|
|
22
|
+
} : Route extends {
|
|
23
|
+
parameters: {
|
|
24
|
+
query?: infer Values;
|
|
25
|
+
};
|
|
26
|
+
} ? {
|
|
27
|
+
query?: Values;
|
|
28
|
+
} : {
|
|
29
|
+
query?: undefined;
|
|
30
|
+
};
|
|
31
|
+
type BodyOptions<Route> = Route extends {
|
|
32
|
+
requestBody: {
|
|
33
|
+
content: {
|
|
34
|
+
"application/json": infer Body;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
} ? {
|
|
38
|
+
body: Body;
|
|
39
|
+
} : {
|
|
40
|
+
body?: undefined;
|
|
41
|
+
};
|
|
42
|
+
/** What every call may add. */
|
|
43
|
+
interface CallOptions {
|
|
44
|
+
/** Makes a `POST` safe to retry: a repeat within 24 hours replays the first answer. */
|
|
45
|
+
idempotencyKey?: string;
|
|
46
|
+
/** Cancels the call. */
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
}
|
|
49
|
+
type Options<Route> = PathOptions<Route> & QueryOptions<Route> & BodyOptions<Route> & CallOptions;
|
|
50
|
+
type Arguments<Route> = object extends Options<Route> ? [options?: Options<Route>] : [options: Options<Route>];
|
|
51
|
+
type JsonOf<Response> = Response extends {
|
|
52
|
+
content: {
|
|
53
|
+
"application/json": infer Body;
|
|
54
|
+
};
|
|
55
|
+
} ? Body : undefined;
|
|
56
|
+
type ResultOf<Route> = Route extends {
|
|
57
|
+
responses: infer Responses;
|
|
58
|
+
} ? JsonOf<Responses[Extract<keyof Responses, 200 | 201 | 202 | 204>]> : never;
|
|
59
|
+
type Call<Name extends Method> = <Path extends PathOf<Name>>(path: Path, ...options: Arguments<Operation<Path, Name>>) => Promise<ResultOf<Operation<Path, Name>>>;
|
|
60
|
+
/** What a call to a path answers, for naming it in your own code. */
|
|
61
|
+
export type ApiResult<Path extends keyof paths, Name extends Method = "get"> = ResultOf<Operation<Path, Name>>;
|
|
62
|
+
/** A client of the Highseat API, with one typed function per HTTP method. */
|
|
63
|
+
export interface HighseatApi {
|
|
64
|
+
get: Call<"get">;
|
|
65
|
+
post: Call<"post">;
|
|
66
|
+
patch: Call<"patch">;
|
|
67
|
+
delete: Call<"delete">;
|
|
68
|
+
}
|
|
69
|
+
/** How to reach the API. */
|
|
70
|
+
export interface ApiOptions {
|
|
71
|
+
/**
|
|
72
|
+
* An API key or an OAuth access token. Pass a function when the token changes, as an OAuth
|
|
73
|
+
* token does when it is refreshed: it runs before every call.
|
|
74
|
+
*/
|
|
75
|
+
token: string | (() => string | Promise<string>);
|
|
76
|
+
/** The API's address, with its version. Defaults to `https://api.highseat.com/v1`. */
|
|
77
|
+
baseUrl?: string;
|
|
78
|
+
/** Replaces the global `fetch`. */
|
|
79
|
+
fetch?: typeof globalThis.fetch;
|
|
80
|
+
}
|
|
81
|
+
/** One invalid parameter of a refused call. */
|
|
82
|
+
export interface ApiIssue {
|
|
83
|
+
/** The parameter at fault, such as `["filter", 1]` for the second filter. */
|
|
84
|
+
path: (string | number)[];
|
|
85
|
+
message: string;
|
|
86
|
+
}
|
|
87
|
+
interface ApiFailure {
|
|
88
|
+
code?: string;
|
|
89
|
+
message?: string;
|
|
90
|
+
requestId?: string;
|
|
91
|
+
id?: string;
|
|
92
|
+
issues?: ApiIssue[];
|
|
93
|
+
}
|
|
94
|
+
/** A call the API refused. Branch on `code`, which is stable. */
|
|
95
|
+
export declare class HighseatApiError extends Error {
|
|
96
|
+
/** The HTTP status. */
|
|
97
|
+
readonly status: number;
|
|
98
|
+
/** Stable and machine-readable, such as `RATE_LIMITED`. */
|
|
99
|
+
readonly code: string;
|
|
100
|
+
/** Names the request. Quote it to support. */
|
|
101
|
+
readonly requestId?: string;
|
|
102
|
+
/** On a conflict, the existing row's id. */
|
|
103
|
+
readonly id?: string;
|
|
104
|
+
/** One entry per invalid parameter. */
|
|
105
|
+
readonly issues?: ApiIssue[];
|
|
106
|
+
constructor(status: number, failure: ApiFailure);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Creates a client of the Highseat API. Paths, parameters and answers are typed from the API's
|
|
110
|
+
* OpenAPI document, and a refused call throws {@link HighseatApiError}.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* import { createApi } from "highseat/api"
|
|
115
|
+
*
|
|
116
|
+
* const api = createApi({ token: process.env.HIGHSEAT_API_KEY })
|
|
117
|
+
* const { data } = await api.get("/sites/{site}/realtime", { path: { site: "example.com" } })
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export declare function createApi({ token, baseUrl, fetch }: ApiOptions): HighseatApi;
|
|
121
|
+
//#endregion
|
|
122
|
+
export {};
|
|
123
|
+
//# sourceMappingURL=api.d.ts.map
|