@guidelytech/node 0.15.0 → 0.17.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 +149 -5
- package/dist/index.cjs +1417 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +368 -7
- package/dist/index.d.ts +368 -7
- package/dist/index.js +1404 -32
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
Send product data to [Guidely](https://guidelytech.com) from your Node.js backend: who a user is
|
|
4
4
|
(`identify`), what they did (`track`) and which account they belong to (`group`). Server events land
|
|
5
5
|
next to browser and app events, so they work in Events, Funnels, Retention, audiences and guide
|
|
6
|
-
targeting.
|
|
6
|
+
targeting. It also reports errors and crashes ([Error monitoring](#error-monitoring)) with the same
|
|
7
|
+
API as Sentry's Node SDK.
|
|
7
8
|
|
|
8
9
|
* Node.js 18+, no runtime dependencies (uses the global `fetch`). ESM and CommonJS.
|
|
9
10
|
* Calls never throw and never wait for the network: they validate, queue and return.
|
|
@@ -20,7 +21,8 @@ Feature flags for servers live in [`@guidelytech/flags`](../flags).
|
|
|
20
21
|
|
|
21
22
|
Create a **secret key** (`gly_sk_…`) with the **`events:write`** scope in **Settings → API keys**.
|
|
22
23
|
Keep it in your secret manager; never ship it to a browser or app. Each client writes to one
|
|
23
|
-
environment: `development`, `staging` or
|
|
24
|
+
environment, named by its key: `development`, `staging`, `production`, or one you added in
|
|
25
|
+
**Settings → Environments & Install** (`qa`, `uat`…).
|
|
24
26
|
|
|
25
27
|
## Usage
|
|
26
28
|
|
|
@@ -78,7 +80,7 @@ export const handler = async (event) => {
|
|
|
78
80
|
| Option | Default | |
|
|
79
81
|
|---|---|---|
|
|
80
82
|
| `secretKey` | — | **Required.** A `gly_sk_…` key with `events:write`. |
|
|
81
|
-
| `environment` | — | **Required.** `development`, `staging` or `
|
|
83
|
+
| `environment` | — | **Required.** The environment key: `development`, `staging`, `production` or one you added (`qa`). |
|
|
82
84
|
| `apiUrl` | `https://guidelytech.com` | For self-hosted or local servers. |
|
|
83
85
|
| `flushAt` | `100` | Send when this many messages are queued (a batch holds at most 100 messages and 500 KB). |
|
|
84
86
|
| `flushIntervalMs` | `5000` | Send what is queued at least this often. The timer never keeps the process alive. |
|
|
@@ -89,20 +91,162 @@ export const handler = async (event) => {
|
|
|
89
91
|
| `onError` | `console.warn` | Receives a `GuidelyError` for every problem (see below). |
|
|
90
92
|
| `handleExit` | `true` | Flush on `beforeExit`. |
|
|
91
93
|
| `fetch` | global `fetch` | Any `fetch`-compatible function (proxies, tests). |
|
|
94
|
+
| `appVersion` | — | Your app's version, sent with every `track` for [App versions](https://guidelytech.com/docs/product-analytics#app-versions); a message's own `appVersion` wins. Also the default release of errors. |
|
|
95
|
+
| `errors` | `{}` | [Error monitoring](#error-monitoring) options; `false` turns off the automatic breadcrumbs. |
|
|
96
|
+
|
|
97
|
+
## Error monitoring
|
|
98
|
+
|
|
99
|
+
Errors become `exception` events grouped into issues on the Errors page. The API is Sentry's, so
|
|
100
|
+
moving off `@sentry/node` is mostly a rename. Every capture call returns at once and never throws;
|
|
101
|
+
events go out through the same batched, retried queue as `track` calls, sent right away instead of
|
|
102
|
+
waiting for the flush interval.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
try {
|
|
106
|
+
await chargeCard(order);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
guidely.captureException(err, {
|
|
109
|
+
userId: "u_42", // default: the scope user's id
|
|
110
|
+
tags: { area: "billing" },
|
|
111
|
+
extra: { orderId: order.id },
|
|
112
|
+
level: "error", // fatal | error | warning | log | info | debug
|
|
113
|
+
fingerprint: ["{{ default }}", "billing"],
|
|
114
|
+
contexts: { payment: { provider: "stripe" } },
|
|
115
|
+
request: req, // an Express/Fastify/Koa request: method, URL, headers (no credentials)
|
|
116
|
+
}); // → the event id
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
guidely.captureMessage("Disk almost full", "warning");
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Other keys of the second argument (`orderId: "o_1"`) are kept as event properties. Errors carry their
|
|
123
|
+
`cause` chain and an `AggregateError`'s `errors` (up to 20, causes first), stack frames marked
|
|
124
|
+
in-app unless they are in `node_modules` or Node itself, and five source lines around each in-app
|
|
125
|
+
frame, read from disk (cached, files over 1 MB skipped; `contextLines: 0` turns it off).
|
|
126
|
+
|
|
127
|
+
### Scopes and breadcrumbs
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
guidely.setTag("region", "eu-west-1");
|
|
131
|
+
guidely.setTags({ tier: "pro" });
|
|
132
|
+
guidely.setExtra("build", 512);
|
|
133
|
+
guidely.setContext("queue", { name: "emails", depth: 40 }); // null removes it
|
|
134
|
+
guidely.setUser({ id: "u_42", email: "ada@example.com" }); // null clears it
|
|
135
|
+
guidely.addBreadcrumb({ category: "auth", message: "Signed in", level: "info", data: { method: "sso" } });
|
|
136
|
+
|
|
137
|
+
await guidely.withScope(async (scope) => {
|
|
138
|
+
scope.setTag("job", "nightly-export");
|
|
139
|
+
await runExport(); // errors captured in here (and in what it starts) carry the tag
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The client has one process-wide scope. `withScope()` runs a callback with a copy of the current one,
|
|
144
|
+
through `AsyncLocalStorage`, so what is set inside (tags, user, breadcrumbs) never leaks into other
|
|
145
|
+
work running at the same time; the framework middlewares below open one per request. The last 100
|
|
146
|
+
breadcrumbs of a scope ride along with each error.
|
|
147
|
+
|
|
148
|
+
Breadcrumbs are recorded automatically for `console.warn` / `console.error` and for outgoing
|
|
149
|
+
`http`/`https` requests and `fetch` (method, URL without the query string, status, duration; never
|
|
150
|
+
requests to Guidely). HTTP requests are observed through `diagnostics_channel`, so nothing is
|
|
151
|
+
patched; console methods are wrapped once and restored by `shutdown()`.
|
|
152
|
+
|
|
153
|
+
### Process handlers
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
const uninstall = guidely.installProcessHandlers();
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Opt-in. Reports `uncaughtException` (level `fatal`, `handled: false`) and `unhandledRejection`, then
|
|
160
|
+
behaves as Node would without them: the error is printed, queued events are sent (waiting at most
|
|
161
|
+
`flushTimeoutMs`, default 2000 ms) and the process exits with code 1.
|
|
162
|
+
|
|
163
|
+
* It does not exit when your app has its own `uncaughtException` listener (Node would not either),
|
|
164
|
+
or with `exitOnUncaught: false`, which keeps the process running in whatever state the error left
|
|
165
|
+
it, like any `uncaughtException` listener.
|
|
166
|
+
* `unhandledRejection` follows Node's `--unhandled-rejections` mode (the flag, else the `throw`
|
|
167
|
+
default): `throw` ends the process unless something else listens for rejections, `strict` even
|
|
168
|
+
then (both unless your app listens for `uncaughtException`, as in Node), `warn` prints a warning,
|
|
169
|
+
`none` stays silent. A rejection that ends the process is `fatal`,
|
|
170
|
+
otherwise `error`. Override it with `{ unhandledRejection: "warn" }`.
|
|
171
|
+
* `shutdown()` removes the handlers, as does the returned function.
|
|
172
|
+
|
|
173
|
+
### Frameworks
|
|
174
|
+
|
|
175
|
+
The SDK has no framework dependencies; the middlewares are typed structurally.
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
// Express
|
|
179
|
+
app.use(guidely.expressRequestHandler()); // first: a scope per request
|
|
180
|
+
// …routes…
|
|
181
|
+
app.use(guidely.expressErrorHandler()); // after the routes, before your own error handlers
|
|
182
|
+
|
|
183
|
+
// Fastify
|
|
184
|
+
await app.register(guidely.fastifyPlugin()); // request scopes + an onError hook
|
|
185
|
+
// or only the hook: app.addHook("onError", guidely.fastifyErrorHook());
|
|
186
|
+
// inside your own setErrorHandler: guidely.captureException(error, { request });
|
|
187
|
+
|
|
188
|
+
// Koa
|
|
189
|
+
app.use(guidely.koaMiddleware()); // first: reports errors from downstream and rethrows them
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Errors are reported with the request (method, URL, query string with token-like values
|
|
193
|
+
`[Filtered]`, headers without cookies, `Authorization`, API keys or forwarding addresses) and the
|
|
194
|
+
transaction `METHOD /route` when the router exposes the matched route. By default only errors with
|
|
195
|
+
no HTTP status or a 5xx status (`status`, `statusCode`) are reported; pass
|
|
196
|
+
`{ shouldHandleError: (err) => boolean }` to change that. An error passing through two hooks is
|
|
197
|
+
reported once.
|
|
198
|
+
|
|
199
|
+
### Options
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
const guidely = new Guidely({
|
|
203
|
+
secretKey: process.env.GUIDELY_SECRET_KEY!,
|
|
204
|
+
environment: "production",
|
|
205
|
+
appVersion: "2.4.1",
|
|
206
|
+
errors: {
|
|
207
|
+
sampleRate: 1,
|
|
208
|
+
ignoreErrors: ["ECONNRESET", /^AbortError/],
|
|
209
|
+
beforeSend: (event, hint) => (event.tags?.area === "health" ? null : event),
|
|
210
|
+
beforeBreadcrumb: (crumb) => (crumb.category === "console" && crumb.level === "warning" ? null : crumb),
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
| `errors.` option | Default | |
|
|
216
|
+
|---|---|---|
|
|
217
|
+
| `sampleRate` | `1` | Share of errors sent (0–1). |
|
|
218
|
+
| `ignoreErrors` | `[]` | Strings match `"<type>: <message>"` or the message as substrings; RegExps as patterns. |
|
|
219
|
+
| `beforeSend` | — | `(event, hint) => event \| null`, may be async. Edit the Sentry-shaped event or return `null` to drop it. A hook that throws leaves the event unchanged. |
|
|
220
|
+
| `beforeBreadcrumb` | — | `(crumb, hint) => crumb \| null`. |
|
|
221
|
+
| `release` | `appVersion` | The release errors belong to. |
|
|
222
|
+
| `serverName` | `os.hostname()` | |
|
|
223
|
+
| `inAppExclude` | `[]` | Paths (substrings or RegExps) that are not your code. |
|
|
224
|
+
| `captureConsole` | `true` | `warn` + `error` breadcrumbs; a list picks the levels (`["log", "warn", "error"]`); `false` turns them off. |
|
|
225
|
+
| `captureHttp` | `true` | Breadcrumbs for outgoing requests. |
|
|
226
|
+
| `contextLines` | `5` | Source lines before and after in-app frames (up to 10; `0` turns it off). |
|
|
227
|
+
| `sendDefaultPii` | `false` | Send the client IP of requests (`user.ip_address`, forwarding headers). |
|
|
228
|
+
| `maxBreadcrumbs` | `100` | Breadcrumbs kept per scope (at most 100). |
|
|
229
|
+
|
|
230
|
+
Credentials never leave the process: keys that look like `password`, `secret`, `token`, `card`,
|
|
231
|
+
`apiKey`… are removed from extras, contexts, the user, breadcrumb data and event properties, before
|
|
232
|
+
and again after `beforeSend`. Each event carries the `runtime`, `os`, `device`, `app` and `culture`
|
|
233
|
+
contexts. The wire format is in [docs/error-monitoring/sdks.md](../../docs/error-monitoring/sdks.md):
|
|
234
|
+
an `exception` message in the server batch, with Guidely `properties` and a Sentry-shaped `detail`
|
|
235
|
+
trimmed to 64 KB (breadcrumbs first, then source lines).
|
|
92
236
|
|
|
93
237
|
## Validation and errors
|
|
94
238
|
|
|
95
239
|
Invalid calls are dropped and reported, never thrown: `identify` needs a `userId`; `track` needs an
|
|
96
240
|
`event` (1–120 characters) and a `userId` or `anonymousId`; `group` needs an `accountId`. Ids are
|
|
97
241
|
strings of 1–256 characters (integers are converted); `traits` and `properties` must be plain
|
|
98
|
-
objects. Messages over 32 KB are dropped.
|
|
242
|
+
objects. Messages over 32 KB are dropped (80 KB for an exception, which first loses its `detail`).
|
|
99
243
|
|
|
100
244
|
`onError` receives a `GuidelyError` with a `code`:
|
|
101
245
|
|
|
102
246
|
| `code` | |
|
|
103
247
|
|---|---|
|
|
104
248
|
| `invalid_message` | A call was invalid and dropped. |
|
|
105
|
-
| `message_too_large` | A message serialized to more than 32 KB. |
|
|
249
|
+
| `message_too_large` | A message serialized to more than 32 KB (an exception: 80 KB). |
|
|
106
250
|
| `queue_full` | The queue was full; `count` oldest messages were dropped. |
|
|
107
251
|
| `delivery_failed` | A batch of `count` messages was rejected (`status`: `400`, `401`/`403` = bad key or missing scope, …), failed after every retry (`status` `0` = unreachable) or was cut short by the shutdown timeout. |
|
|
108
252
|
| `closed` | A call was made after `shutdown()`. |
|