@hawk.so/nextjs 0.0.0-stage → 1.1.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.
@@ -0,0 +1,500 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/server/index.ts
31
+ var server_exports = {};
32
+ __export(server_exports, {
33
+ HawkErrorBoundary: () => import_react.HawkErrorBoundary,
34
+ HawkGlobalError: () => import_react.HawkGlobalError,
35
+ breadcrumbs: () => breadcrumbs,
36
+ clearUser: () => clearUser,
37
+ flush: () => flush,
38
+ init: () => init,
39
+ onRequestError: () => onRequestError,
40
+ onRouterTransitionStart: () => onRouterTransitionStart,
41
+ send: () => send,
42
+ setContext: () => setContext,
43
+ setUser: () => setUser,
44
+ useHawkError: () => import_react.useHawkError
45
+ });
46
+ module.exports = __toCommonJS(server_exports);
47
+
48
+ // src/server/sdk.ts
49
+ var import_core3 = require("@hawk.so/core");
50
+
51
+ // src/common/carrier.ts
52
+ var CARRIER_KEY = /* @__PURE__ */ Symbol.for("hawk.nextjs");
53
+ function getCarrier() {
54
+ const store = globalThis;
55
+ store[CARRIER_KEY] ??= {};
56
+ return store[CARRIER_KEY];
57
+ }
58
+
59
+ // src/common/control-flow.ts
60
+ var CONTROL_FLOW_DIGEST_PREFIXES = [
61
+ "NEXT_REDIRECT",
62
+ "NEXT_HTTP_ERROR_FALLBACK;",
63
+ "NEXT_NOT_FOUND",
64
+ "BAILOUT_TO_CLIENT_SIDE_RENDERING",
65
+ "DYNAMIC_SERVER_USAGE",
66
+ "NEXT_PRERENDER_INTERRUPTED",
67
+ "HANGING_PROMISE_REJECTION"
68
+ ];
69
+ var STATIC_GEN_BAILOUT_CODE = "NEXT_STATIC_GEN_BAILOUT";
70
+ var REACT_POSTPONE_TYPE = /* @__PURE__ */ Symbol.for("react.postpone");
71
+ var MAX_CAUSE_DEPTH = 5;
72
+ function isControlFlowDigest(value) {
73
+ return typeof value === "string" && CONTROL_FLOW_DIGEST_PREFIXES.some((prefix) => value.startsWith(prefix));
74
+ }
75
+ function isNextControlFlowError(error) {
76
+ let current = error;
77
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH; depth++) {
78
+ if (typeof current !== "object" || current === null) {
79
+ return false;
80
+ }
81
+ const value = current;
82
+ if (isControlFlowDigest(value.digest) || isControlFlowDigest(value.message) || value.code === STATIC_GEN_BAILOUT_CODE || value.$$typeof === REACT_POSTPONE_TYPE) {
83
+ return true;
84
+ }
85
+ current = value.cause;
86
+ }
87
+ return false;
88
+ }
89
+ function getDigest(error) {
90
+ if (typeof error === "object" && error !== null) {
91
+ const { digest } = error;
92
+ if (typeof digest === "string" && digest !== "") {
93
+ return digest;
94
+ }
95
+ }
96
+ return void 0;
97
+ }
98
+
99
+ // src/common/error.ts
100
+ function isErrorLike(value) {
101
+ return typeof value === "object" && value !== null && typeof value.message === "string" && typeof value.stack === "string";
102
+ }
103
+ function normalizeError(value) {
104
+ if (value instanceof Error || typeof value === "string") {
105
+ return value;
106
+ }
107
+ if (isErrorLike(value)) {
108
+ const error = new Error(value.message);
109
+ error.name = typeof value.name === "string" ? value.name : error.name;
110
+ error.stack = value.stack;
111
+ return error;
112
+ }
113
+ try {
114
+ return JSON.stringify(value) ?? String(value);
115
+ } catch (_error) {
116
+ return String(value);
117
+ }
118
+ }
119
+
120
+ // src/common/options.ts
121
+ var DEFAULT_FLUSH_TIMEOUT = 2e3;
122
+
123
+ // src/common/request.ts
124
+ var SAFE_HEADERS = ["host", "referer", "user-agent"];
125
+ function pickSafeHeaders(headers) {
126
+ const result = {};
127
+ for (const name of SAFE_HEADERS) {
128
+ const value = headers[name];
129
+ if (value !== void 0) {
130
+ result[name] = Array.isArray(value) ? value.join(", ") : value;
131
+ }
132
+ }
133
+ return result;
134
+ }
135
+ function getRequestAddons(error, request, context) {
136
+ const addons = {
137
+ digest: getDigest(error),
138
+ routerKind: context.routerKind,
139
+ routePath: context.routePath,
140
+ routeType: context.routeType,
141
+ renderSource: context.renderSource,
142
+ revalidateReason: context.revalidateReason,
143
+ renderType: context.renderType,
144
+ method: request.method,
145
+ path: request.path,
146
+ headers: pickSafeHeaders(request.headers)
147
+ };
148
+ return Object.fromEntries(Object.entries(addons).filter(([, value]) => value !== void 0));
149
+ }
150
+
151
+ // src/server/catcher.ts
152
+ var import_core2 = require("@hawk.so/core");
153
+
154
+ // src/common/timeout.ts
155
+ async function settleWithTimeout(promises, timeout) {
156
+ const list = [...promises];
157
+ if (list.length === 0) {
158
+ return true;
159
+ }
160
+ let timer;
161
+ const timeoutReached = new Promise((resolve) => {
162
+ timer = setTimeout(() => resolve(false), timeout);
163
+ });
164
+ const allSettled = Promise.allSettled(list).then(() => true);
165
+ try {
166
+ return await Promise.race([allSettled, timeoutReached]);
167
+ } finally {
168
+ clearTimeout(timer);
169
+ }
170
+ }
171
+
172
+ // src/common/version.ts
173
+ var VERSION = true ? "1.1.0" : "dev";
174
+
175
+ // src/server/stack-parser.ts
176
+ var import_core = require("@hawk.so/core");
177
+ var import_error_stack_parser = __toESM(require("error-stack-parser"), 1);
178
+ var ServerStackParser = class extends import_core.StackParser {
179
+ /**
180
+ * Returns frames of the stack trace
181
+ * @param error - error with the stack trace
182
+ */
183
+ parse(error) {
184
+ const frames = import_error_stack_parser.default.parse(error).map((frame) => ({
185
+ file: frame.fileName ?? "",
186
+ line: frame.lineNumber ?? 0,
187
+ column: frame.columnNumber,
188
+ function: frame.functionName,
189
+ arguments: frame.args
190
+ }));
191
+ return Promise.resolve(frames);
192
+ }
193
+ };
194
+
195
+ // src/server/transport.ts
196
+ var REQUEST_TIMEOUT = 1e4;
197
+ var FetchTransport = class {
198
+ /**
199
+ * Collector endpoint
200
+ */
201
+ endpoint;
202
+ /**
203
+ * Requests in progress
204
+ */
205
+ pending = /* @__PURE__ */ new Set();
206
+ /**
207
+ * @param endpoint - collector endpoint, e.g. `https://<integration id>.k1.hawk.so/`
208
+ */
209
+ constructor(endpoint) {
210
+ this.endpoint = endpoint;
211
+ }
212
+ /**
213
+ * Sends the event
214
+ * @param message - event with the token and the catcher type
215
+ */
216
+ send(message) {
217
+ const request = this.post(message).finally(() => {
218
+ this.pending.delete(request);
219
+ });
220
+ this.pending.add(request);
221
+ return request;
222
+ }
223
+ /**
224
+ * Waits for the requests in progress
225
+ * @param timeout - max time to wait, in milliseconds
226
+ * @returns false when the timeout is reached
227
+ */
228
+ flush(timeout) {
229
+ return settleWithTimeout(this.pending, timeout);
230
+ }
231
+ /**
232
+ * Posts the event to the collector
233
+ * @param message - event with the token and the catcher type
234
+ */
235
+ async post(message) {
236
+ const controller = new AbortController();
237
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
238
+ try {
239
+ const response = await fetch(this.endpoint, {
240
+ method: "POST",
241
+ headers: [["Content-Type", "application/json"]],
242
+ body: JSON.stringify(message),
243
+ signal: controller.signal
244
+ });
245
+ if (!response.ok) {
246
+ throw new Error(`Collector responded with ${response.status} ${response.statusText}`);
247
+ }
248
+ } finally {
249
+ clearTimeout(timer);
250
+ }
251
+ }
252
+ };
253
+
254
+ // src/server/catcher.ts
255
+ var usersByError = /* @__PURE__ */ new WeakMap();
256
+ var noopStorage = {
257
+ getItem: () => null,
258
+ setItem: () => void 0,
259
+ removeItem: () => void 0
260
+ };
261
+ var zeroRandomGenerator = {
262
+ getRandomNumbers: (length) => new Uint8Array(length)
263
+ };
264
+ var ServerUserManager = class extends import_core2.HawkUserManager {
265
+ /**
266
+ * Creates a manager that never stores the user
267
+ */
268
+ constructor() {
269
+ super(noopStorage, zeroRandomGenerator);
270
+ }
271
+ };
272
+ var ServerUserProcessor = class {
273
+ /**
274
+ * @param payload - event being built
275
+ * @param error - sent error
276
+ */
277
+ apply(payload, error) {
278
+ const user = typeof error === "object" ? usersByError.get(error) : void 0;
279
+ if (user !== void 0) {
280
+ payload.user = user;
281
+ } else {
282
+ delete payload.user;
283
+ }
284
+ return payload;
285
+ }
286
+ };
287
+ function getDefaultEndpoint(token) {
288
+ return `https://${(0, import_core2.decodeIntegrationId)(token)}.k1.hawk.so/`;
289
+ }
290
+ var ServerCatcher = class extends import_core2.BaseCatcher {
291
+ /**
292
+ * Transport kept to wait for the events in progress
293
+ */
294
+ fetchTransport;
295
+ /**
296
+ * Current runtime: `nodejs` or `edge`
297
+ */
298
+ runtime;
299
+ /**
300
+ * Events being built: the stack trace is parsed asynchronously before the event gets to the transport
301
+ */
302
+ pending = /* @__PURE__ */ new Set();
303
+ /**
304
+ * @param settings - token, release and other settings
305
+ * @throws when the token is invalid and the collector endpoint is not set
306
+ */
307
+ constructor(settings) {
308
+ const transport = new FetchTransport(settings.collectorEndpoint ?? getDefaultEndpoint(settings.token));
309
+ super(settings.token, transport, new ServerUserManager(), settings.release, settings.context, settings.beforeSend);
310
+ this.fetchTransport = transport;
311
+ this.runtime = settings.runtime;
312
+ this.addMessageProcessor(new ServerUserProcessor());
313
+ this.stackParser = new ServerStackParser();
314
+ }
315
+ /**
316
+ * Sends the error and resolves when it is passed to the transport
317
+ * @param error - error or message
318
+ * @param options - context, user and Next.js data of the event
319
+ */
320
+ capture(error, options = {}) {
321
+ if (options.user !== void 0 && typeof error === "object") {
322
+ usersByError.set(error, options.user);
323
+ }
324
+ const addons = {
325
+ runtime: this.runtime,
326
+ ...options.addons
327
+ };
328
+ const building = this.formatAndSend(error, { nextjs: addons }, options.context).finally(() => {
329
+ this.pending.delete(building);
330
+ });
331
+ this.pending.add(building);
332
+ return building;
333
+ }
334
+ /**
335
+ * Waits for the events being built and sent
336
+ * @param timeout - max time to wait, in milliseconds
337
+ * @returns false when the timeout is reached
338
+ */
339
+ async flush(timeout) {
340
+ const deadline = Date.now() + timeout;
341
+ if (!await settleWithTimeout(this.pending, timeout)) {
342
+ return false;
343
+ }
344
+ return this.fetchTransport.flush(Math.max(deadline - Date.now(), 0));
345
+ }
346
+ /**
347
+ * Returns the catcher type
348
+ */
349
+ getCatcherType() {
350
+ return "errors/nodejs";
351
+ }
352
+ /**
353
+ * Returns the catcher version
354
+ */
355
+ getCatcherVersion() {
356
+ return VERSION;
357
+ }
358
+ };
359
+
360
+ // src/server/sdk.ts
361
+ var REQUEST_CONTEXT_KEY = /* @__PURE__ */ Symbol.for("@next/request-context");
362
+ var noopBreadcrumbs = {
363
+ add: () => void 0,
364
+ get: () => [],
365
+ clear: () => void 0
366
+ };
367
+ var isSetUserWarningShown = false;
368
+ function getCatcher() {
369
+ return getCarrier().catcher;
370
+ }
371
+ function setConsoleLogger() {
372
+ if ((0, import_core3.isLoggerSet)()) {
373
+ return;
374
+ }
375
+ (0, import_core3.setLogger)((message, type = "log", args) => {
376
+ const method = type === "warn" || type === "error" || type === "info" ? type : "log";
377
+ const text = `\u{1F985} Hawk | ${message}`;
378
+ if (args === void 0) {
379
+ console[method](text);
380
+ } else {
381
+ console[method](text, args);
382
+ }
383
+ });
384
+ }
385
+ function getEnvToken() {
386
+ return [process.env.HAWK_TOKEN, process.env.NEXT_PUBLIC_HAWK_TOKEN].find((value) => value !== void 0 && value !== "");
387
+ }
388
+ function waitUntil(promise) {
389
+ globalThis[REQUEST_CONTEXT_KEY]?.get?.()?.waitUntil?.(promise);
390
+ }
391
+ function initServerCatcher(options, runtime) {
392
+ const carrier = getCarrier();
393
+ if (carrier.catcher !== void 0) {
394
+ return void 0;
395
+ }
396
+ setConsoleLogger();
397
+ const token = options.token ?? getEnvToken();
398
+ if (token === void 0 || token === "") {
399
+ (0, import_core3.log)("Integration Token is not provided, errors will not be sent. Set NEXT_PUBLIC_HAWK_TOKEN or pass `token` to init()", "warn");
400
+ return void 0;
401
+ }
402
+ try {
403
+ carrier.catcher = new ServerCatcher({
404
+ token,
405
+ release: options.release ?? process.env.HAWK_RELEASE,
406
+ context: options.context,
407
+ beforeSend: options.beforeSend,
408
+ collectorEndpoint: options.collectorEndpoint,
409
+ runtime
410
+ });
411
+ } catch (_error) {
412
+ (0, import_core3.log)("Invalid Integration Token, errors will not be sent", "error");
413
+ return void 0;
414
+ }
415
+ const { catcher } = carrier;
416
+ carrier.capture = (error, addons) => {
417
+ void catcher.capture(normalizeError(error), { addons });
418
+ };
419
+ return catcher;
420
+ }
421
+ async function captureServerError(error, options = {}) {
422
+ const catcher = getCatcher();
423
+ if (catcher === void 0 || isNextControlFlowError(error)) {
424
+ return;
425
+ }
426
+ await catcher.capture(normalizeError(error), options);
427
+ }
428
+ function send(error, context, user) {
429
+ void captureServerError(error, {
430
+ context,
431
+ user
432
+ });
433
+ }
434
+ function setUser(_user) {
435
+ if (!isSetUserWarningShown) {
436
+ isSetUserWarningShown = true;
437
+ (0, import_core3.log)("setUser() is ignored on the server, pass the user to send() instead", "warn");
438
+ }
439
+ }
440
+ function clearUser() {
441
+ }
442
+ function setContext(context) {
443
+ getCatcher()?.setContext(context);
444
+ }
445
+ var breadcrumbs = noopBreadcrumbs;
446
+ async function flush(timeout = DEFAULT_FLUSH_TIMEOUT) {
447
+ const catcher = getCatcher();
448
+ return catcher === void 0 ? false : catcher.flush(timeout);
449
+ }
450
+ async function onRequestError(error, request, context) {
451
+ const catcher = getCatcher();
452
+ if (catcher === void 0 || isNextControlFlowError(error)) {
453
+ return;
454
+ }
455
+ const sending = captureServerError(error, {
456
+ addons: getRequestAddons(error, request, context)
457
+ }).then(() => catcher.flush(DEFAULT_FLUSH_TIMEOUT));
458
+ waitUntil(sending);
459
+ await sending;
460
+ }
461
+ function onRouterTransitionStart(_url, _navigationType) {
462
+ }
463
+
464
+ // src/server/index.ts
465
+ var import_react = require("./react.cjs");
466
+ async function handleProcessError(event, error) {
467
+ await captureServerError(error);
468
+ await flush();
469
+ if (process.listenerCount(event) === 1) {
470
+ console.error(error);
471
+ process.exit(1);
472
+ }
473
+ }
474
+ function init(options = {}) {
475
+ const catcher = initServerCatcher(options, "nodejs");
476
+ if (catcher === void 0 || options.disableGlobalErrorsHandling === true) {
477
+ return;
478
+ }
479
+ process.on("uncaughtException", (error) => {
480
+ void handleProcessError("uncaughtException", error);
481
+ });
482
+ process.on("unhandledRejection", (reason) => {
483
+ void handleProcessError("unhandledRejection", reason);
484
+ });
485
+ }
486
+ // Annotate the CommonJS export names for ESM import in node:
487
+ 0 && (module.exports = {
488
+ HawkErrorBoundary,
489
+ HawkGlobalError,
490
+ breadcrumbs,
491
+ clearUser,
492
+ flush,
493
+ init,
494
+ onRequestError,
495
+ onRouterTransitionStart,
496
+ send,
497
+ setContext,
498
+ setUser,
499
+ useHawkError
500
+ });