@faststats/nitro 0.5.0 → 0.7.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.
@@ -1,6 +1,3 @@
1
- import { createRequire } from "node:module";
2
- import { readFileSync } from "node:fs";
3
- import { version } from "node:os";
4
1
  //#region src/options.ts
5
2
  const DEFAULT_ENDPOINT = "https://metrics.faststats.dev/v1/error";
6
3
  const envInt = (key, fallback) => {
@@ -23,18 +20,9 @@ function resolveFaststatsNitroOptions(overrides = {}) {
23
20
  };
24
21
  }
25
22
  const SDK_NAME = "@faststats/nitro";
26
- const SDK_VERSION = "0.5.0";
23
+ const SDK_VERSION = "0.7.0";
27
24
  //#endregion
28
25
  //#region src/context.ts
29
- function readPackageVersion(name) {
30
- try {
31
- const raw = readFileSync(createRequire(import.meta.url).resolve(`${name}/package.json`), "utf8");
32
- const pkg = JSON.parse(raw);
33
- return typeof pkg.version === "string" ? pkg.version : void 0;
34
- } catch {
35
- return;
36
- }
37
- }
38
26
  function detectRuntime() {
39
27
  if (typeof Bun !== "undefined" && Bun.version !== void 0) return {
40
28
  runtime: "bun",
@@ -67,15 +55,11 @@ function buildServerReportContext(extra = {}) {
67
55
  return {
68
56
  source: "@faststats/nitro",
69
57
  ...extra,
70
- nitroVersion: readPackageVersion("nitropack") ?? readPackageVersion("nitro"),
71
58
  nitroPreset: process?.env?.NITRO_PRESET,
72
59
  runtime,
73
60
  runtimeVersion,
74
- os: process?.release?.name ?? "unknown",
75
- osVersion: version() || "unknown",
76
61
  platform: process?.platform ?? "unknown",
77
- arch: process?.arch ?? "unknown",
78
- ...process?.cwd?.() ? { cwd: process.cwd() } : {}
62
+ arch: process?.arch ?? "unknown"
79
63
  };
80
64
  }
81
65
  //#endregion
@@ -97,39 +81,19 @@ function serializeCause(cause) {
97
81
  message: cause
98
82
  };
99
83
  }
100
- function causeFingerprint(cause) {
101
- if (!cause) return "";
102
- return `${cause.error}\0${cause.message ?? ""}\0${causeFingerprint(cause.cause)}`;
103
- }
104
- function hashMessage(message, cause) {
105
- const key = [
106
- "error",
107
- message,
108
- "",
109
- "",
110
- causeFingerprint(cause)
111
- ].join("\0");
112
- let a = 2166136261;
113
- let b = 3598710387;
114
- for (let i = 0; i < key.length; i++) {
115
- const c = key.charCodeAt(i);
116
- a = Math.imul(a ^ c, 16777619);
117
- b = Math.imul(b ^ c, 2246822519);
118
- }
119
- return `err_${(a >>> 0).toString(16).padStart(8, "0")}${(b >>> 0).toString(16).padStart(8, "0")}`;
120
- }
121
- function hashServerError(error) {
84
+ function normalizeServerError(error) {
122
85
  const err = error instanceof Error ? error : new Error(String(error));
123
86
  const name = err.name?.trim() || "Error";
124
87
  const cause = serializeCause(err.cause);
88
+ const entry = {
89
+ error: name,
90
+ message: err.message,
91
+ stack: parseStack(err.stack),
92
+ ...cause ? { cause } : {}
93
+ };
125
94
  return {
126
- hash: hashMessage(err.message, cause),
127
- entry: {
128
- error: name,
129
- message: err.message,
130
- stack: parseStack(err.stack),
131
- ...cause ? { cause } : {}
132
- }
95
+ key: JSON.stringify(entry),
96
+ entry
133
97
  };
134
98
  }
135
99
  //#endregion
@@ -139,55 +103,40 @@ const defaultRuntime = {
139
103
  setTimeout: (callback, delay) => setTimeout(callback, delay),
140
104
  clearTimeout: (timer) => clearTimeout(timer),
141
105
  now: () => Date.now(),
142
- randomUUID: () => crypto.randomUUID?.() ?? `sess-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`,
143
106
  warn: (...args) => console.warn(...args)
144
107
  };
145
108
  function definedStrings(context) {
146
109
  return Object.fromEntries(Object.entries(context).filter(([, value]) => value !== void 0 && value !== ""));
147
110
  }
111
+ function mergeBucket(target, incoming) {
112
+ target.count += incoming.count;
113
+ }
148
114
  function mergeBuckets(target, incoming) {
149
115
  for (const [hash, bucket] of incoming) {
150
116
  const existing = target.get(hash);
151
- if (!existing) {
152
- target.set(hash, { ...bucket });
153
- continue;
154
- }
155
- existing.count += bucket.count;
156
- if (bucket.path !== void 0) existing.path = bucket.path;
157
- if (bucket.tags.length > 0) existing.tags = bucket.tags;
117
+ if (existing) mergeBucket(existing, bucket);
118
+ else target.set(hash, { ...bucket });
158
119
  }
159
120
  }
160
- function buildPayload(buckets, options, sessionId) {
161
- const errors = [...buckets.entries()].map(([hash, bucket]) => ({
121
+ function buildPayload(buckets, options) {
122
+ const errors = [];
123
+ for (const bucket of buckets.values()) errors.push({
162
124
  ...bucket.entry,
163
- hash,
164
- count: bucket.count
165
- }));
166
- const tags = [...new Set([...buckets.values()].flatMap((b) => b.tags))];
167
- let route;
168
- let routeCount = -1;
169
- const custom = {};
170
- for (const bucket of buckets.values()) {
171
- if (bucket.path !== void 0 && bucket.count > routeCount) {
172
- routeCount = bucket.count;
173
- route = bucket.path;
125
+ count: bucket.count,
126
+ context: {
127
+ ...bucket.path ? { route: bucket.path } : {},
128
+ ...bucket.tags.length ? { tags: bucket.tags.join(",") } : {},
129
+ ...bucket.custom
174
130
  }
175
- for (const [key, value] of Object.entries(bucket.custom)) if (!(key in custom)) custom[key] = value;
176
- }
131
+ });
177
132
  return {
178
133
  errors,
179
- sessionId,
134
+ language: "javascript",
135
+ ...options.sessionId ? { sessionId: options.sessionId } : {},
180
136
  ...options.buildId ? { buildId: options.buildId } : {},
181
137
  sdkName: SDK_NAME,
182
138
  sdkVersion: SDK_VERSION,
183
- context: {
184
- ...definedStrings(buildServerReportContext({
185
- route,
186
- plugin: options.pluginId,
187
- tags: tags.length > 0 ? tags.join(",") : void 0
188
- })),
189
- ...custom
190
- }
139
+ context: definedStrings(buildServerReportContext({ plugin: options.pluginId }))
191
140
  };
192
141
  }
193
142
  function createNitroErrorReporter(options, runtime) {
@@ -196,16 +145,10 @@ function createNitroErrorReporter(options, runtime) {
196
145
  let flushing = false;
197
146
  let consecutiveFailures = 0;
198
147
  let pausedUntil = 0;
199
- let sessionId = options.sessionId;
200
- const getSessionId = () => {
201
- sessionId ??= runtime.randomUUID();
202
- return sessionId;
203
- };
204
148
  const clearTimer = () => {
205
- if (timer !== null) {
206
- runtime.clearTimeout(timer);
207
- timer = null;
208
- }
149
+ if (timer === null) return;
150
+ runtime.clearTimeout(timer);
151
+ timer = null;
209
152
  };
210
153
  const scheduleFlush = () => {
211
154
  if (timer !== null) return;
@@ -222,7 +165,7 @@ function createNitroErrorReporter(options, runtime) {
222
165
  "Content-Type": "application/json",
223
166
  Authorization: `Bearer ${options.token}`
224
167
  },
225
- body: JSON.stringify(buildPayload(snapshot, options, getSessionId()))
168
+ body: JSON.stringify(buildPayload(snapshot, options))
226
169
  });
227
170
  if (response.ok) return true;
228
171
  if (options.debug) runtime.warn("[faststats nitro] report failed", response.status, await response.text());
@@ -263,31 +206,30 @@ function createNitroErrorReporter(options, runtime) {
263
206
  if (options.debug) runtime.warn("[faststats nitro] FASTSTATS_TOKEN not set; skipping error report");
264
207
  return;
265
208
  }
266
- if (runtime.now() < pausedUntil) return;
267
- const { hash, entry } = hashServerError(error);
268
- const existing = buckets.get(hash);
269
- if (existing) {
270
- existing.count += 1;
271
- if (args.path !== void 0) existing.path = args.path;
272
- if (args.tags.length > 0) existing.tags = args.tags;
273
- } else {
274
- let custom = {};
275
- if (options.getContext) try {
276
- custom = await options.getContext({
277
- error,
278
- ...args
279
- }) ?? {};
280
- } catch {
281
- custom = {};
282
- }
283
- buckets.set(hash, {
284
- entry,
285
- count: 1,
286
- path: args.path,
287
- tags: args.tags,
288
- custom
289
- });
209
+ const { key, entry } = normalizeServerError(error);
210
+ let custom = {};
211
+ if (options.getContext) try {
212
+ custom = await options.getContext({
213
+ error,
214
+ ...args
215
+ }) ?? {};
216
+ } catch {}
217
+ let customKey = "";
218
+ try {
219
+ customKey = JSON.stringify(custom);
220
+ } catch {
221
+ custom = {};
290
222
  }
223
+ const bucketKey = `${key}\0${args.path ?? ""}\0${args.tags.join("\0")}\0${customKey}`;
224
+ const existing = buckets.get(bucketKey);
225
+ if (existing) mergeBucket(existing, { count: 1 });
226
+ else buckets.set(bucketKey, {
227
+ entry,
228
+ count: 1,
229
+ path: args.path,
230
+ tags: args.tags,
231
+ custom
232
+ });
291
233
  if (buckets.size >= options.maxBatchHashes) {
292
234
  clearTimer();
293
235
  await flush();
@@ -299,18 +241,22 @@ function createNitroErrorReporter(options, runtime) {
299
241
  //#endregion
300
242
  //#region src/plugin-hook.ts
301
243
  function extractPath(event) {
302
- if (!event || typeof event !== "object") return;
244
+ if (!event || typeof event !== "object") return void 0;
303
245
  const e = event;
304
- if (e.path) return e.path;
305
- const url = e.node?.req?.url;
306
- return url !== void 0 ? String(url) : void 0;
246
+ const value = e.path ?? e.node?.req?.url;
247
+ if (value === void 0) return void 0;
248
+ try {
249
+ return new URL(String(value), "http://faststats.local").pathname;
250
+ } catch {
251
+ return String(value).split(/[?#]/, 1)[0];
252
+ }
307
253
  }
308
254
  function createNitroErrorHook(resolved) {
309
255
  const reporter = createNitroErrorReporter(resolved, defaultRuntime);
310
256
  return (error, context) => {
311
257
  reporter.report(error, {
312
258
  path: extractPath(context.event),
313
- tags: Array.isArray(context.tags) ? context.tags : []
259
+ tags: Array.isArray(context.tags) ? context.tags.filter((tag) => typeof tag === "string") : []
314
260
  });
315
261
  };
316
262
  }
package/dist/v2.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as resolveFaststatsNitroOptions, t as createNitroErrorHook } from "./plugin-hook-BMw88N35.mjs";
1
+ import { n as resolveFaststatsNitroOptions, t as createNitroErrorHook } from "./plugin-hook-4hE4T3qk.mjs";
2
2
  import { defineNitroPlugin } from "nitropack/runtime";
3
3
  //#region src/v2.ts
4
4
  function createFaststatsNitroPluginV2(options) {
package/dist/v3.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as resolveFaststatsNitroOptions, t as createNitroErrorHook } from "./plugin-hook-BMw88N35.mjs";
1
+ import { n as resolveFaststatsNitroOptions, t as createNitroErrorHook } from "./plugin-hook-4hE4T3qk.mjs";
2
2
  import { definePlugin } from "nitro";
3
3
  //#region src/v3.ts
4
4
  function createFaststatsNitroPluginV3(options) {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "git",
5
5
  "url": "https://github.com/faststats-dev/faststats-javascript.git"
6
6
  },
7
- "version": "0.5.0",
7
+ "version": "0.7.0",
8
8
  "type": "module",
9
9
  "main": "./dist/v2.mjs",
10
10
  "module": "./dist/v2.mjs",