File size: 9,147 Bytes
979bf48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
0 && (module.exports = {
    createHTMLErrorHandler: null,
    createReactServerErrorHandler: null,
    getDigestForWellKnownError: null,
    isUserLandError: null
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: all[name]
    });
}
_export(exports, {
    createHTMLErrorHandler: function() {
        return createHTMLErrorHandler;
    },
    createReactServerErrorHandler: function() {
        return createReactServerErrorHandler;
    },
    getDigestForWellKnownError: function() {
        return getDigestForWellKnownError;
    },
    isUserLandError: function() {
        return isUserLandError;
    }
});
const _stringhash = /*#__PURE__*/ _interop_require_default(require("next/dist/compiled/string-hash"));
const _formatservererror = require("../../lib/format-server-error");
const _tracer = require("../lib/trace/tracer");
const _pipereadable = require("../pipe-readable");
const _bailouttocsr = require("../../shared/lib/lazy-dynamic/bailout-to-csr");
const _hooksservercontext = require("../../client/components/hooks-server-context");
const _isnextroutererror = require("../../client/components/is-next-router-error");
const _dynamicrendering = require("./dynamic-rendering");
const _iserror = require("../../lib/is-error");
const _errortelemetryutils = require("../../lib/error-telemetry-utils");
const _reactlargeshellerror = require("./react-large-shell-error");
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
function getDigestForWellKnownError(error) {
    // If we're bailing out to CSR, we don't need to log the error.
    if ((0, _bailouttocsr.isBailoutToCSRError)(error)) return error.digest;
    // If this is a navigation error, we don't need to log the error.
    if ((0, _isnextroutererror.isNextRouterError)(error)) return error.digest;
    // If this error occurs, we know that we should be stopping the static
    // render. This is only thrown in static generation when PPR is not enabled,
    // which causes the whole page to be marked as dynamic. We don't need to
    // tell the user about this error, as it's not actionable.
    if ((0, _hooksservercontext.isDynamicServerError)(error)) return error.digest;
    // If this is a prerender interrupted error, we don't need to log the error.
    if ((0, _dynamicrendering.isPrerenderInterruptedError)(error)) return error.digest;
    return undefined;
}
function createReactServerErrorHandler(shouldFormatError, isNextExport, reactServerErrors, onReactServerRenderError, spanToRecordOn) {
    return (thrownValue)=>{
        var _err_message;
        if (typeof thrownValue === 'string') {
            // TODO-APP: look at using webcrypto instead. Requires a promise to be awaited.
            return (0, _stringhash.default)(thrownValue).toString();
        }
        // If the response was closed, we don't need to log the error.
        if ((0, _pipereadable.isAbortError)(thrownValue)) return;
        const digest = getDigestForWellKnownError(thrownValue);
        if (digest) {
            return digest;
        }
        if ((0, _reactlargeshellerror.isReactLargeShellError)(thrownValue)) {
            // TODO: Aggregate
            console.error(thrownValue);
            return undefined;
        }
        let err = (0, _iserror.getProperError)(thrownValue);
        let silenceLog = false;
        // If the error already has a digest, respect the original digest,
        // so it won't get re-generated into another new error.
        if (err.digest) {
            if (process.env.NODE_ENV === 'production' && reactServerErrors.has(err.digest)) {
                // This error is likely an obfuscated error from another react-server
                // environment (e.g. 'use cache'). We recover the original error here
                // for reporting purposes.
                err = reactServerErrors.get(err.digest);
                // We don't log it again though, as it was already logged in the
                // original environment.
                silenceLog = true;
            } else {
            // Either we're in development (where we want to keep the transported
            // error with environmentName), or the error is not in reactServerErrors
            // but has a digest from other means. Keep the error as-is.
            }
        } else {
            err.digest = (0, _errortelemetryutils.createDigestWithErrorCode)(err, // TODO-APP: look at using webcrypto instead. Requires a promise to be awaited.
            (0, _stringhash.default)(err.message + (err.stack || '')).toString());
        }
        // @TODO by putting this here and not at the top it is possible that
        // we don't error the build in places we actually expect to
        if (!reactServerErrors.has(err.digest)) {
            reactServerErrors.set(err.digest, err);
        }
        // Format server errors in development to add more helpful error messages
        if (shouldFormatError) {
            (0, _formatservererror.formatServerError)(err);
        }
        // Don't log the suppressed error during export
        if (!(isNextExport && (err == null ? void 0 : (_err_message = err.message) == null ? void 0 : _err_message.includes('The specific message is omitted in production builds to avoid leaking sensitive details.')))) {
            // Record exception on the provided span if available, otherwise try active span.
            const span = spanToRecordOn ?? (0, _tracer.getTracer)().getActiveScopeSpan();
            if (span) {
                span.recordException(err);
                span.setAttribute('error.type', err.name);
                span.setStatus({
                    code: _tracer.SpanStatusCode.ERROR,
                    message: err.message
                });
            }
            onReactServerRenderError(err, silenceLog);
        }
        return err.digest;
    };
}
function createHTMLErrorHandler(shouldFormatError, isNextExport, reactServerErrors, allCapturedErrors, onHTMLRenderSSRError, spanToRecordOn) {
    return (thrownValue, errorInfo)=>{
        var _err_message;
        if ((0, _reactlargeshellerror.isReactLargeShellError)(thrownValue)) {
            // TODO: Aggregate
            console.error(thrownValue);
            return undefined;
        }
        let isSSRError = true;
        allCapturedErrors.push(thrownValue);
        // If the response was closed, we don't need to log the error.
        if ((0, _pipereadable.isAbortError)(thrownValue)) return;
        const digest = getDigestForWellKnownError(thrownValue);
        if (digest) {
            return digest;
        }
        const err = (0, _iserror.getProperError)(thrownValue);
        // If the error already has a digest, respect the original digest,
        // so it won't get re-generated into another new error.
        if (err.digest) {
            if (reactServerErrors.has(err.digest)) {
                // This error is likely an obfuscated error from react-server.
                // We recover the original error here.
                thrownValue = reactServerErrors.get(err.digest);
                isSSRError = false;
            } else {
            // The error is not from react-server but has a digest
            // from other means so we don't need to produce a new one
            }
        } else {
            err.digest = (0, _errortelemetryutils.createDigestWithErrorCode)(err, (0, _stringhash.default)(err.message + ((errorInfo == null ? void 0 : errorInfo.componentStack) || err.stack || '')).toString());
        }
        // Format server errors in development to add more helpful error messages
        if (shouldFormatError) {
            (0, _formatservererror.formatServerError)(err);
        }
        // Don't log the suppressed error during export
        if (!(isNextExport && (err == null ? void 0 : (_err_message = err.message) == null ? void 0 : _err_message.includes('The specific message is omitted in production builds to avoid leaking sensitive details.')))) {
            // HTML errors contain RSC errors as well, filter them out before reporting
            if (isSSRError) {
                // Record exception on the provided span if available, otherwise try active span.
                const span = spanToRecordOn ?? (0, _tracer.getTracer)().getActiveScopeSpan();
                if (span) {
                    span.recordException(err);
                    span.setAttribute('error.type', err.name);
                    span.setStatus({
                        code: _tracer.SpanStatusCode.ERROR,
                        message: err.message
                    });
                }
                onHTMLRenderSSRError(err, errorInfo);
            }
        }
        return err.digest;
    };
}
function isUserLandError(err) {
    return !(0, _pipereadable.isAbortError)(err) && !(0, _bailouttocsr.isBailoutToCSRError)(err) && !(0, _isnextroutererror.isNextRouterError)(err);
}

//# sourceMappingURL=create-error-handler.js.map