@autter/runtime-node 1.8.0 → 1.9.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 CHANGED
@@ -127,7 +127,39 @@ export const POST = createBrowserRelayFetchHandler({
127
127
  ```
128
128
 
129
129
  Options: `endpoint` (default `https://otlp.autter.dev`), `maxBodyBytes`
130
- (default 64 KB), `onError`.
130
+ (default 64 KB), `perIpRateLimit` (default 120 requests a minute per client;
131
+ `false` when a WAF already limits the route), `trustProxy`, `redact`,
132
+ `compatCheck`, `onError`.
133
+
134
+ **Rate-limit key and `trustProxy`.** The relay route is public, so it limits
135
+ each visitor. Any caller can write anything into `X-Forwarded-For`, so by
136
+ default (`trustProxy: "auto"`) the relay never takes that header's first entry.
137
+ Proxies append the address they saw to the right of the header, or overwrite
138
+ it, so the relay reads it right to left and takes the first public address,
139
+ skipping hops on your own network. That works behind load balancers, CDNs and
140
+ hosting platforms with no setup:
141
+
142
+ | Where the relay runs | What `"auto"` keys |
143
+ | --- | --- |
144
+ | Node, reached directly | The connection's own address; the header is ignored |
145
+ | Node behind a load balancer or ingress on your network | The address that proxy appended |
146
+ | Next.js route handlers, Vercel, Netlify, Deno Deploy and other fetch runtimes | The rightmost public entry, which the platform's edge wrote |
147
+ | Cloudflare Workers | `CF-Connecting-IP` |
148
+
149
+ Other settings:
150
+
151
+ - `trustProxy: 1` (or any count) when exactly that many proxies you control
152
+ sit in front, such as a Node origin behind Cloudflare with no load balancer.
153
+ - `trustProxy: true` when every proxy in front strips client values: the
154
+ leftmost entry.
155
+ - `trustProxy: false` never reads the header: the socket address in Node, one
156
+ shared bucket in fetch runtimes.
157
+ - `clientIp: (request) => string` to supply the address yourself, for example
158
+ Deno's `info.remoteAddr` or Bun's `server.requestIP`.
159
+
160
+ Strings from environment variables work too (`"auto"`, `"false"`, `"2"`).
161
+ A fetch handler exposed straight to the internet with no proxy in front can
162
+ still be fooled by a forged header; set `trustProxy: false` or `clientIp` there.
131
163
 
132
164
  ## 2. Server tracker
133
165
 
@@ -276,6 +308,42 @@ settings (drop or sample by error type, message, service, environment,
276
308
  route, release or source; ingester 1.9.0). The ingester also collapses a
277
309
  looping error into one sample a minute with an exact count.
278
310
 
311
+ ### Caught errors and deliberate fallbacks
312
+
313
+ Errors you catch and log count on their own — no `captureException` needed.
314
+ An `Error` passed to `console.error`, pino, winston, bunyan or
315
+ `runtimeLogger.error` becomes an occurrence at `error` severity, with the log
316
+ line as context and the error's stack, marked
317
+ `autter.handling: "caught"`. A log line with no error object is untouched, and
318
+ an error you also pass to `captureException` in the same tick is reported
319
+ once. A caught error never fails the request it happened in, and handled
320
+ occurrences stay out of your error rate — they are issues to see, not
321
+ failures. A burst limit per error kind keeps ten at once, then one every ten
322
+ seconds; the ones left out are counted.
323
+
324
+ ```ts
325
+ initAutterServer({ ... }); // on by default
326
+ initAutterServer({ ..., captureLoggedErrors: false }); // off
327
+ initAutterServer({ ..., captureLoggedErrors: { console: false } }); // keep loggers, not console
328
+ ```
329
+
330
+ When you mean to ignore an error — a fallback you chose — say so in one line:
331
+
332
+ ```ts
333
+ try {
334
+ prices = await pricingService.get();
335
+ } catch (err) {
336
+ runtimeContext.degraded(err, "pricing service down, using cached prices");
337
+ prices = cached;
338
+ }
339
+ ```
340
+
341
+ `runtimeContext.degraded(err, reason)` records the error at `warning`
342
+ severity, marked `autter.handling: "deliberate"` with the reason (grouped by
343
+ its code when the error is coded), and never pages anyone. The current
344
+ request or operation ends `degraded` unless it already failed or set its own
345
+ outcome — so a crash, a caught error and a deliberate fallback are told apart.
346
+
279
347
  ### Unhandled promise rejections
280
348
 
281
349
  Recorded as errors (severity `error`, `autter.unhandled_rejection: true`, not
@@ -5,7 +5,7 @@
5
5
 
6
6
 
7
7
 
8
- var _chunkMOZOUEMAcjs = require('./chunk-MOZOUEMA.cjs');
8
+ var _chunkBHYHORUDcjs = require('./chunk-BHYHORUD.cjs');
9
9
 
10
10
  // src/doctor.ts
11
11
  var _fs = require('fs');
@@ -30,19 +30,19 @@ function installedSdks(cwd) {
30
30
  }
31
31
  if (_path.dirname.call(void 0, dir) === dir) break;
32
32
  }
33
- if (!found.has(_chunkMOZOUEMAcjs.SDK_IDENTITY.name)) found.set(_chunkMOZOUEMAcjs.SDK_IDENTITY.name, _chunkMOZOUEMAcjs.SDK_IDENTITY.version);
33
+ if (!found.has(_chunkBHYHORUDcjs.SDK_IDENTITY.name)) found.set(_chunkBHYHORUDcjs.SDK_IDENTITY.name, _chunkBHYHORUDcjs.SDK_IDENTITY.version);
34
34
  return [...found].map(([name, version]) => ({ name, version }));
35
35
  }
36
36
  function featuresInUse(sdks) {
37
- return _chunkMOZOUEMAcjs.COMPAT_MANIFEST.features.filter(
37
+ return _chunkBHYHORUDcjs.COMPAT_MANIFEST.features.filter(
38
38
  (feature) => sdks.some((sdk) => {
39
39
  const min = feature.sdks[sdk.name];
40
- return min !== void 0 && _chunkMOZOUEMAcjs.versionAtLeast.call(void 0, sdk.version, min) === true;
40
+ return min !== void 0 && _chunkBHYHORUDcjs.versionAtLeast.call(void 0, sdk.version, min) === true;
41
41
  })
42
42
  ).map((feature) => feature.id);
43
43
  }
44
44
  async function checkKey(endpoint, key, timeoutMs) {
45
- const res = await _chunkMOZOUEMAcjs.compatRequest.call(void 0, "POST", `${endpoint}/v1/traces`, {
45
+ const res = await _chunkBHYHORUDcjs.compatRequest.call(void 0, "POST", `${endpoint}/v1/traces`, {
46
46
  body: JSON.stringify({ resourceSpans: [] }),
47
47
  headers: { authorization: `Bearer ${key}` },
48
48
  unref: false,
@@ -62,10 +62,10 @@ async function runDoctor(options = {}) {
62
62
  const features = _optionalChain([options, 'access', _ => _.features, 'optionalAccess', _2 => _2.length]) ? options.features : featuresInUse(sdks);
63
63
  const routes = [
64
64
  ...new Set(
65
- features.map((id) => _optionalChain([_chunkMOZOUEMAcjs.COMPAT_MANIFEST, 'access', _3 => _3.features, 'access', _4 => _4.find, 'call', _5 => _5((f) => f.id === id), 'optionalAccess', _6 => _6.route])).filter((route) => Boolean(route))
65
+ features.map((id) => _optionalChain([_chunkBHYHORUDcjs.COMPAT_MANIFEST, 'access', _3 => _3.features, 'access', _4 => _4.find, 'call', _5 => _5((f) => f.id === id), 'optionalAccess', _6 => _6.route])).filter((route) => Boolean(route))
66
66
  )
67
67
  ];
68
- const { info, error } = await _chunkMOZOUEMAcjs.fetchIngesterCompat.call(void 0, endpoint, { probeRoutes: routes, unref: false, timeoutMs });
68
+ const { info, error } = await _chunkBHYHORUDcjs.fetchIngesterCompat.call(void 0, endpoint, { probeRoutes: routes, unref: false, timeoutMs });
69
69
  const issues = [];
70
70
  const seen = /* @__PURE__ */ new Set();
71
71
  const add = (issue) => {
@@ -75,15 +75,15 @@ async function runDoctor(options = {}) {
75
75
  issues.push(issue);
76
76
  }
77
77
  };
78
- _chunkMOZOUEMAcjs.evaluateCompat.call(void 0, { features, ingester: info }).forEach(add);
78
+ _chunkBHYHORUDcjs.evaluateCompat.call(void 0, { features, ingester: info }).forEach(add);
79
79
  for (const sdk of sdks) {
80
80
  if (_optionalChain([options, 'access', _7 => _7.features, 'optionalAccess', _8 => _8.length])) {
81
- _chunkMOZOUEMAcjs.evaluateCompat.call(void 0, { features: options.features, ingester: null, sdk }).forEach(add);
81
+ _chunkBHYHORUDcjs.evaluateCompat.call(void 0, { features: options.features, ingester: null, sdk }).forEach(add);
82
82
  }
83
83
  }
84
84
  const key = options.key ? await checkKey(endpoint, options.key, timeoutMs) : void 0;
85
85
  const featureRows = features.flatMap((id) => {
86
- const feature = _chunkMOZOUEMAcjs.COMPAT_MANIFEST.features.find((f) => f.id === id);
86
+ const feature = _chunkBHYHORUDcjs.COMPAT_MANIFEST.features.find((f) => f.id === id);
87
87
  if (!feature) return [];
88
88
  const bad = issues.some((issue) => issue.feature === id);
89
89
  const known = _optionalChain([info, 'optionalAccess', _9 => _9.version]) != null || feature.route !== void 0 && _optionalChain([info, 'optionalAccess', _10 => _10.routes, 'optionalAccess', _11 => _11[feature.route]]) !== void 0;
@@ -153,7 +153,7 @@ Checks that your Autter SDKs, ingester and ClickHouse schema versions match.
153
153
  Options:
154
154
  --endpoint <url> Ingester base URL (default: $AUTTER_ENDPOINT, $OTEL_EXPORTER_OTLP_ENDPOINT, or ${DEFAULT_ENDPOINT})
155
155
  --key <key> Also verify an ingest key (default: $AUTTER_RUNTIME_KEY)
156
- --features <a,b> Only check these features (${_chunkMOZOUEMAcjs.COMPAT_MANIFEST.features.map((f) => f.id).join(", ")})
156
+ --features <a,b> Only check these features (${_chunkBHYHORUDcjs.COMPAT_MANIFEST.features.map((f) => f.id).join(", ")})
157
157
  --json Machine-readable output
158
158
  --timeout <ms> Per-request timeout (default 5000)
159
159
 
@@ -170,6 +170,43 @@ var compat_manifest_default = {
170
170
  ingester: "1.9.0",
171
171
  migrations: ["0021-occurrence-collapsed-count"],
172
172
  sdks: {}
173
+ },
174
+ {
175
+ id: "edge_request_counts",
176
+ title: "Edge requests counted from their request summaries",
177
+ ingester: "1.10.0",
178
+ migrations: [],
179
+ route: "/v1/logs",
180
+ sdks: { "@autter/runtime-edge": "1.3.0" }
181
+ },
182
+ {
183
+ id: "caught_errors",
184
+ title: "Caught errors and deliberate fallbacks recorded as handled occurrences",
185
+ ingester: "1.11.0",
186
+ migrations: [],
187
+ sdks: { "@autter/runtime-node": "1.9.0", "@autter/runtime-edge": "1.4.0", "@autter/runtime-next": "1.9.0", "@autter/runtime-browser": "1.8.0" }
188
+ },
189
+ {
190
+ id: "route_percentiles",
191
+ title: "Per-route p95 and p99 latency in the 1-minute rollups",
192
+ ingester: "1.11.0",
193
+ migrations: ["0022-route-latency-1m"],
194
+ sdks: {}
195
+ },
196
+ {
197
+ id: "server_source_maps",
198
+ title: "Server bundles symbolicated, with source context lines",
199
+ ingester: "1.11.0",
200
+ migrations: [],
201
+ route: "/v1/sourcemaps",
202
+ sdks: { "@autter/runtime-cli": "1.3.0", "@autter/runtime-node": "1.9.0", "@autter/runtime-edge": "1.4.0", "@autter/runtime-next": "1.9.0" }
203
+ },
204
+ {
205
+ id: "cause_fingerprint",
206
+ title: "Wrapped errors grouped with their root cause (cause-v1)",
207
+ ingester: "1.11.0",
208
+ migrations: [],
209
+ sdks: { "@autter/runtime-node": "1.9.0", "@autter/runtime-edge": "1.4.0", "@autter/runtime-next": "1.9.0" }
173
210
  }
174
211
  ]
175
212
  };
@@ -515,7 +552,7 @@ var _https = require('https');
515
552
  // package.json
516
553
  var package_default = {
517
554
  name: "@autter/runtime-node",
518
- version: "1.8.0",
555
+ version: "1.9.0",
519
556
  description: "Autter Runtime for Node.js: request wide events, coded errors, operation logging, same-origin browser relay and a curated OpenTelemetry server tracker",
520
557
  license: "MIT",
521
558
  type: "module",
@@ -560,16 +597,20 @@ var package_default = {
560
597
  "@opentelemetry/sdk-metrics": "^1.30.1",
561
598
  "@opentelemetry/sdk-node": "^0.57.1",
562
599
  "@opentelemetry/sdk-trace-base": "^1.30.1",
563
- "@opentelemetry/semantic-conventions": "^1.28.0"
600
+ "@opentelemetry/semantic-conventions": "^1.28.0",
601
+ "require-in-the-middle": "^7.5.2"
564
602
  },
565
603
  devDependencies: {
566
604
  "@autter/runtime-core": "0.0.0",
567
605
  "@types/node": "^22.10.0",
606
+ bunyan: "^1.8.15",
568
607
  express: "^4.21.2",
569
608
  fastify: "^5.12.5",
570
609
  koa: "^3.2.1",
610
+ pino: "^10.4.0",
571
611
  tsup: "^8.3.5",
572
- typescript: "^5.7.0"
612
+ typescript: "^5.7.0",
613
+ winston: "^3.19.0"
573
614
  },
574
615
  engines: {
575
616
  node: ">=20"