From 27b3fb2ad9279f4a8f1d083549be25dec8e13285 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 28 Sep 2022 17:55:05 -0700 Subject: [PATCH 01/66] feat: Next.js instrumentation --- .gitignore | 1 + lib/instrumentation/index.js | 6 + .../next/dist/server/api-utils/node.js | 84 + .../modules/next/dist/server/next-server.js | 145 ++ test/nextjs/a-nextjs-app/apmsetup.js | 14 + test/nextjs/a-nextjs-app/components/Header.js | 36 + test/nextjs/a-nextjs-app/next.config.js | 24 + test/nextjs/a-nextjs-app/package-lock.json | 1677 +++++++++++++++++ test/nextjs/a-nextjs-app/package.json | 19 + .../pages/a-dynamic-page/[num].js | 43 + test/nextjs/a-nextjs-app/pages/a-page.js | 29 + .../a-nextjs-app/pages/an-ssr-error-page.js | 22 + test/nextjs/a-nextjs-app/pages/an-ssr-page.js | 26 + .../pages/api/a-dynamic-api-endpoint/[num].js | 21 + .../pages/api/an-api-endpoint-that-throws.js | 7 + .../a-nextjs-app/pages/api/an-api-endpoint.js | 7 + test/nextjs/a-nextjs-app/pages/index.js | 68 + .../a-nextjs-app/public/elastic-logo.png | Bin 0 -> 5106 bytes test/nextjs/a-nextjs-app/public/favicon.ico | Bin 0 -> 9662 bytes 19 files changed, 2229 insertions(+) create mode 100644 lib/instrumentation/modules/next/dist/server/api-utils/node.js create mode 100644 lib/instrumentation/modules/next/dist/server/next-server.js create mode 100644 test/nextjs/a-nextjs-app/apmsetup.js create mode 100644 test/nextjs/a-nextjs-app/components/Header.js create mode 100644 test/nextjs/a-nextjs-app/next.config.js create mode 100644 test/nextjs/a-nextjs-app/package-lock.json create mode 100644 test/nextjs/a-nextjs-app/package.json create mode 100644 test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js create mode 100644 test/nextjs/a-nextjs-app/pages/a-page.js create mode 100644 test/nextjs/a-nextjs-app/pages/an-ssr-error-page.js create mode 100644 test/nextjs/a-nextjs-app/pages/an-ssr-page.js create mode 100644 test/nextjs/a-nextjs-app/pages/api/a-dynamic-api-endpoint/[num].js create mode 100644 test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js create mode 100644 test/nextjs/a-nextjs-app/pages/api/an-api-endpoint.js create mode 100644 test/nextjs/a-nextjs-app/pages/index.js create mode 100644 test/nextjs/a-nextjs-app/public/elastic-logo.png create mode 100644 test/nextjs/a-nextjs-app/public/favicon.ico diff --git a/.gitignore b/.gitignore index 88194e1551e..e58f59c9e45 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ node_modules /test/benchmarks/.tmp /tmp /examples/*/dist +.next diff --git a/lib/instrumentation/index.js b/lib/instrumentation/index.js index ce14b9537cc..1c98c4c7dd8 100644 --- a/lib/instrumentation/index.js +++ b/lib/instrumentation/index.js @@ -59,6 +59,8 @@ var MODULES = [ 'mongodb', 'mysql', 'mysql2', + 'next/dist/server/next-server', + 'next/dist/server/api-utils/node', 'pg', 'pug', 'redis', @@ -321,6 +323,10 @@ Instrumentation.prototype._patchModule = function (exports, name, version, enabl continue } + // XXX This doesn't catch, and should. If there are multiple patches for a + // given module, then a crash in an early one will break usage of the + // subsequent ones. Also, I'm not sure of the impact on the `hook()` + // call. The failure here is also silent, so hard to notice. exports = patch(exports, this._agent, { version, enabled }) } } diff --git a/lib/instrumentation/modules/next/dist/server/api-utils/node.js b/lib/instrumentation/modules/next/dist/server/api-utils/node.js new file mode 100644 index 00000000000..a3468b3ae30 --- /dev/null +++ b/lib/instrumentation/modules/next/dist/server/api-utils/node.js @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict' + +// XXX comment this module is solely about capturing possible error in a user's API handler. +// It is admittedly a lot of code for just that. + +const semver = require('semver') + +const shimmer = require('../../../../../shimmer') + +module.exports = function (mod, agent, { version, enabled }) { + console.log('XXX enabled: ', enabled) + console.log('XXX version: ', version) + if (!enabled) { + return mod + } + if (!semver.satisfies(version, '>=11.1.0 <13.0.0')) { + agent.logger.debug('next/dist/server/api-utils/node.js version %s not supported, skipping', version) + return mod + } + + shimmer.wrap(mod, 'apiResolver', wrapApiResolver) + + return mod + + function wrapApiResolver (orig) { + return function wrappedApiResolver (req, res, query, resolverModule, apiContext, propagateError, dev, page) { + // XXX This is iteration and creating a wrapper object for every invocation. Yuck. + // Need to cache this. WeakRef on resolverModule? + // XXX I haven't yet tested the case where `resolverModule` doesn't have a + // `.default`, but *is* the handler function. What cases can that happen? + + // The user module that we are wrapping is a webpack-wrapped build that + // exposes fields only as getters. We therefore need to fully replace + // the module to be able to wrap the single `.default` field. + // XXX can we use lib/propwrap.js?! + const wrappedMod = {} + const names = Object.getOwnPropertyNames(resolverModule) + for (let i = 0; i < names.length; i++) { + const name = names[i] + if (name !== 'default') { + // Proxy other module fields. + Object.defineProperty(wrappedMod, name, { enumerable: true, get: function () { return resolverModule[name] } }) + } + } + wrappedMod.default = wrapApiHandler(resolverModule.default || resolverModule) + arguments[3] = wrappedMod + + return orig.apply(this, arguments) + } + } + + function wrapApiHandler (orig) { + // This wraps a user's API handler in order to capture an error if there + // is one. For example: + // // pages/api/some-endpoint-path.js + // export default function handler(req, res) { + // // ... + // throw new Error('boom') + // } + // That handler might also be async, in which case it returns a promise + // that we want to watch for a possible rejection. + return function wrappedApiHandler () { + let promise + try { + promise = orig.apply(this, arguments) + } catch (syncErr) { + agent.captureError(syncErr) + throw syncErr + } + if (promise) { + promise.catch(rejectErr => { + agent.captureError(rejectErr) + }) + } + return promise + } + } +} diff --git a/lib/instrumentation/modules/next/dist/server/next-server.js b/lib/instrumentation/modules/next/dist/server/next-server.js new file mode 100644 index 00000000000..4028ebf017e --- /dev/null +++ b/lib/instrumentation/modules/next/dist/server/next-server.js @@ -0,0 +1,145 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict' + +// The main (but not only) module for instrumenting the Next.js server. +// +// XXX server hierarchy, mainly about the "prod" server, but mostly should +// work for the dev server `npm run dev`. +// +// Some notes on how the instrumentation works. +// +// - XXX NextNodeServer.handleRequest +// - *API* routes ("pages/api/...") in a Next.js app are handled differently +// from other pages. The `catchAllRoute` calls `handleApiRequest`, which +// resolves the URL path to a possibly dynamic route name (e.g. +// `/api/widgets/[id]`, we instrument `ensureApiPage` to get that resolve +// route name), loads the webpack-compiled user module for that route, and +// calls `apiResolver` in "api-utils/node.ts" to execute. We instrument that +// `apiResolve()` function to capture any errors in the user's handler. +// - For other routes ("pages/..."), XXX +// +// - There is open discussion here for other ways to support error capture +// for Next.js: https://github.com/vercel/next.js/discussions/32230 + +const semver = require('semver') + +const shimmer = require('../../../../shimmer') + +const kRouteName = Symbol('nextJsRouteName') +const kPossibleApiRouteName = Symbol('nextJsPossibleApiRouteName') + +module.exports = function (mod, agent, { version, enabled }) { + console.log('XXX enabled: ', enabled) + console.log('XXX version: ', version) + if (!enabled) { + return mod + } + if (!semver.satisfies(version, '>=11.1.0 <13.0.0')) { + agent.logger.debug('next version %s not supported, skipping', version) + return mod + } + + const ins = agent._instrumentation + const log = agent.logger + + const NextNodeServer = mod.default + shimmer.wrap(NextNodeServer.prototype, 'generateRoutes', wrapGenerateRoutes) + shimmer.wrap(NextNodeServer.prototype, 'handleRequest', wrapHandleRequest) + shimmer.wrap(NextNodeServer.prototype, 'handleApiRequest', wrapHandleApiRequest) + shimmer.wrap(NextNodeServer.prototype, 'ensureApiPage', wrapEnsureApiPage) + shimmer.wrap(NextNodeServer.prototype, 'findPageComponents', wrapFindPageComponents) + shimmer.wrap(NextNodeServer.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse) + + /* + XXX + - might still use generateRoutes to wrap route.fn functions to capture + the name of a route for the internal `_next`-y routes + + */ + + return mod + + function wrapGenerateRoutes (orig) { + return function wrappedGenerateRoutes () { + const routes = orig.apply(this, arguments) + // console.log('XXX routes: ', routes) + return routes + } + } + + function wrapHandleRequest (orig) { + return function wrappedHandleRequest (req, _res, parsedUrl) { + console.log('\nXXX wrappedHandleRequest(req "%s %s", res, parsedUrl=%s)', req.method, req.url, parsedUrl) + const promise = orig.apply(this, arguments) + promise.then( + () => { + const trans = ins.currTransaction() + if (trans && trans[kRouteName]) { + trans.setDefaultName(`${req.method} ${trans[kRouteName]}`) + } + } + ) + return promise + } + } + + function wrapHandleApiRequest (orig) { + return function wrappedHandleApiRequest () { + const promise = orig.apply(this, arguments) + promise.then( + handled => { + if (handled) { + // The API request was handled, therefore the route name found + // in the wrapped `ensureApiPage` is the route name. + const trans = ins.currTransaction() + if (trans && trans[kPossibleApiRouteName]) { + trans[kRouteName] = trans[kPossibleApiRouteName] + } + } + } + ) + return promise + } + } + function wrapEnsureApiPage (orig) { + return function wrappedEnsureApiPage (pathname) { + const trans = ins.currTransaction() + if (trans) { + log.trace({ pathname }, 'found possible API route name from ensureApiPage') + trans[kPossibleApiRouteName] = pathname + } + return orig.apply(this, arguments) + } + } + + function wrapFindPageComponents (orig) { + return function wrappedFindPageComponents ({ pathname }) { + const promise = orig.apply(this, arguments) + promise.then(findComponentsResult => { + if (findComponentsResult) { + const trans = ins.currTransaction() + // Avoid overriding and already set kRouteName for the case when + // there is a page error and `findPageComponents()` is called + // independently to load the error page (e.g. "_error.js" or "500.js"). + if (trans && !trans[kRouteName]) { + log.trace({ pathname }, 'found route from findPageComponents') + trans[kRouteName] = pathname + } + } + }) + return promise + } + } + function wrapRenderErrorToResponse (orig) { + return function wrappedRenderErrorToResponse (ctx, err) { + console.log('XXX wrappedRenderErrorToResponse(ctx, err=%s)', err) + agent.captureError(err) + return orig.apply(this, arguments) + } + } +} diff --git a/test/nextjs/a-nextjs-app/apmsetup.js b/test/nextjs/a-nextjs-app/apmsetup.js new file mode 100644 index 00000000000..7aa4d34fcf6 --- /dev/null +++ b/test/nextjs/a-nextjs-app/apmsetup.js @@ -0,0 +1,14 @@ +require('elastic-apm-node').start({ + // XXX most of these for dev/debugging + apmServerVersion: '8.4.1', + cloudProvider: 'none', + centralConfig: false, + metricsInterval: '0s', + useElasticTraceparentHeader: false, // XXX + // captureExceptions: false, // XXX + logUncaughtExceptions: true, + // usePathAsTransactionName: true, + apiRequestTime: '5s', + // logLevel: 'debug' +}) + diff --git a/test/nextjs/a-nextjs-app/components/Header.js b/test/nextjs/a-nextjs-app/components/Header.js new file mode 100644 index 00000000000..56d9be0edf8 --- /dev/null +++ b/test/nextjs/a-nextjs-app/components/Header.js @@ -0,0 +1,36 @@ +import Head from "next/head" +import Link from "next/link" + +// XXX v demo app inits @elastic/apm-rum here + +function Header() { + return ( + <> + + A Next.js App + +
+ {/*
*/} +
+ Home +  |  + APage +  |  + redirect-to-APage +  |  + rewrite-to-APage +  |  + AnSSRPage +  |  + AnSSRErrorPage +  |  + ADynamicPage/42 +  |  + AnAPIEndpoint +
+
+ + ) +} + +export default Header diff --git a/test/nextjs/a-nextjs-app/next.config.js b/test/nextjs/a-nextjs-app/next.config.js new file mode 100644 index 00000000000..934dc4d6c20 --- /dev/null +++ b/test/nextjs/a-nextjs-app/next.config.js @@ -0,0 +1,24 @@ +/** + * @type {import('next').NextConfig} + */ +const nextConfig = { + async redirects() { + return [ + { + source: '/a-page-redirect', + destination: '/a-page', + permanent: false, + }, + ] + }, + async rewrites() { + return [ + { + source: '/a-page-rewrite', + destination: '/a-page', + }, + ] + }, +} + +module.exports = nextConfig diff --git a/test/nextjs/a-nextjs-app/package-lock.json b/test/nextjs/a-nextjs-app/package-lock.json new file mode 100644 index 00000000000..fb5bc087891 --- /dev/null +++ b/test/nextjs/a-nextjs-app/package-lock.json @@ -0,0 +1,1677 @@ +{ + "name": "a-nextjs-app", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "a-nextjs-app", + "version": "1.0.0", + "license": "BSD-2-Clause", + "dependencies": { + "elastic-apm-node": "../../..", + "next": "^12.3.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sharp": "^0.31.0" + } + }, + "../../..": { + "name": "elastic-apm-node", + "version": "3.38.0", + "license": "BSD-2-Clause", + "dependencies": { + "@elastic/ecs-pino-format": "^1.2.0", + "@opentelemetry/api": "^1.1.0", + "after-all-results": "^2.0.0", + "async-cache": "^1.1.0", + "async-value-promise": "^1.1.1", + "basic-auth": "^2.0.1", + "cookie": "^0.5.0", + "core-util-is": "^1.0.2", + "elastic-apm-http-client": "11.0.1", + "end-of-stream": "^1.4.4", + "error-callsites": "^2.0.4", + "error-stack-parser": "^2.0.6", + "escape-string-regexp": "^4.0.0", + "fast-safe-stringify": "^2.0.7", + "http-headers": "^3.0.2", + "is-native": "^1.0.1", + "lru-cache": "^6.0.0", + "measured-reporting": "^1.51.1", + "monitor-event-loop-delay": "^1.0.0", + "object-filter-sequence": "^1.0.0", + "object-identity-map": "^1.0.2", + "original-url": "^1.2.3", + "pino": "^6.11.2", + "relative-microtime": "^2.0.0", + "require-in-the-middle": "^5.2.0", + "semver": "^6.3.0", + "set-cookie-serde": "^1.0.0", + "shallow-clone-shim": "^2.0.0", + "source-map": "^0.8.0-beta.0", + "sql-summary": "^1.0.1", + "traverse": "^0.6.6", + "unicode-byte-truncate": "^1.0.0" + }, + "devDependencies": { + "@babel/cli": "^7.8.4", + "@babel/core": "^7.8.4", + "@babel/preset-env": "^7.8.4", + "@elastic/elasticsearch": "^8.2.1", + "@elastic/elasticsearch-canary": "^8.2.0-canary.2", + "@fastify/formbody": "^7.0.1", + "@hapi/hapi": "^20.1.2", + "@koa/router": "^12.0.0", + "@types/node": "^18.0.1", + "@types/pino": "^6.3.8", + "ajv": "^7.2.4", + "apollo-server-core": "^3.0.0", + "apollo-server-express": "^3.0.0", + "aws-sdk": "^2.622.0", + "backport": "^5.1.2", + "benchmark": "^2.1.4", + "bluebird": "^3.7.2", + "body-parser": "^1.19.0", + "cassandra-driver": "^4.4.0", + "clone": "^2.0.0", + "columnify": "^1.5.4", + "connect": "^3.7.0", + "container-info": "^1.0.1", + "dashdash": "^2.0.0", + "dependency-check": "^4.1.0", + "diagnostics_channel": "^1.1.0", + "elasticsearch": "^16.7.3", + "eslint": "^6.8.0", + "eslint-config-standard": "^14.1.1", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-license-header": "^0.6.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.3.1", + "eslint-plugin-standard": "^4.1.0", + "express": "^4.17.1", + "express-graphql": "^0.12.0", + "express-queue": "^0.0.13", + "fastify": "^4.0.2", + "finalhandler": "^1.1.2", + "generic-pool": "^3.7.1", + "get-port": "^5.1.1", + "got": "^11.8.5", + "graphql": "^15.8.0", + "handlebars": "^4.7.3", + "https-pem": "^3.0.0", + "ioredis": "^5.1.0", + "knex": "^0.95.15", + "koa": "^2.11.0", + "koa-bodyparser": "^4.3.0", + "koa-router": "^12.0.0", + "lambda-local": "^2.0.2", + "memcached": "^2.2.2", + "mimic-response": "^2.1.0", + "mkdirp": "^0.5.1", + "module-details-from-path": "^1.0.3", + "mongodb": "^4.2.1", + "mongodb-core": "^3.2.7", + "mysql": "^2.18.1", + "mysql2": "^2.1.0", + "ndjson": "^1.5.0", + "numeral": "^2.0.6", + "nyc": "^15.0.0", + "once": "^1.4.0", + "pg": "^8.7.1", + "pug": "^3.0.1", + "redis": "^3.1.2", + "request": "^2.88.2", + "restify": "^8.5.1", + "rimraf": "^3.0.2", + "tap-junit": "^5.0.1", + "tape": "^5.0.0", + "tedious": "^15.1.0", + "test-all-versions": "^4.1.1", + "thunky": "^1.1.0", + "typescript": "^4.7.4", + "undici": "^5.8.0", + "vasync": "^2.2.0", + "wait-on": "^6.0.1", + "ws": "^7.2.1" + }, + "engines": { + "node": "^8.6.0 || 10 || 12 || 14 || 16 || 17 || 18 || 19" + } + }, + "node_modules/@next/env": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/env/-/env-12.3.1.tgz", + "integrity": "sha512-9P9THmRFVKGKt9DYqeC2aKIxm8rlvkK38V1P1sRE7qyoPBIs8l9oo79QoSdPtOWfzkbDAVUqvbQGgTMsb8BtJg==" + }, + "node_modules/@next/swc-android-arm-eabi": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.1.tgz", + "integrity": "sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-android-arm64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.3.1.tgz", + "integrity": "sha512-CmgU2ZNyBP0rkugOOqLnjl3+eRpXBzB/I2sjwcGZ7/Z6RcUJXK5Evz+N0ucOxqE4cZ3gkTeXtSzRrMK2mGYV8Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.3.1.tgz", + "integrity": "sha512-hT/EBGNcu0ITiuWDYU9ur57Oa4LybD5DOQp4f22T6zLfpoBMfBibPtR8XktXmOyFHrL/6FC2p9ojdLZhWhvBHg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.3.1.tgz", + "integrity": "sha512-9S6EVueCVCyGf2vuiLiGEHZCJcPAxglyckTZcEwLdJwozLqN0gtS0Eq0bQlGS3dH49Py/rQYpZ3KVWZ9BUf/WA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-freebsd-x64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.3.1.tgz", + "integrity": "sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm-gnueabihf": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.3.1.tgz", + "integrity": "sha512-diL9MSYrEI5nY2wc/h/DBewEDUzr/DqBjIgHJ3RUNtETAOB3spMNHvJk2XKUDjnQuluLmFMloet9tpEqU2TT9w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.1.tgz", + "integrity": "sha512-o/xB2nztoaC7jnXU3Q36vGgOolJpsGG8ETNjxM1VAPxRwM7FyGCPHOMk1XavG88QZSQf+1r+POBW0tLxQOJ9DQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.1.tgz", + "integrity": "sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.1.tgz", + "integrity": "sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.1.tgz", + "integrity": "sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.1.tgz", + "integrity": "sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.1.tgz", + "integrity": "sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.1.tgz", + "integrity": "sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@swc/helpers": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.11.tgz", + "integrity": "sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001410", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001410.tgz", + "integrity": "sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/elastic-apm-node": { + "resolved": "../../..", + "link": true + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + }, + "node_modules/next": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/next/-/next-12.3.1.tgz", + "integrity": "sha512-l7bvmSeIwX5lp07WtIiP9u2ytZMv7jIeB8iacR28PuUEFG5j0HGAPnMqyG5kbZNBG2H7tRsrQ4HCjuMOPnANZw==", + "dependencies": { + "@next/env": "12.3.1", + "@swc/helpers": "0.4.11", + "caniuse-lite": "^1.0.30001406", + "postcss": "8.4.14", + "styled-jsx": "5.0.7", + "use-sync-external-store": "1.2.0" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=12.22.0" + }, + "optionalDependencies": { + "@next/swc-android-arm-eabi": "12.3.1", + "@next/swc-android-arm64": "12.3.1", + "@next/swc-darwin-arm64": "12.3.1", + "@next/swc-darwin-x64": "12.3.1", + "@next/swc-freebsd-x64": "12.3.1", + "@next/swc-linux-arm-gnueabihf": "12.3.1", + "@next/swc-linux-arm64-gnu": "12.3.1", + "@next/swc-linux-arm64-musl": "12.3.1", + "@next/swc-linux-x64-gnu": "12.3.1", + "@next/swc-linux-x64-musl": "12.3.1", + "@next/swc-win32-arm64-msvc": "12.3.1", + "@next/swc-win32-ia32-msvc": "12.3.1", + "@next/swc-win32-x64-msvc": "12.3.1" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^6.0.0 || ^7.0.0", + "react": "^17.0.2 || ^18.0.0-0", + "react-dom": "^17.0.2 || ^18.0.0-0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/node-abi": { + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.25.0.tgz", + "integrity": "sha512-p+0xx5ruIQ+8X57CRIMxbTZRT7tU0Tjn2C/aAK68AEMrbGsCo6IjnDdPNhEyyjWCT4bRtzomXchYd3sSgk3BJQ==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", + "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.0.tgz", + "integrity": "sha512-ft96f8WzGxavg0rkLpMw90MTPMUZDyf0tHjPPh8Ob59xt6KzX8EqtotcqZGUm7kwqpX2pmYiyYX2LL0IZ/FDEw==", + "hasInstallScript": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.1", + "node-addon-api": "^5.0.0", + "prebuild-install": "^7.1.1", + "semver": "^7.3.7", + "simple-get": "^4.0.1", + "tar-fs": "^2.1.1", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.7.tgz", + "integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==", + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + }, + "dependencies": { + "@next/env": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/env/-/env-12.3.1.tgz", + "integrity": "sha512-9P9THmRFVKGKt9DYqeC2aKIxm8rlvkK38V1P1sRE7qyoPBIs8l9oo79QoSdPtOWfzkbDAVUqvbQGgTMsb8BtJg==" + }, + "@next/swc-android-arm-eabi": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.1.tgz", + "integrity": "sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ==", + "optional": true + }, + "@next/swc-android-arm64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.3.1.tgz", + "integrity": "sha512-CmgU2ZNyBP0rkugOOqLnjl3+eRpXBzB/I2sjwcGZ7/Z6RcUJXK5Evz+N0ucOxqE4cZ3gkTeXtSzRrMK2mGYV8Q==", + "optional": true + }, + "@next/swc-darwin-arm64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.3.1.tgz", + "integrity": "sha512-hT/EBGNcu0ITiuWDYU9ur57Oa4LybD5DOQp4f22T6zLfpoBMfBibPtR8XktXmOyFHrL/6FC2p9ojdLZhWhvBHg==", + "optional": true + }, + "@next/swc-darwin-x64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.3.1.tgz", + "integrity": "sha512-9S6EVueCVCyGf2vuiLiGEHZCJcPAxglyckTZcEwLdJwozLqN0gtS0Eq0bQlGS3dH49Py/rQYpZ3KVWZ9BUf/WA==", + "optional": true + }, + "@next/swc-freebsd-x64": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.3.1.tgz", + "integrity": "sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q==", + "optional": true + }, + "@next/swc-linux-arm-gnueabihf": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.3.1.tgz", + "integrity": "sha512-diL9MSYrEI5nY2wc/h/DBewEDUzr/DqBjIgHJ3RUNtETAOB3spMNHvJk2XKUDjnQuluLmFMloet9tpEqU2TT9w==", + "optional": true + }, + "@next/swc-linux-arm64-gnu": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.1.tgz", + "integrity": "sha512-o/xB2nztoaC7jnXU3Q36vGgOolJpsGG8ETNjxM1VAPxRwM7FyGCPHOMk1XavG88QZSQf+1r+POBW0tLxQOJ9DQ==", + "optional": true + }, + "@next/swc-linux-arm64-musl": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.1.tgz", + "integrity": "sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg==", + "optional": true + }, + "@next/swc-linux-x64-gnu": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.1.tgz", + "integrity": "sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA==", + "optional": true + }, + "@next/swc-linux-x64-musl": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.1.tgz", + "integrity": "sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg==", + "optional": true + }, + "@next/swc-win32-arm64-msvc": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.1.tgz", + "integrity": "sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw==", + "optional": true + }, + "@next/swc-win32-ia32-msvc": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.1.tgz", + "integrity": "sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA==", + "optional": true + }, + "@next/swc-win32-x64-msvc": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.1.tgz", + "integrity": "sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA==", + "optional": true + }, + "@swc/helpers": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.11.tgz", + "integrity": "sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==", + "requires": { + "tslib": "^2.4.0" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "caniuse-lite": { + "version": "1.0.30001410", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001410.tgz", + "integrity": "sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==" + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "requires": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" + }, + "elastic-apm-node": { + "version": "file:../../..", + "requires": { + "@babel/cli": "^7.8.4", + "@babel/core": "^7.8.4", + "@babel/preset-env": "^7.8.4", + "@elastic/ecs-pino-format": "^1.2.0", + "@elastic/elasticsearch": "^8.2.1", + "@elastic/elasticsearch-canary": "^8.2.0-canary.2", + "@fastify/formbody": "^7.0.1", + "@hapi/hapi": "^20.1.2", + "@koa/router": "^12.0.0", + "@opentelemetry/api": "^1.1.0", + "@types/node": "^18.0.1", + "@types/pino": "^6.3.8", + "after-all-results": "^2.0.0", + "ajv": "^7.2.4", + "apollo-server-core": "^3.0.0", + "apollo-server-express": "^3.0.0", + "async-cache": "^1.1.0", + "async-value-promise": "^1.1.1", + "aws-sdk": "^2.622.0", + "backport": "^5.1.2", + "basic-auth": "^2.0.1", + "benchmark": "^2.1.4", + "bluebird": "^3.7.2", + "body-parser": "^1.19.0", + "cassandra-driver": "^4.4.0", + "clone": "^2.0.0", + "columnify": "^1.5.4", + "connect": "^3.7.0", + "container-info": "^1.0.1", + "cookie": "^0.5.0", + "core-util-is": "^1.0.2", + "dashdash": "^2.0.0", + "dependency-check": "^4.1.0", + "diagnostics_channel": "^1.1.0", + "elastic-apm-http-client": "11.0.1", + "elasticsearch": "^16.7.3", + "end-of-stream": "^1.4.4", + "error-callsites": "^2.0.4", + "error-stack-parser": "^2.0.6", + "escape-string-regexp": "^4.0.0", + "eslint": "^6.8.0", + "eslint-config-standard": "^14.1.1", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-license-header": "^0.6.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.3.1", + "eslint-plugin-standard": "^4.1.0", + "express": "^4.17.1", + "express-graphql": "^0.12.0", + "express-queue": "^0.0.13", + "fast-safe-stringify": "^2.0.7", + "fastify": "^4.0.2", + "finalhandler": "^1.1.2", + "generic-pool": "^3.7.1", + "get-port": "^5.1.1", + "got": "^11.8.5", + "graphql": "^15.8.0", + "handlebars": "^4.7.3", + "http-headers": "^3.0.2", + "https-pem": "^3.0.0", + "ioredis": "^5.1.0", + "is-native": "^1.0.1", + "knex": "^0.95.15", + "koa": "^2.11.0", + "koa-bodyparser": "^4.3.0", + "koa-router": "^12.0.0", + "lambda-local": "^2.0.2", + "lru-cache": "^6.0.0", + "measured-reporting": "^1.51.1", + "memcached": "^2.2.2", + "mimic-response": "^2.1.0", + "mkdirp": "^0.5.1", + "module-details-from-path": "^1.0.3", + "mongodb": "^4.2.1", + "mongodb-core": "^3.2.7", + "monitor-event-loop-delay": "^1.0.0", + "mysql": "^2.18.1", + "mysql2": "^2.1.0", + "ndjson": "^1.5.0", + "numeral": "^2.0.6", + "nyc": "^15.0.0", + "object-filter-sequence": "^1.0.0", + "object-identity-map": "^1.0.2", + "once": "^1.4.0", + "original-url": "^1.2.3", + "pg": "^8.7.1", + "pino": "^6.11.2", + "pug": "^3.0.1", + "redis": "^3.1.2", + "relative-microtime": "^2.0.0", + "request": "^2.88.2", + "require-in-the-middle": "^5.2.0", + "restify": "^8.5.1", + "rimraf": "^3.0.2", + "semver": "^6.3.0", + "set-cookie-serde": "^1.0.0", + "shallow-clone-shim": "^2.0.0", + "source-map": "^0.8.0-beta.0", + "sql-summary": "^1.0.1", + "tap-junit": "^5.0.1", + "tape": "^5.0.0", + "tedious": "^15.1.0", + "test-all-versions": "^4.1.1", + "thunky": "^1.1.0", + "traverse": "^0.6.6", + "typescript": "^4.7.4", + "undici": "^5.8.0", + "unicode-byte-truncate": "^1.0.0", + "vasync": "^2.2.0", + "wait-on": "^6.0.1", + "ws": "^7.2.1" + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + }, + "next": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/next/-/next-12.3.1.tgz", + "integrity": "sha512-l7bvmSeIwX5lp07WtIiP9u2ytZMv7jIeB8iacR28PuUEFG5j0HGAPnMqyG5kbZNBG2H7tRsrQ4HCjuMOPnANZw==", + "requires": { + "@next/env": "12.3.1", + "@next/swc-android-arm-eabi": "12.3.1", + "@next/swc-android-arm64": "12.3.1", + "@next/swc-darwin-arm64": "12.3.1", + "@next/swc-darwin-x64": "12.3.1", + "@next/swc-freebsd-x64": "12.3.1", + "@next/swc-linux-arm-gnueabihf": "12.3.1", + "@next/swc-linux-arm64-gnu": "12.3.1", + "@next/swc-linux-arm64-musl": "12.3.1", + "@next/swc-linux-x64-gnu": "12.3.1", + "@next/swc-linux-x64-musl": "12.3.1", + "@next/swc-win32-arm64-msvc": "12.3.1", + "@next/swc-win32-ia32-msvc": "12.3.1", + "@next/swc-win32-x64-msvc": "12.3.1", + "@swc/helpers": "0.4.11", + "caniuse-lite": "^1.0.30001406", + "postcss": "8.4.14", + "styled-jsx": "5.0.7", + "use-sync-external-store": "1.2.0" + } + }, + "node-abi": { + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.25.0.tgz", + "integrity": "sha512-p+0xx5ruIQ+8X57CRIMxbTZRT7tU0Tjn2C/aAK68AEMrbGsCo6IjnDdPNhEyyjWCT4bRtzomXchYd3sSgk3BJQ==", + "requires": { + "semver": "^7.3.5" + } + }, + "node-addon-api": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", + "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "requires": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "requires": { + "loose-envify": "^1.1.0" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "sharp": { + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.0.tgz", + "integrity": "sha512-ft96f8WzGxavg0rkLpMw90MTPMUZDyf0tHjPPh8Ob59xt6KzX8EqtotcqZGUm7kwqpX2pmYiyYX2LL0IZ/FDEw==", + "requires": { + "color": "^4.2.3", + "detect-libc": "^2.0.1", + "node-addon-api": "^5.0.0", + "prebuild-install": "^7.1.1", + "semver": "^7.3.7", + "simple-get": "^4.0.1", + "tar-fs": "^2.1.1", + "tunnel-agent": "^0.6.0" + } + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" + }, + "simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "requires": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "requires": { + "is-arrayish": "^0.3.1" + } + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" + }, + "styled-jsx": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.7.tgz", + "integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==", + "requires": {} + }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/test/nextjs/a-nextjs-app/package.json b/test/nextjs/a-nextjs-app/package.json new file mode 100644 index 00000000000..b2713ad87ad --- /dev/null +++ b/test/nextjs/a-nextjs-app/package.json @@ -0,0 +1,19 @@ +{ + "name": "a-nextjs-app", + "version": "1.0.0", + "description": "a Next.js-usage app to test elastic-apm-node instrumentation", + "scripts": { + "dev": "NODE_OPTIONS='-r ./apmsetup.js' next dev", + "build": "next build", + "start": "NODE_OPTIONS='-r ./apmsetup.js' next start" + }, + "keywords": [], + "license": "BSD-2-Clause", + "dependencies": { + "elastic-apm-node": "../../..", + "next": "^12.3.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sharp": "^0.31.0" + } +} diff --git a/test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js b/test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js new file mode 100644 index 00000000000..1248816c262 --- /dev/null +++ b/test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js @@ -0,0 +1,43 @@ +import { useRouter } from 'next/router' +import Header from '../../components/Header' + +// Run at build time to determine a set of dynamic paths to prerender at build time. +// https://nextjs.org/docs/basic-features/data-fetching/get-static-paths +export async function getStaticPaths() { + console.log('XXX ADynamicPage.getStaticPaths') + return { + paths: [ + {params: {num: '41'}}, + {params: {num: '42'}}, + {params: {num: '43'}}, + ], + fallback: true, // false, true, or 'blocking' + } +} + +export async function getStaticProps({ params }) { + console.log('XXX ADynamicPage.getStaticProps') + return { + props: { + doubleThat: Number(params.num) * 2 + } + } +} + +const ADynamicPage = ({ doubleThat }) => { + console.log('XXX ADynamicPage') + const router = useRouter() + const { num } = router.query + + return ( + <> +
+
+
ADynamicPage {num} - doubleThat is {doubleThat}
+
+ + ) +} + +export default ADynamicPage + diff --git a/test/nextjs/a-nextjs-app/pages/a-page.js b/test/nextjs/a-nextjs-app/pages/a-page.js new file mode 100644 index 00000000000..d354e502456 --- /dev/null +++ b/test/nextjs/a-nextjs-app/pages/a-page.js @@ -0,0 +1,29 @@ +// A static page. + +import Image from "next/future/image" +import Header from "../components/Header" + +import img from '../public/elastic-logo.png' + +// Runs at build time. +export async function getStaticProps() { + return { + props: { + buildTime: Date.now() + } + } +} + +function APage({ buildTime }) { + return ( + <> +
+
+
A Page (built at {new Date(buildTime).toISOString()})
+ Elastic logo +
+ + ) +} + +export default APage diff --git a/test/nextjs/a-nextjs-app/pages/an-ssr-error-page.js b/test/nextjs/a-nextjs-app/pages/an-ssr-error-page.js new file mode 100644 index 00000000000..45aef5c9406 --- /dev/null +++ b/test/nextjs/a-nextjs-app/pages/an-ssr-error-page.js @@ -0,0 +1,22 @@ +// A page that throws in `getServerSideProps`. +// https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props#does-getserversideprops-render-an-error-page + +import Header from "../components/Header" + +// Gets called on every request. +export async function getServerSideProps() { + throw new Error('AnSSRErrorPage thrown error') +} + +function AnSSRErrorPage({ currTime }) { + return ( + <> +
+
+
AnSSRErrorPage (currTime is {new Date(currTime).toISOString()})
+
+ + ) +} + +export default AnSSRErrorPage diff --git a/test/nextjs/a-nextjs-app/pages/an-ssr-page.js b/test/nextjs/a-nextjs-app/pages/an-ssr-page.js new file mode 100644 index 00000000000..63e4bcbe4e4 --- /dev/null +++ b/test/nextjs/a-nextjs-app/pages/an-ssr-page.js @@ -0,0 +1,26 @@ +// A server-side rendered page. + +import Header from "../components/Header" + +// Gets called on every request. +export async function getServerSideProps() { + return { + props: { + currTime: Date.now() + } + } +} + +function AnSSRPage({ currTime }) { + throw new Error('XXX boom') + return ( + <> +
+
+
AnSSRPage (currTime is {new Date(currTime).toISOString()})
+
+ + ) +} + +export default AnSSRPage diff --git a/test/nextjs/a-nextjs-app/pages/api/a-dynamic-api-endpoint/[num].js b/test/nextjs/a-nextjs-app/pages/api/a-dynamic-api-endpoint/[num].js new file mode 100644 index 00000000000..b62b498059b --- /dev/null +++ b/test/nextjs/a-nextjs-app/pages/api/a-dynamic-api-endpoint/[num].js @@ -0,0 +1,21 @@ +// A dynamic API endpoint. + +// Always executed server-side. +export default function aDynamicApiEndpoint(req, res) { + const { num } = req.query + const n = Number(num) + if (isNaN(n)) { + res.status(400).json({ + num, + error: 'num is not a Number' + }) + } else { + res.status(200).json({ + num, + n, + double: n * 2, + floor: Math.floor(n) + }) + } +} + diff --git a/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js b/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js new file mode 100644 index 00000000000..b9fb96fc890 --- /dev/null +++ b/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js @@ -0,0 +1,7 @@ +// An API endpoint whose handler throws, to test error handling. + +// Always executed server-side. +export default function anApiEndpointThatThrows(req, res) { + throw new Error('boom') +} + diff --git a/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint.js b/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint.js new file mode 100644 index 00000000000..4e16596bc67 --- /dev/null +++ b/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint.js @@ -0,0 +1,7 @@ +// An API endpoint. + +// Always executed server-side. +export default function anApiEndpoint(req, res) { + res.status(200).json({ ping: 'pong' }) +} + diff --git a/test/nextjs/a-nextjs-app/pages/index.js b/test/nextjs/a-nextjs-app/pages/index.js new file mode 100644 index 00000000000..800c6e7e6d0 --- /dev/null +++ b/test/nextjs/a-nextjs-app/pages/index.js @@ -0,0 +1,68 @@ +import Link from 'next/link' +import Header from '../components/Header' + +function Home() { + return ( + <> +
+
+
Welcome to A-Next.js-App!
+ +
+ + ) +} + +export default Home + diff --git a/test/nextjs/a-nextjs-app/public/elastic-logo.png b/test/nextjs/a-nextjs-app/public/elastic-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a3a10778d3ad5e96575cab967c950fcd23ed599c GIT binary patch literal 5106 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 literal 0 HcmV?d00001 diff --git a/test/nextjs/a-nextjs-app/public/favicon.ico b/test/nextjs/a-nextjs-app/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9aa824a1034e67112666f830a783fceba736df76 GIT binary patch literal 9662 zcmc(lO>9(E6vr=d!NSBg#<6Qc=<3&xd+o6g3K3nX#l#^Az*3lrRtu&}c-x-d@S z20|jUP!UZ)C59LXj3OYRu@hQBLZGFBgn)Dk^t;UQ{BGYFZiji(_ukBu)0{c?`~Lsu zp8N5VI=4ap@_DDfIrr^L&OPg#+oY;R?p;;Ny}an$(@(DGN`Z4Cl2!BCx1?HCZ6%ab zpI6N~M=$yn6i-F9A&W~>a(n7{Lucm38+B2F_Vja)S@EYAFpgc?E=Nxf$f4uz5j}jO zPev|m^PH@Y+OlfD>U#5F&Z?^5YjCx~99MQ~WKgE>jnExoob~45v4a7>#^GG6eDcO# z@FT_{r)kz?g2G5hQfuW_gJTE7vuX*(a4}Xf9?1V#sp@^Ao zU^fmoZXNUp!TiSg1M<|FFBd)6f7M@$~NbGa>k zc$}NLWgK!TIl#H=zBhiXE7;7}ah5fTq@Xs<8uTP~4FARPArC7Kx0Pqa&sqs*oz2&6 z%b9?*+rw}O@UtJ2`k%EDeAI2+HXiQ|WXILtVq@s#YB`Mmer=u3{DYr83{K7=?qHMU*(sSCJ1CPsZI_F?2W9-bZL)A@ z+^VaZXtx#xt*_7+|NMG(bI)SP-gIEQJ-pZBJom|f4DWD{=*Uo?Zv!rsI1Z(iYLTV! z{ZhX_*)tAy2VXxj^a$2g_^k|{k(AYju|!@TOpVFhFR#nY{^w-&z$Tw(9lHI7Aa?M( z;Yw;*hai>;5r?8-2Q%1BjRW=Fx{J;C#C1~KVLSRog3oiL>x*M2LkDQ=0VM%f}VEmkEBG z<0ix%rs0Vs<-k34<)6>}IY7VP)nmKUihOm?vMj*M(=yN{{Bj`2iP}}_}EVyKVd8x2gaM!C%^}n z*2Wy-Ck+Sog&#xT3~O=V&WnVzr-lzhOli`yhhFtV{rsOaV!Ih%(mb(-aL2}R;C&IC zot*t;`Vh})a9i7@i+kuE8{S4_LeOC`H?JNJM80xL6C*}bs0as zD0go@@aIN544u~5xunxJ{odUMUc+tnIpr`+~%FYd4)b_yBrZv{VReRyUzbFn@A&V}^v z&UP^r_`Z?>JN`$@s-6RW{_^eVRq+J&>!bSESLx;+U@iHEh@_y_@MN(?8wd+tlywQj z$67buapCujj$cyEg1Na*PRA*$PNy5j4zJg-FhpU7=js`Ij z)>kw`zvi(XXFjiZN~%@WRzf-TdDUzp7-s2DKD&9j6{UVH~KT=d$SgAx7$C96SecsmHpxXZdR3FkM literal 0 HcmV?d00001 From e0efb1c016345d84302ab7cb1ca2cb80caf305e3 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 29 Sep 2022 14:32:28 -0700 Subject: [PATCH 02/66] fixes: 'error.transaction.*' capture; correct trans.name when error handling - Set trans.name before a possible captureError handling a route. This is because `captureError(err)` will set `error.transaction.*` fields. We want the `trans.name` to be set by then. - Ensure that when an *error* page component is being loaded, it isn't used for the transaction name. --- .../modules/next/dist/server/next-server.js | 159 ++++++++++-------- .../pages/api/an-api-endpoint-that-throws.js | 2 +- 2 files changed, 92 insertions(+), 69 deletions(-) diff --git a/lib/instrumentation/modules/next/dist/server/next-server.js b/lib/instrumentation/modules/next/dist/server/next-server.js index 4028ebf017e..c9d99083504 100644 --- a/lib/instrumentation/modules/next/dist/server/next-server.js +++ b/lib/instrumentation/modules/next/dist/server/next-server.js @@ -8,21 +8,30 @@ // The main (but not only) module for instrumenting the Next.js server. // -// XXX server hierarchy, mainly about the "prod" server, but mostly should -// work for the dev server `npm run dev`. -// -// Some notes on how the instrumentation works. -// -// - XXX NextNodeServer.handleRequest -// - *API* routes ("pages/api/...") in a Next.js app are handled differently -// from other pages. The `catchAllRoute` calls `handleApiRequest`, which -// resolves the URL path to a possibly dynamic route name (e.g. -// `/api/widgets/[id]`, we instrument `ensureApiPage` to get that resolve -// route name), loads the webpack-compiled user module for that route, and -// calls `apiResolver` in "api-utils/node.ts" to execute. We instrument that -// `apiResolve()` function to capture any errors in the user's handler. -// - For other routes ("pages/..."), XXX +// Some notes on how the Next.js node server and the instrumentation works. // +// - There are a number of ways to deploy (https://nextjs.org/docs/deployment) +// a Next.js app. This instrumentation works with "Self-Hosting", and using +// Next.js's built-in server (`class NextNodeServer`). This is the server +// that is used for `next build && next start` and a subclass of that server +// for `next dev`. +// - The Next.js server is a vanilla Node.js `http.createServer` using +// `NextNodeServer.handleRequest` as the request handler, so every request +// to the server is a call to that method. +// - Routes are defined by files under "pages/". An incoming request path is +// resolved a built-in Next.js route handler or one of those pages -- loaded +// by `NextNodeServer.findPageComponents`. +// - An error in rendering a page results in `renderErrorToResponse(err)` being +// called to handle that error. (Limitation: There are some edge cases where +// this method is not used to handle an exception. This instrumentation isn't +// capturing those.) +// - *API* routes ("pages/api/...") are handled differently from other pages. +// The `catchAllRoute` route handler calls `handleApiRequest`, which resolves +// the URL path to a possibly dynamic route name (e.g. `/api/widgets/[id]`, +// we instrument `ensureApiPage` to get that resolve route name), loads the +// webpack-compiled user module for that route, and calls `apiResolver` in +// "api-utils/node.ts" to execute. We instrument that `apiResolve()` function +// to capture any errors in the user's handler. // - There is open discussion here for other ways to support error capture // for Next.js: https://github.com/vercel/next.js/discussions/32230 @@ -30,8 +39,9 @@ const semver = require('semver') const shimmer = require('../../../../shimmer') -const kRouteName = Symbol('nextJsRouteName') -const kPossibleApiRouteName = Symbol('nextJsPossibleApiRouteName') +const kInErrorHandling = Symbol('nextJsInErrorHandling') +// const kRouteName = Symbol('nextJsRouteName') +// const kPossibleApiRouteName = Symbol('nextJsPossibleApiRouteName') module.exports = function (mod, agent, { version, enabled }) { console.log('XXX enabled: ', enabled) @@ -48,70 +58,73 @@ module.exports = function (mod, agent, { version, enabled }) { const log = agent.logger const NextNodeServer = mod.default + // XXX might still use generateRoutes to wrap route.fn functions to capture + // the name of a route for the internal `_next`-y routes shimmer.wrap(NextNodeServer.prototype, 'generateRoutes', wrapGenerateRoutes) - shimmer.wrap(NextNodeServer.prototype, 'handleRequest', wrapHandleRequest) - shimmer.wrap(NextNodeServer.prototype, 'handleApiRequest', wrapHandleApiRequest) + // shimmer.wrap(NextNodeServer.prototype, 'handleRequest', wrapHandleRequest) + // shimmer.wrap(NextNodeServer.prototype, 'handleApiRequest', wrapHandleApiRequest) shimmer.wrap(NextNodeServer.prototype, 'ensureApiPage', wrapEnsureApiPage) shimmer.wrap(NextNodeServer.prototype, 'findPageComponents', wrapFindPageComponents) shimmer.wrap(NextNodeServer.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse) - /* - XXX - - might still use generateRoutes to wrap route.fn functions to capture - the name of a route for the internal `_next`-y routes - - */ - return mod function wrapGenerateRoutes (orig) { return function wrappedGenerateRoutes () { const routes = orig.apply(this, arguments) + console.log('XXX wrappedGenerateRoutes') // console.log('XXX routes: ', routes) return routes } } - function wrapHandleRequest (orig) { - return function wrappedHandleRequest (req, _res, parsedUrl) { - console.log('\nXXX wrappedHandleRequest(req "%s %s", res, parsedUrl=%s)', req.method, req.url, parsedUrl) - const promise = orig.apply(this, arguments) - promise.then( - () => { - const trans = ins.currTransaction() - if (trans && trans[kRouteName]) { - trans.setDefaultName(`${req.method} ${trans[kRouteName]}`) - } - } - ) - return promise - } - } + // function wrapHandleRequest (orig) { + // return function wrappedHandleRequest (req, _res, parsedUrl) { + // console.log('\nXXX wrappedHandleRequest(req "%s %s", res, parsedUrl=%s)', req.method, req.url, parsedUrl) + // const promise = orig.apply(this, arguments) + // promise.then( + // () => { + // const trans = ins.currTransaction() + // if (trans && trans[kRouteName]) { + // trans.setDefaultName(`${req.method} ${trans[kRouteName]}`) + // console.log('XXX setDefaultName: ', trans.name) + // } + // } + // ) + // return promise + // } + // } - function wrapHandleApiRequest (orig) { - return function wrappedHandleApiRequest () { - const promise = orig.apply(this, arguments) - promise.then( - handled => { - if (handled) { - // The API request was handled, therefore the route name found - // in the wrapped `ensureApiPage` is the route name. - const trans = ins.currTransaction() - if (trans && trans[kPossibleApiRouteName]) { - trans[kRouteName] = trans[kPossibleApiRouteName] - } - } - } - ) - return promise - } - } + // function wrapHandleApiRequest (orig) { + // return function wrappedHandleApiRequest () { + // const promise = orig.apply(this, arguments) + // promise.then( + // handled => { + // if (handled) { + // console.log('XXX handled: ', handled) + // // The API request was handled, therefore the route name found + // // in the wrapped `ensureApiPage` is the route name. + // const trans = ins.currTransaction() + // if (trans && trans[kPossibleApiRouteName]) { + // console.log('XXX could set trans name here') + // trans[kRouteName] = trans[kPossibleApiRouteName] + // } + // } + // } + // ) + // return promise + // } + // } function wrapEnsureApiPage (orig) { return function wrappedEnsureApiPage (pathname) { const trans = ins.currTransaction() - if (trans) { - log.trace({ pathname }, 'found possible API route name from ensureApiPage') - trans[kPossibleApiRouteName] = pathname + if (trans && trans.req) { + // XXX slight limitation on "handled" could *possibly* be false if + // exception in `getPagePath`. Trade-off between getting that + // wrong and setting the trans.name early enough for captureError + // usage. + log.trace({ pathname }, 'set transaction name from ensureApiPage') + trans.setDefaultName(`${trans.req.method} ${pathname}`) } return orig.apply(this, arguments) } @@ -123,22 +136,32 @@ module.exports = function (mod, agent, { version, enabled }) { promise.then(findComponentsResult => { if (findComponentsResult) { const trans = ins.currTransaction() - // Avoid overriding and already set kRouteName for the case when - // there is a page error and `findPageComponents()` is called - // independently to load the error page (e.g. "_error.js" or "500.js"). - if (trans && !trans[kRouteName]) { - log.trace({ pathname }, 'found route from findPageComponents') - trans[kRouteName] = pathname + // If Next.js is doing error handling for this request, then it is + // loading an *error* page component (e.g. "_error.js"). We don't want + // to use that component's path for the transaction name. + if (trans && !trans[kInErrorHandling] && trans.req) { + log.trace({ pathname }, 'set transaction name from findPageComponents') + trans.setDefaultName(`${trans.req.method} ${pathname}`) } } }) return promise } } + function wrapRenderErrorToResponse (orig) { return function wrappedRenderErrorToResponse (ctx, err) { console.log('XXX wrappedRenderErrorToResponse(ctx, err=%s)', err) - agent.captureError(err) + const trans = ins.currTransaction() + if (trans) { + // Signal to subsequent instrumentation for this transaction that + // Next.js is now doing error handling for this request. + trans[kInErrorHandling] = true + } + // Next.js uses `err=null` to handle a 404. + if (err) { + agent.captureError(err) + } return orig.apply(this, arguments) } } diff --git a/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js b/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js index b9fb96fc890..37cb934eeda 100644 --- a/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js +++ b/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js @@ -2,6 +2,6 @@ // Always executed server-side. export default function anApiEndpointThatThrows(req, res) { - throw new Error('boom') + throw new Error('An error thrown in anApiEndpointThatThrows handler') } From 4e1245181209b5fd84a558962ca3a953cb81df16 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 29 Sep 2022 14:56:23 -0700 Subject: [PATCH 03/66] fix: 'make check' Do a first pass at eslinting test/nextjs/a-nextjs-app, but then get eslint to skip that dir because it can't handle 'Link' is defined but never used with JSX templates and I can't be bother to solve configuring eslint for this subdir --- .eslintrc.json | 1 + test/nextjs/a-nextjs-app/apmsetup.js | 9 +++++++-- test/nextjs/a-nextjs-app/components/Header.js | 12 ++++++++--- test/nextjs/a-nextjs-app/next.config.js | 20 ++++++++++++------- .../pages/a-dynamic-page/[num].js | 19 +++++++++++------- test/nextjs/a-nextjs-app/pages/a-page.js | 14 +++++++++---- .../a-nextjs-app/pages/an-ssr-error-page.js | 12 ++++++++--- test/nextjs/a-nextjs-app/pages/an-ssr-page.js | 12 ++++++++--- .../pages/api/a-dynamic-api-endpoint/[num].js | 9 +++++++-- .../pages/api/an-api-endpoint-that-throws.js | 9 +++++++-- .../a-nextjs-app/pages/api/an-api-endpoint.js | 9 +++++++-- test/nextjs/a-nextjs-app/pages/index.js | 9 +++++++-- 12 files changed, 98 insertions(+), 37 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 4e5b73bf7f1..9c0ca4ada1b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -19,6 +19,7 @@ "/lib/opentelemetry-bridge/opentelemetry-core-mini", "/test/babel/out.js", "/test/lambda/fixtures/esbuild-bundled-handler/hello.js", + "/test/nextjs/a-nextjs-app", "/test/sourcemaps/fixtures/lib", "/test/sourcemaps/fixtures/src", "/test/stacktraces/fixtures/dist", diff --git a/test/nextjs/a-nextjs-app/apmsetup.js b/test/nextjs/a-nextjs-app/apmsetup.js index 7aa4d34fcf6..70158bd1260 100644 --- a/test/nextjs/a-nextjs-app/apmsetup.js +++ b/test/nextjs/a-nextjs-app/apmsetup.js @@ -1,3 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + require('elastic-apm-node').start({ // XXX most of these for dev/debugging apmServerVersion: '8.4.1', @@ -8,7 +14,6 @@ require('elastic-apm-node').start({ // captureExceptions: false, // XXX logUncaughtExceptions: true, // usePathAsTransactionName: true, - apiRequestTime: '5s', + apiRequestTime: '5s' // logLevel: 'debug' }) - diff --git a/test/nextjs/a-nextjs-app/components/Header.js b/test/nextjs/a-nextjs-app/components/Header.js index 56d9be0edf8..e7bae9474f7 100644 --- a/test/nextjs/a-nextjs-app/components/Header.js +++ b/test/nextjs/a-nextjs-app/components/Header.js @@ -1,9 +1,15 @@ -import Head from "next/head" -import Link from "next/link" +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +import Head from 'next/head' +import Link from 'next/link' // XXX v demo app inits @elastic/apm-rum here -function Header() { +function Header () { return ( <> diff --git a/test/nextjs/a-nextjs-app/next.config.js b/test/nextjs/a-nextjs-app/next.config.js index 934dc4d6c20..7ce3ce3d7ac 100644 --- a/test/nextjs/a-nextjs-app/next.config.js +++ b/test/nextjs/a-nextjs-app/next.config.js @@ -1,24 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + /** * @type {import('next').NextConfig} */ const nextConfig = { - async redirects() { + async redirects () { return [ { source: '/a-page-redirect', destination: '/a-page', - permanent: false, - }, + permanent: false + } ] }, - async rewrites() { + async rewrites () { return [ { source: '/a-page-rewrite', - destination: '/a-page', - }, + destination: '/a-page' + } ] - }, + } } module.exports = nextConfig diff --git a/test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js b/test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js index 1248816c262..8cff9e46eee 100644 --- a/test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js +++ b/test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js @@ -1,21 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + import { useRouter } from 'next/router' import Header from '../../components/Header' // Run at build time to determine a set of dynamic paths to prerender at build time. // https://nextjs.org/docs/basic-features/data-fetching/get-static-paths -export async function getStaticPaths() { +export async function getStaticPaths () { console.log('XXX ADynamicPage.getStaticPaths') return { paths: [ - {params: {num: '41'}}, - {params: {num: '42'}}, - {params: {num: '43'}}, + { params: { num: '41' } }, + { params: { num: '42' } }, + { params: { num: '43' } } ], - fallback: true, // false, true, or 'blocking' + fallback: true // false, true, or 'blocking' } } -export async function getStaticProps({ params }) { +export async function getStaticProps ({ params }) { console.log('XXX ADynamicPage.getStaticProps') return { props: { @@ -40,4 +46,3 @@ const ADynamicPage = ({ doubleThat }) => { } export default ADynamicPage - diff --git a/test/nextjs/a-nextjs-app/pages/a-page.js b/test/nextjs/a-nextjs-app/pages/a-page.js index d354e502456..0ee2a2d6ba5 100644 --- a/test/nextjs/a-nextjs-app/pages/a-page.js +++ b/test/nextjs/a-nextjs-app/pages/a-page.js @@ -1,12 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + // A static page. -import Image from "next/future/image" -import Header from "../components/Header" +import Image from 'next/future/image' +import Header from '../components/Header' import img from '../public/elastic-logo.png' // Runs at build time. -export async function getStaticProps() { +export async function getStaticProps () { return { props: { buildTime: Date.now() @@ -14,7 +20,7 @@ export async function getStaticProps() { } } -function APage({ buildTime }) { +function APage ({ buildTime }) { return ( <>
diff --git a/test/nextjs/a-nextjs-app/pages/an-ssr-error-page.js b/test/nextjs/a-nextjs-app/pages/an-ssr-error-page.js index 45aef5c9406..430f1fdc1e0 100644 --- a/test/nextjs/a-nextjs-app/pages/an-ssr-error-page.js +++ b/test/nextjs/a-nextjs-app/pages/an-ssr-error-page.js @@ -1,14 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + // A page that throws in `getServerSideProps`. // https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props#does-getserversideprops-render-an-error-page -import Header from "../components/Header" +import Header from '../components/Header' // Gets called on every request. -export async function getServerSideProps() { +export async function getServerSideProps () { throw new Error('AnSSRErrorPage thrown error') } -function AnSSRErrorPage({ currTime }) { +function AnSSRErrorPage ({ currTime }) { return ( <>
diff --git a/test/nextjs/a-nextjs-app/pages/an-ssr-page.js b/test/nextjs/a-nextjs-app/pages/an-ssr-page.js index 63e4bcbe4e4..04f40aaa00d 100644 --- a/test/nextjs/a-nextjs-app/pages/an-ssr-page.js +++ b/test/nextjs/a-nextjs-app/pages/an-ssr-page.js @@ -1,9 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + // A server-side rendered page. -import Header from "../components/Header" +import Header from '../components/Header' // Gets called on every request. -export async function getServerSideProps() { +export async function getServerSideProps () { return { props: { currTime: Date.now() @@ -11,7 +17,7 @@ export async function getServerSideProps() { } } -function AnSSRPage({ currTime }) { +function AnSSRPage ({ currTime }) { throw new Error('XXX boom') return ( <> diff --git a/test/nextjs/a-nextjs-app/pages/api/a-dynamic-api-endpoint/[num].js b/test/nextjs/a-nextjs-app/pages/api/a-dynamic-api-endpoint/[num].js index b62b498059b..2884ae9104d 100644 --- a/test/nextjs/a-nextjs-app/pages/api/a-dynamic-api-endpoint/[num].js +++ b/test/nextjs/a-nextjs-app/pages/api/a-dynamic-api-endpoint/[num].js @@ -1,7 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + // A dynamic API endpoint. // Always executed server-side. -export default function aDynamicApiEndpoint(req, res) { +export default function aDynamicApiEndpoint (req, res) { const { num } = req.query const n = Number(num) if (isNaN(n)) { @@ -18,4 +24,3 @@ export default function aDynamicApiEndpoint(req, res) { }) } } - diff --git a/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js b/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js index 37cb934eeda..96403cc64f6 100644 --- a/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js +++ b/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js @@ -1,7 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + // An API endpoint whose handler throws, to test error handling. // Always executed server-side. -export default function anApiEndpointThatThrows(req, res) { +export default function anApiEndpointThatThrows (req, res) { throw new Error('An error thrown in anApiEndpointThatThrows handler') } - diff --git a/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint.js b/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint.js index 4e16596bc67..1d655ed59a3 100644 --- a/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint.js +++ b/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint.js @@ -1,7 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + // An API endpoint. // Always executed server-side. -export default function anApiEndpoint(req, res) { +export default function anApiEndpoint (req, res) { res.status(200).json({ ping: 'pong' }) } - diff --git a/test/nextjs/a-nextjs-app/pages/index.js b/test/nextjs/a-nextjs-app/pages/index.js index 800c6e7e6d0..a53672df0d9 100644 --- a/test/nextjs/a-nextjs-app/pages/index.js +++ b/test/nextjs/a-nextjs-app/pages/index.js @@ -1,7 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + import Link from 'next/link' import Header from '../components/Header' -function Home() { +function Home () { return ( <>
@@ -65,4 +71,3 @@ function Home() { } export default Home - From a2f9b4e74a1bcc6f0ecf798598a48a31f99d12ee Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 29 Sep 2022 16:44:18 -0700 Subject: [PATCH 04/66] skip dependency-check in the sample a-nextjs-app because it borks on the recursive link for the 'elastic-apm-node' dep --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 33cb1e8cc8c..7ed3fbaa15d 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "lint:license-files": "./dev-utils/gen-notice.sh --lint . # requires node >=16", "coverage": "COVERAGE=true ./test/script/run_tests.sh", "test": "./test/script/run_tests.sh", - "test:deps": "dependency-check start.js index.js 'lib/**/*.js' 'test/**/*.js' --no-dev -i async_hooks -i perf_hooks -i parseurl -i node:http", + "test:deps": "dependency-check start.js index.js 'lib/**/*.js' 'test/**/*.js' '!test/nextjs/a-nextjs-app' --no-dev -i async_hooks -i perf_hooks -i parseurl -i node:http", "test:tav": "tav --quiet", "test:docs": "./test/script/docker/run_docs.sh", "test:types": "tsc --project test/types/tsconfig.json && tsc --project test/types/transpile/tsconfig.json && node test/types/transpile/index.js && tsc --project test/types/transpile-default/tsconfig.json && node test/types/transpile-default/index.js", From 317181410c1fcf5a7bdfbd5d1958848c51df4464 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Tue, 4 Oct 2022 11:15:44 -0700 Subject: [PATCH 05/66] typo in comment --- lib/propwrap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/propwrap.js b/lib/propwrap.js index 94c74fb1a4e..194edc7bb51 100644 --- a/lib/propwrap.js +++ b/lib/propwrap.js @@ -67,7 +67,7 @@ var __copyProps = (to, from, except, desc) => { * console.log(os.platform()) // => DARWIN * * The subpath can indicate a nested property. Each property in that subpath, - * except the last, much identify an *Object*. + * except the last, must identify an *Object*. * * Limitations: * - This doesn't handle possible Symbol properties on the copied object(s). From 146511aed6cf05fb414f7a7544707ff9129f6318 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Tue, 4 Oct 2022 11:17:55 -0700 Subject: [PATCH 06/66] do the config test pennance for new instrumentation modules --- test/config.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/config.test.js b/test/config.test.js index b95d756f762..8cee47734d8 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -1030,6 +1030,8 @@ test('disableInstrumentations', function (t) { modules.delete('koa-router') // koa-router@11 supports node >=12 modules.delete('@koa/router') // koa-router@11 supports node >=12 } + modules.delete('next/dist/server/next-server') + modules.delete('next/dist/server/api-utils/node') function testSlice (t, name, selector) { var selection = selector(modules) From 54526072541ac520078a10ca5ddc51822aa842d9 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 5 Oct 2022 16:47:51 -0700 Subject: [PATCH 07/66] get routes going with a dev server. now need to move back to instr on class load because after ctor is too late to catch generateRoutes for the prod server --- lib/instrumentation/index.js | 3 + .../modules/next/dist/server/next-server.js | 160 +++++++++++- .../modules/next/dist/server/next.js | 230 ++++++++++++++++++ lib/instrumentation/transaction.js | 1 + test/nextjs/a-nextjs-app/apmsetup.js | 2 +- test/nextjs/a-nextjs-app/next.config.js | 42 +++- test/nextjs/a-nextjs-app/pages/an-ssr-page.js | 3 +- 7 files changed, 423 insertions(+), 18 deletions(-) create mode 100644 lib/instrumentation/modules/next/dist/server/next.js diff --git a/lib/instrumentation/index.js b/lib/instrumentation/index.js index 1c98c4c7dd8..cbc3e13cbb3 100644 --- a/lib/instrumentation/index.js +++ b/lib/instrumentation/index.js @@ -59,6 +59,9 @@ var MODULES = [ 'mongodb', 'mysql', 'mysql2', + // XXX + // 'next', + 'next/dist/server/next', 'next/dist/server/next-server', 'next/dist/server/api-utils/node', 'pg', diff --git a/lib/instrumentation/modules/next/dist/server/next-server.js b/lib/instrumentation/modules/next/dist/server/next-server.js index c9d99083504..6a3c1cf9155 100644 --- a/lib/instrumentation/modules/next/dist/server/next-server.js +++ b/lib/instrumentation/modules/next/dist/server/next-server.js @@ -42,6 +42,8 @@ const shimmer = require('../../../../shimmer') const kInErrorHandling = Symbol('nextJsInErrorHandling') // const kRouteName = Symbol('nextJsRouteName') // const kPossibleApiRouteName = Symbol('nextJsPossibleApiRouteName') +const kSetTransNameFn = Symbol('nextJsSetTransNameFn') +const kRouteIsSet = Symbol('nextJsRouteIsSet') module.exports = function (mod, agent, { version, enabled }) { console.log('XXX enabled: ', enabled) @@ -58,22 +60,157 @@ module.exports = function (mod, agent, { version, enabled }) { const log = agent.logger const NextNodeServer = mod.default + shimmer.wrap(NextNodeServer.prototype, 'generateRoutes', function (orig) { + return function playWrappedProdServerGenerateRoutes () { + // console.log('XXX playWrappedProdServerGenerateRoutes: start: is this my class? ', this.constructor === NextNodeServer) + if (this.constructor === NextNodeServer) { + console.log(' XXX do the prod server thing') + } + return orig.apply(this, arguments) + } + }) // XXX might still use generateRoutes to wrap route.fn functions to capture // the name of a route for the internal `_next`-y routes - shimmer.wrap(NextNodeServer.prototype, 'generateRoutes', wrapGenerateRoutes) - // shimmer.wrap(NextNodeServer.prototype, 'handleRequest', wrapHandleRequest) - // shimmer.wrap(NextNodeServer.prototype, 'handleApiRequest', wrapHandleApiRequest) - shimmer.wrap(NextNodeServer.prototype, 'ensureApiPage', wrapEnsureApiPage) - shimmer.wrap(NextNodeServer.prototype, 'findPageComponents', wrapFindPageComponents) - shimmer.wrap(NextNodeServer.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse) + // shimmer.wrap(NextNodeServer.prototype, 'generateRoutes', wrapGenerateRoutes) + // // shimmer.wrap(NextNodeServer.prototype, 'handleRequest', wrapHandleRequest) + // // shimmer.wrap(NextNodeServer.prototype, 'handleApiRequest', wrapHandleApiRequest) + // shimmer.wrap(NextNodeServer.prototype, 'ensureApiPage', wrapEnsureApiPage) + // shimmer.wrap(NextNodeServer.prototype, 'findPageComponents', wrapFindPageComponents) + // shimmer.wrap(NextNodeServer.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse) return mod + // XXX Link to Next.js route type. + function wrapRedirectRoute (route) { + if (typeof route.fn !== 'function') { + return + } + const origRouteFn = route.fn + route.fn = function () { + const trans = ins.currTransaction() + if (trans) { + trans.setDefaultName('Next.js ' + route.name) + trans[kSetTransNameFn] = () => {} // XXX static noop func for perf? + } + return origRouteFn.apply(this, arguments) + } + } + + function wrapRewriteRoute (route) { + if (typeof route.fn !== 'function') { + return + } + const origRouteFn = route.fn + route.fn = function () { + const trans = ins.currTransaction() + if (trans) { + trans.setDefaultName(`Next.js ${route.name} -> ${route.destination}`) + // trans[kRouteIsSet] = true // XXX splain + trans[kSetTransNameFn] = () => {} // XXX static noop func for perf? + } + return origRouteFn.apply(this, arguments) + } + } + + function wrapCatchAllRoute (route) { + if (typeof route.fn !== 'function') { + return + } + const origRouteFn = route.fn + route.fn = function () { + const trans = ins.currTransaction() + // if (trans && !trans[kRouteIsSet]) { + if (trans && !trans[kSetTransNameFn]) { + trans[kSetTransNameFn] = (req, pathname) => { // XXX + console.log('XXX kSetTransNameFn called for wrapCatchAllRoute: pathname=%s', pathname) + trans.setDefaultName(`${req.method} ${pathname}`) + } + } + return origRouteFn.apply(this, arguments) + } + } + + // XXX splain + function wrapFsRoute (route) { + if (typeof route.fn !== 'function') { + return + } + const origRouteFn = route.fn + // XXX switch on route.name and allowlist the route.fn's we know + switch (route.name) { + case '_next/data catchall': + route.fn = function () { + const trans = ins.currTransaction() + if (trans) { + trans.setDefaultName(`Next.js ${route.name}`) + if (!trans[kSetTransNameFn]) { + trans[kSetTransNameFn] = (req, pathname) => { + trans.setDefaultName(`Next.js data route ${req.method} ${pathname}`) + } + } + } + return origRouteFn.apply(this, arguments) + } + break + // XXX more here + } + } + function wrapFsRouteXXXold (route) { + if (typeof route.fn !== 'function') { + return + } + const origRouteFn = route.fn + // XXX switch on route.name and allowlist the route.fn's we know + if (route.name === '_next/data catchall') { + route.fn = function () { + const trans = ins.currTransaction() + if (trans) { + trans.setDefaultName(`Next.js ${route.name}`) + if (!trans[kSetTransNameFn]) { + trans[kSetTransNameFn] = (req, pathname) => { // XXX + // QQQ HERE, next is "_next/data" and all fsRoutes + console.log('XXX kSetTransNameFn called: pathname=%s', pathname) + trans.setDefaultName(`Next.js data route ${req.method} ${pathname}`) + } + } + } + return origRouteFn.apply(this, arguments) + } + } else { + route.fn = function () { + const promise = origRouteFn.apply(this, arguments) + const trans = ins.currTransaction() + if (trans) { + trans.setDefaultName(`Next.js ${route.name}`) + // promise.then(result => { + // // The `result.finished` check is specifically for the + // // "public folder catchall" route. We don't want to set the + // // transaction name if there isn't a matching "public/..." path. + // if (result && result.finished) { + // // XXX HERE why doesn't this work: + // // curl -i localhost:3000/favicon.ico + // // getting "GET unknown route" + // console.log('XXX setting trans.name from route: %s', route.name, result) + // trans.setDefaultName(`Next.js ${route.name}`) + // } + // }) + } + return promise + } + } + } + function wrapGenerateRoutes (orig) { return function wrappedGenerateRoutes () { const routes = orig.apply(this, arguments) console.log('XXX wrappedGenerateRoutes') // console.log('XXX routes: ', routes) + routes.redirects.forEach(wrapRedirectRoute) + routes.rewrites.beforeFiles.forEach(wrapRewriteRoute) + routes.rewrites.afterFiles.forEach(wrapRewriteRoute) + routes.rewrites.fallback.forEach(wrapRewriteRoute) + routes.fsRoutes.forEach(wrapFsRoute) + wrapCatchAllRoute(routes.catchAllRoute) return routes } } @@ -119,7 +256,7 @@ module.exports = function (mod, agent, { version, enabled }) { return function wrappedEnsureApiPage (pathname) { const trans = ins.currTransaction() if (trans && trans.req) { - // XXX slight limitation on "handled" could *possibly* be false if + // XXX slight limitation on "handled": It could *possibly* be false if // exception in `getPagePath`. Trade-off between getting that // wrong and setting the trans.name early enough for captureError // usage. @@ -139,9 +276,11 @@ module.exports = function (mod, agent, { version, enabled }) { // If Next.js is doing error handling for this request, then it is // loading an *error* page component (e.g. "_error.js"). We don't want // to use that component's path for the transaction name. - if (trans && !trans[kInErrorHandling] && trans.req) { + if (trans && !trans[kInErrorHandling] && trans.req && trans[kSetTransNameFn]) { log.trace({ pathname }, 'set transaction name from findPageComponents') - trans.setDefaultName(`${trans.req.method} ${pathname}`) + // XXX + // trans.setDefaultName(`${trans.req.method} ${pathname}`) + trans[kSetTransNameFn](trans.req, pathname) } } }) @@ -151,11 +290,12 @@ module.exports = function (mod, agent, { version, enabled }) { function wrapRenderErrorToResponse (orig) { return function wrappedRenderErrorToResponse (ctx, err) { - console.log('XXX wrappedRenderErrorToResponse(ctx, err=%s)', err) + console.log('XXX wrappedRenderErrorToResponse(ctx, err=%s)', err && err.message) const trans = ins.currTransaction() if (trans) { // Signal to subsequent instrumentation for this transaction that // Next.js is now doing error handling for this request. + // XXX could try setting `kSetTransNameFn` to noop instead. Then only need the one symbol. trans[kInErrorHandling] = true } // Next.js uses `err=null` to handle a 404. diff --git a/lib/instrumentation/modules/next/dist/server/next.js b/lib/instrumentation/modules/next/dist/server/next.js new file mode 100644 index 00000000000..04ae19c1ed8 --- /dev/null +++ b/lib/instrumentation/modules/next/dist/server/next.js @@ -0,0 +1,230 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict' + +const semver = require('semver') + +const shimmer = require('../../../../shimmer') + +const kInErrorHandling = Symbol('nextJsInErrorHandling') +const kSetTransNameFn = Symbol('nextJsSetTransNameFn') + +const noopFn = () => {} + +module.exports = function (mod, agent, { version, enabled }) { + console.log('XXX hi in "next" instr (at next/dist/server/next.js)') + if (!enabled) { + return mod + } + if (!semver.satisfies(version, '>=11.1.0 <13.0.0')) { + agent.logger.debug('next version %s not supported, skipping', version) + return mod + } + if (typeof mod !== 'function' || mod.default !== mod) { + agent.logger.debug('next module is not the expected form, skipping') + return mod + } + + const ins = agent._instrumentation + const log = agent.logger + + // The top-level module `createServer()` returns a `NextServer` with a + // `.createServer()` method that returns a promise for a next Server subclass + // instance that we want to instrument. + agent.logger.debug('wrapping next (createServer)') + const origCreateServer = mod + function wrappedModCreateServer () { + const nextServerInst = origCreateServer.apply(this, arguments) + console.log('XXX nextServerInst: ', nextServerInst.constructor.prototype) + shimmer.wrap(nextServerInst.constructor.prototype, 'createServer', wrapCreateServer) + return nextServerInst + } + wrappedModCreateServer.default = wrappedModCreateServer + + return wrappedModCreateServer + + function wrapCreateServer (orig) { + return function wrappedCreateServer () { + const promise = orig.apply(this, arguments) + promise.then(serverInst => { + // `serverInst` is an instance of next-server.ts `NextNodeServer` (when + // using `next build && next start`) or next-dev-server.ts `DevServer` + // (when using `next dev`). + const prototype = serverInst.constructor.prototype + console.log('XXX prototype: ', prototype.generateRoutes, serverInst.constructor) + shimmer.wrap(prototype, 'generateRoutes', wrapGenerateRoutes) + shimmer.wrap(prototype, 'ensureApiPage', wrapEnsureApiPage) + shimmer.wrap(prototype, 'findPageComponents', wrapFindPageComponents) + shimmer.wrap(prototype, 'renderErrorToResponse', wrapRenderErrorToResponse) + }) + return promise + } + } + + function wrapGenerateRoutes (orig) { + console.log('XXX wrapGenerateRoutes') + return function wrappedGenerateRoutes () { + if (this.constructor.name === 'DevServer') { + console.log(' XXX do the dev server thing') + } + console.log('XXX wrappedGenerateRoutes') + const routes = orig.apply(this, arguments) + console.log('XXX routes: ', routes) + routes.redirects.forEach(wrapRedirectRoute) + routes.rewrites.beforeFiles.forEach(wrapRewriteRoute) + routes.rewrites.afterFiles.forEach(wrapRewriteRoute) + routes.rewrites.fallback.forEach(wrapRewriteRoute) + routes.fsRoutes.forEach(wrapFsRoute) + wrapCatchAllRoute(routes.catchAllRoute) + return routes + } + } + + // XXX Link to Next.js route type. + function wrapRedirectRoute (route) { + if (typeof route.fn !== 'function') { + return + } + const origRouteFn = route.fn + route.fn = function () { + const trans = ins.currTransaction() + if (trans) { + trans.setDefaultName('Next.js ' + route.name) + trans[kSetTransNameFn] = noopFn + } + return origRouteFn.apply(this, arguments) + } + } + + function wrapRewriteRoute (route) { + if (typeof route.fn !== 'function') { + return + } + const origRouteFn = route.fn + route.fn = function () { + const trans = ins.currTransaction() + if (trans) { + trans.setDefaultName(`Next.js ${route.name} -> ${route.destination}`) + trans[kSetTransNameFn] = noopFn + } + return origRouteFn.apply(this, arguments) + } + } + + function wrapFsRoute (route) { + if (typeof route.fn !== 'function') { + return + } + const origRouteFn = route.fn + // We explicitly handle only the `fsRoute`s that we know by name in the + // Next.js code. We cannot set `trans.name` for all of them because of the + // true catch-all-routes that match any path and only sometimes handled them + // (e.g. 'public folder catchall'). + // XXX splain why we cannot do 'public folder catchall' and equiv. + switch (route.name) { + // XXX splain + case '_next/data catchall': + route.fn = function () { + const trans = ins.currTransaction() + if (trans) { + trans.setDefaultName(`Next.js ${route.name}`) + if (!trans[kSetTransNameFn]) { + trans[kSetTransNameFn] = (_req, pathname) => { + trans.setDefaultName(`Next.js _next/data for ${pathname}`) + } + } + } + return origRouteFn.apply(this, arguments) + } + break + case '_next/static/development/_devMiddlewareManifest.json': + case '_next/static/development/_devPagesManifest.json': + case '_next/development catchall': + case '_next/static catchall': + case '_next/image catchall': + case '_next catchall': + route.fn = function () { + const trans = ins.currTransaction() + if (trans) { + // XXX 'splain result.finished + trans.setDefaultName(`Next.js ${route.name}`) + } + return origRouteFn.apply(this, arguments) + } + break + } + } + + function wrapCatchAllRoute (route) { + if (typeof route.fn !== 'function') { + return + } + const origRouteFn = route.fn + route.fn = function () { + const trans = ins.currTransaction() + if (trans && !trans[kSetTransNameFn]) { + trans[kSetTransNameFn] = (req, pathname) => { + console.log('XXX kSetTransNameFn called for wrapCatchAllRoute: pathname=%s', pathname) + trans.setDefaultName(`${req.method} ${pathname}`) + } + } + return origRouteFn.apply(this, arguments) + } + } + + function wrapEnsureApiPage (orig) { + return function wrappedEnsureApiPage (pathname) { + const trans = ins.currTransaction() + if (trans && trans.req) { + // XXX slight limitation on "handled": It could *possibly* be false if + // exception in `getPagePath`. Trade-off between getting that + // wrong and setting the trans.name early enough for captureError + // usage. + log.trace({ pathname }, 'set transaction name from ensureApiPage') + trans.setDefaultName(`${trans.req.method} ${pathname}`) + } + return orig.apply(this, arguments) + } + } + + function wrapFindPageComponents (orig) { + return function wrappedFindPageComponents ({ pathname }) { + const promise = orig.apply(this, arguments) + promise.then(findComponentsResult => { + if (findComponentsResult) { + const trans = ins.currTransaction() + // If Next.js is doing error handling for this request, then it is + // loading an *error* page component (e.g. "_error.js"). We don't want + // to use that component's path for the transaction name. + if (trans && !trans[kInErrorHandling] && trans.req && trans[kSetTransNameFn]) { + log.trace({ pathname }, 'set transaction name from findPageComponents') + trans[kSetTransNameFn](trans.req, pathname) + } + } + }) + return promise + } + } + + function wrapRenderErrorToResponse (orig) { + return function wrappedRenderErrorToResponse (ctx, err) { + console.log('XXX wrappedRenderErrorToResponse(ctx, err=%s)', err && err.message) + const trans = ins.currTransaction() + if (trans) { + // Signal to subsequent instrumentation for this transaction that + // Next.js is now doing error handling for this request. + // XXX could try setting `kSetTransNameFn` to noop instead. Then only need the one symbol. + trans[kInErrorHandling] = true + } + // Next.js uses `err=null` to handle a 404. + if (err) { + agent.captureError(err) + } + return orig.apply(this, arguments) + } + } +} diff --git a/lib/instrumentation/transaction.js b/lib/instrumentation/transaction.js index e7f85360816..51684dbf5f2 100644 --- a/lib/instrumentation/transaction.js +++ b/lib/instrumentation/transaction.js @@ -306,6 +306,7 @@ Transaction.prototype._encode = function () { } Transaction.prototype.setDefaultName = function (name) { + console.log('XXX Transaction.setDefaultName(name=%s) ended? %s', name, this.ended) this._agent.logger.debug('setting default transaction name: %s %o', name, { trans: this.id, parent: this.parentId, trace: this.traceId }) this._defaultName = name } diff --git a/test/nextjs/a-nextjs-app/apmsetup.js b/test/nextjs/a-nextjs-app/apmsetup.js index 70158bd1260..3f7d2c22be0 100644 --- a/test/nextjs/a-nextjs-app/apmsetup.js +++ b/test/nextjs/a-nextjs-app/apmsetup.js @@ -14,6 +14,6 @@ require('elastic-apm-node').start({ // captureExceptions: false, // XXX logUncaughtExceptions: true, // usePathAsTransactionName: true, - apiRequestTime: '5s' + apiRequestTime: '3s' // logLevel: 'debug' }) diff --git a/test/nextjs/a-nextjs-app/next.config.js b/test/nextjs/a-nextjs-app/next.config.js index 7ce3ce3d7ac..1032c6313c4 100644 --- a/test/nextjs/a-nextjs-app/next.config.js +++ b/test/nextjs/a-nextjs-app/next.config.js @@ -18,12 +18,42 @@ const nextConfig = { ] }, async rewrites () { - return [ - { - source: '/a-page-rewrite', - destination: '/a-page' - } - ] + return { + beforeFiles: [ + { + source: '/rewrite-with-a-has-condition', + destination: '/a-page', + has: [{ type: 'query', key: 'overrideMe' }], + } + ], + afterFiles: [ + { + source: '/rewrite-to-a-page', + destination: '/a-page' + }, + { + // XXX improve this to have dynamic value in params + source: '/rewrite-to-a-dynamic-page', + destination: '/a-dynamic-page/3.14' + }, + { + source: '/rewrite-to-a-public-file', + destination: '/favicon.ico' + }, + { + source: '/rewrite-to-a-404', + destination: '/no-such-page' + } + ], + fallback: [ + // These rewrites are checked after both pages/public files + // and dynamic routes are checked + { + source: '/rewrite-external/:path*', + destination: `https://old.example.com/:path*`, + }, + ], + } } } diff --git a/test/nextjs/a-nextjs-app/pages/an-ssr-page.js b/test/nextjs/a-nextjs-app/pages/an-ssr-page.js index 04f40aaa00d..68de30b3212 100644 --- a/test/nextjs/a-nextjs-app/pages/an-ssr-page.js +++ b/test/nextjs/a-nextjs-app/pages/an-ssr-page.js @@ -10,6 +10,7 @@ import Header from '../components/Header' // Gets called on every request. export async function getServerSideProps () { + console.log('XXX AnSSRPage.getServerSideProps') return { props: { currTime: Date.now() @@ -18,7 +19,7 @@ export async function getServerSideProps () { } function AnSSRPage ({ currTime }) { - throw new Error('XXX boom') + console.log('XXX AnSSRPage') return ( <>
From 67bad71b9abde2bab16ac1773292ff680a8561c4 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 6 Oct 2022 17:45:07 -0700 Subject: [PATCH 08/66] flushing on server shutdown; DevServer instrumentation; most of the test suite; still have a couple routing bugs --- .eslintrc.json | 3 +- lib/instrumentation/index.js | 5 +- .../next/dist/server/dev/next-dev-server.js | 211 +++++++ .../modules/next/dist/server/next-server.js | 205 ++---- .../modules/next/dist/server/next.js | 2 + test/nextjs/a-nextjs-app/apmsetup.js | 18 +- test/nextjs/a-nextjs-app/components/Header.js | 7 - test/nextjs/a-nextjs-app/next.config.js | 12 +- test/nextjs/a-nextjs-app/next.test.js | 585 ++++++++++++++++++ .../pages/a-dynamic-page/[num].js | 2 +- test/nextjs/a-nextjs-app/pages/a-page.js | 2 +- ...ge.js => a-throw-in-getServerSideProps.js} | 8 +- .../pages/a-throw-in-page-handler.js | 20 + test/nextjs/a-nextjs-app/pages/an-ssr-page.js | 4 +- test/nextjs/a-nextjs-app/pages/index.js | 51 +- 15 files changed, 946 insertions(+), 189 deletions(-) create mode 100644 lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js create mode 100644 test/nextjs/a-nextjs-app/next.test.js rename test/nextjs/a-nextjs-app/pages/{an-ssr-error-page.js => a-throw-in-getServerSideProps.js} (74%) create mode 100644 test/nextjs/a-nextjs-app/pages/a-throw-in-page-handler.js diff --git a/.eslintrc.json b/.eslintrc.json index 9c0ca4ada1b..ffa403c327c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -19,7 +19,8 @@ "/lib/opentelemetry-bridge/opentelemetry-core-mini", "/test/babel/out.js", "/test/lambda/fixtures/esbuild-bundled-handler/hello.js", - "/test/nextjs/a-nextjs-app", + "/test/nextjs/a-nextjs-app/pages", + "/test/nextjs/a-nextjs-app/components", "/test/sourcemaps/fixtures/lib", "/test/sourcemaps/fixtures/src", "/test/stacktraces/fixtures/dist", diff --git a/lib/instrumentation/index.js b/lib/instrumentation/index.js index cbc3e13cbb3..af3cf4494b1 100644 --- a/lib/instrumentation/index.js +++ b/lib/instrumentation/index.js @@ -61,9 +61,10 @@ var MODULES = [ 'mysql2', // XXX // 'next', - 'next/dist/server/next', - 'next/dist/server/next-server', + // 'next/dist/server/next', 'next/dist/server/api-utils/node', + 'next/dist/server/dev/next-dev-server', + 'next/dist/server/next-server', 'pg', 'pug', 'redis', diff --git a/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js new file mode 100644 index 00000000000..7660dbe8954 --- /dev/null +++ b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js @@ -0,0 +1,211 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict' + +const semver = require('semver') + +const shimmer = require('../../../../../shimmer') + +const kInErrorHandling = Symbol('nextJsInErrorHandling') +const kSetTransNameFn = Symbol('nextJsSetTransNameFn') + +const noopFn = () => {} + +module.exports = function (mod, agent, { version, enabled }) { + console.log('XXX hi in "next-dev-server" instr') + if (!enabled) { + return mod + } + if (!semver.satisfies(version, '>=11.1.0 <13.0.0')) { + agent.logger.debug('next version %s not supported, skipping', version) + return mod + } + + const ins = agent._instrumentation + const log = agent.logger + + console.log('XXX mod: ', mod) + + const DevServer = mod.default + shimmer.wrap(DevServer.prototype, 'generateRoutes', wrapGenerateRoutes) + shimmer.wrap(DevServer.prototype, 'ensureApiPage', wrapEnsureApiPage) + shimmer.wrap(DevServer.prototype, 'findPageComponents', wrapFindPageComponents) + shimmer.wrap(DevServer.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse) + + return mod + + function wrapGenerateRoutes (orig) { + return function wrappedGenerateRoutes () { + if (this.constructor !== DevServer) { + return orig.apply(this, arguments) + } + const routes = orig.apply(this, arguments) + log.debug('wrap Next.js DevServer routes') + routes.redirects.forEach(wrapRedirectRoute) + routes.rewrites.beforeFiles.forEach(wrapRewriteRoute) + routes.rewrites.afterFiles.forEach(wrapRewriteRoute) + routes.rewrites.fallback.forEach(wrapRewriteRoute) + routes.fsRoutes.forEach(wrapFsRoute) + wrapCatchAllRoute(routes.catchAllRoute) + return routes + } + } + + // XXX Link to Next.js route type. + function wrapRedirectRoute (route) { + if (typeof route.fn !== 'function') { + return + } + const origRouteFn = route.fn + route.fn = function () { + const trans = ins.currTransaction() + if (trans) { + trans.setDefaultName('Next.js ' + route.name) + trans[kSetTransNameFn] = noopFn + } + return origRouteFn.apply(this, arguments) + } + } + + function wrapRewriteRoute (route) { + if (typeof route.fn !== 'function') { + return + } + const origRouteFn = route.fn + route.fn = function () { + const trans = ins.currTransaction() + if (trans) { + trans.setDefaultName(`Next.js ${route.name} -> ${route.destination}`) + trans[kSetTransNameFn] = noopFn + } + return origRouteFn.apply(this, arguments) + } + } + + // XXX splain + function wrapFsRoute (route) { + if (typeof route.fn !== 'function') { + return + } + const origRouteFn = route.fn + // We explicitly handle only the `fsRoute`s that we know by name in the + // Next.js code. We cannot set `trans.name` for all of them because of the + // true catch-all-routes that match any path and only sometimes handled them + // (e.g. 'public folder catchall'). + // XXX splain why we cannot do 'public folder catchall' and equiv. + switch (route.name) { + // XXX splain + case '_next/data catchall': + route.fn = function () { + const trans = ins.currTransaction() + if (trans) { + trans.setDefaultName(`Next.js ${route.name}`) + if (!trans[kSetTransNameFn]) { + trans[kSetTransNameFn] = (_req, pathname) => { + trans.setDefaultName(`Next.js _next/data route ${pathname}`) + } + } + } + return origRouteFn.apply(this, arguments) + } + break + case '_next/static/development/_devMiddlewareManifest.json': + case '_next/static/development/_devPagesManifest.json': + case '_next/development catchall': + case '_next/static catchall': + case '_next/image catchall': + case '_next catchall': + route.fn = function () { + const trans = ins.currTransaction() + if (trans) { + // XXX 'splain result.finished + trans.setDefaultName(`Next.js ${route.name}`) + } + return origRouteFn.apply(this, arguments) + } + break + } + } + + function wrapCatchAllRoute (route) { + if (typeof route.fn !== 'function') { + return + } + const origRouteFn = route.fn + route.fn = function () { + const trans = ins.currTransaction() + if (trans && !trans[kSetTransNameFn]) { + trans[kSetTransNameFn] = (req, pathname) => { + console.log('XXX kSetTransNameFn called for wrapCatchAllRoute: pathname=%s', pathname) + trans.setDefaultName(`${req.method} ${pathname}`) + } + } + return origRouteFn.apply(this, arguments) + } + } + + function wrapEnsureApiPage (orig) { + return function wrappedEnsureApiPage (pathname) { + if (this.constructor !== DevServer) { + return orig.apply(this, arguments) + } + const trans = ins.currTransaction() + if (trans && trans.req) { + // XXX slight limitation on "handled": It could *possibly* be false if + // exception in `getPagePath`. Trade-off between getting that + // wrong and setting the trans.name early enough for captureError + // usage. + log.trace({ pathname }, 'set transaction name from ensureApiPage') + trans.setDefaultName(`${trans.req.method} ${pathname}`) + } + return orig.apply(this, arguments) + } + } + + function wrapFindPageComponents (orig) { + return function wrappedFindPageComponents ({ pathname }) { + if (this.constructor !== DevServer) { + return orig.apply(this, arguments) + } + const promise = orig.apply(this, arguments) + promise.then(findComponentsResult => { + if (findComponentsResult) { + const trans = ins.currTransaction() + // If Next.js is doing error handling for this request, then it is + // loading an *error* page component (e.g. "_error.js"). We don't want + // to use that component's path for the transaction name. + if (trans && !trans[kInErrorHandling] && trans.req && trans[kSetTransNameFn]) { + log.trace({ pathname }, 'set transaction name from findPageComponents') + trans[kSetTransNameFn](trans.req, pathname) + } + } + }) + return promise + } + } + + function wrapRenderErrorToResponse (orig) { + return function wrappedRenderErrorToResponse (ctx, err) { + if (this.constructor !== DevServer) { + return orig.apply(this, arguments) + } + console.log('XXX wrappedRenderErrorToResponse(ctx, err=%s)', err && err.message) + const trans = ins.currTransaction() + if (trans) { + // Signal to subsequent instrumentation for this transaction that + // Next.js is now doing error handling for this request. + // XXX could try setting `kSetTransNameFn` to noop instead. Then only need the one symbol. + trans[kInErrorHandling] = true + } + // Next.js uses `err=null` to handle a 404. + if (err) { + agent.captureError(err) + } + return orig.apply(this, arguments) + } + } +} diff --git a/lib/instrumentation/modules/next/dist/server/next-server.js b/lib/instrumentation/modules/next/dist/server/next-server.js index 6a3c1cf9155..f601020a03f 100644 --- a/lib/instrumentation/modules/next/dist/server/next-server.js +++ b/lib/instrumentation/modules/next/dist/server/next-server.js @@ -6,6 +6,9 @@ 'use strict' +// XXX where to have discussion on duplication (almost) between this and next-dev-server.js + +// XXX this should go somewhere else // The main (but not only) module for instrumenting the Next.js server. // // Some notes on how the Next.js node server and the instrumentation works. @@ -40,14 +43,12 @@ const semver = require('semver') const shimmer = require('../../../../shimmer') const kInErrorHandling = Symbol('nextJsInErrorHandling') -// const kRouteName = Symbol('nextJsRouteName') -// const kPossibleApiRouteName = Symbol('nextJsPossibleApiRouteName') const kSetTransNameFn = Symbol('nextJsSetTransNameFn') -const kRouteIsSet = Symbol('nextJsRouteIsSet') + +const noopFn = () => {} module.exports = function (mod, agent, { version, enabled }) { - console.log('XXX enabled: ', enabled) - console.log('XXX version: ', version) + console.log('XXX hi in "next-server" instr') if (!enabled) { return mod } @@ -60,26 +61,31 @@ module.exports = function (mod, agent, { version, enabled }) { const log = agent.logger const NextNodeServer = mod.default - shimmer.wrap(NextNodeServer.prototype, 'generateRoutes', function (orig) { - return function playWrappedProdServerGenerateRoutes () { - // console.log('XXX playWrappedProdServerGenerateRoutes: start: is this my class? ', this.constructor === NextNodeServer) - if (this.constructor === NextNodeServer) { - console.log(' XXX do the prod server thing') - } - return orig.apply(this, arguments) - } - }) - // XXX might still use generateRoutes to wrap route.fn functions to capture - // the name of a route for the internal `_next`-y routes - // shimmer.wrap(NextNodeServer.prototype, 'generateRoutes', wrapGenerateRoutes) - // // shimmer.wrap(NextNodeServer.prototype, 'handleRequest', wrapHandleRequest) - // // shimmer.wrap(NextNodeServer.prototype, 'handleApiRequest', wrapHandleApiRequest) - // shimmer.wrap(NextNodeServer.prototype, 'ensureApiPage', wrapEnsureApiPage) - // shimmer.wrap(NextNodeServer.prototype, 'findPageComponents', wrapFindPageComponents) - // shimmer.wrap(NextNodeServer.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse) + shimmer.wrap(NextNodeServer.prototype, 'generateRoutes', wrapGenerateRoutes) + shimmer.wrap(NextNodeServer.prototype, 'ensureApiPage', wrapEnsureApiPage) + shimmer.wrap(NextNodeServer.prototype, 'findPageComponents', wrapFindPageComponents) + shimmer.wrap(NextNodeServer.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse) return mod + function wrapGenerateRoutes (orig) { + return function wrappedGenerateRoutes () { + if (this.constructor !== NextNodeServer) { + return orig.apply(this, arguments) + } + const routes = orig.apply(this, arguments) + log.debug('wrap Next.js NodeNextServer routes') + // console.log('XXX routes: ', routes) + routes.redirects.forEach(wrapRedirectRoute) + routes.rewrites.beforeFiles.forEach(wrapRewriteRoute) + routes.rewrites.afterFiles.forEach(wrapRewriteRoute) + routes.rewrites.fallback.forEach(wrapRewriteRoute) + routes.fsRoutes.forEach(wrapFsRoute) + wrapCatchAllRoute(routes.catchAllRoute) + return routes + } + } + // XXX Link to Next.js route type. function wrapRedirectRoute (route) { if (typeof route.fn !== 'function') { @@ -90,7 +96,7 @@ module.exports = function (mod, agent, { version, enabled }) { const trans = ins.currTransaction() if (trans) { trans.setDefaultName('Next.js ' + route.name) - trans[kSetTransNameFn] = () => {} // XXX static noop func for perf? + trans[kSetTransNameFn] = noopFn } return origRouteFn.apply(this, arguments) } @@ -105,26 +111,7 @@ module.exports = function (mod, agent, { version, enabled }) { const trans = ins.currTransaction() if (trans) { trans.setDefaultName(`Next.js ${route.name} -> ${route.destination}`) - // trans[kRouteIsSet] = true // XXX splain - trans[kSetTransNameFn] = () => {} // XXX static noop func for perf? - } - return origRouteFn.apply(this, arguments) - } - } - - function wrapCatchAllRoute (route) { - if (typeof route.fn !== 'function') { - return - } - const origRouteFn = route.fn - route.fn = function () { - const trans = ins.currTransaction() - // if (trans && !trans[kRouteIsSet]) { - if (trans && !trans[kSetTransNameFn]) { - trans[kSetTransNameFn] = (req, pathname) => { // XXX - console.log('XXX kSetTransNameFn called for wrapCatchAllRoute: pathname=%s', pathname) - trans.setDefaultName(`${req.method} ${pathname}`) - } + trans[kSetTransNameFn] = noopFn } return origRouteFn.apply(this, arguments) } @@ -136,124 +123,64 @@ module.exports = function (mod, agent, { version, enabled }) { return } const origRouteFn = route.fn - // XXX switch on route.name and allowlist the route.fn's we know + // We explicitly handle only the `fsRoute`s that we know by name in the + // Next.js code. We cannot set `trans.name` for all of them because of the + // true catch-all-routes that match any path and only sometimes handled them + // (e.g. 'public folder catchall'). + // XXX splain why we cannot do 'public folder catchall' and equiv. switch (route.name) { + // XXX splain case '_next/data catchall': route.fn = function () { const trans = ins.currTransaction() if (trans) { trans.setDefaultName(`Next.js ${route.name}`) if (!trans[kSetTransNameFn]) { - trans[kSetTransNameFn] = (req, pathname) => { - trans.setDefaultName(`Next.js data route ${req.method} ${pathname}`) + trans[kSetTransNameFn] = (_req, pathname) => { + trans.setDefaultName(`Next.js _next/data route ${pathname}`) } } } return origRouteFn.apply(this, arguments) } break - // XXX more here + case '_next/static catchall': + case '_next/image catchall': + case '_next catchall': + route.fn = function () { + const trans = ins.currTransaction() + if (trans) { + // XXX 'splain result.finished + trans.setDefaultName(`Next.js ${route.name}`) + } + return origRouteFn.apply(this, arguments) + } + break } } - function wrapFsRouteXXXold (route) { + + function wrapCatchAllRoute (route) { if (typeof route.fn !== 'function') { return } const origRouteFn = route.fn - // XXX switch on route.name and allowlist the route.fn's we know - if (route.name === '_next/data catchall') { - route.fn = function () { - const trans = ins.currTransaction() - if (trans) { - trans.setDefaultName(`Next.js ${route.name}`) - if (!trans[kSetTransNameFn]) { - trans[kSetTransNameFn] = (req, pathname) => { // XXX - // QQQ HERE, next is "_next/data" and all fsRoutes - console.log('XXX kSetTransNameFn called: pathname=%s', pathname) - trans.setDefaultName(`Next.js data route ${req.method} ${pathname}`) - } - } - } - return origRouteFn.apply(this, arguments) - } - } else { - route.fn = function () { - const promise = origRouteFn.apply(this, arguments) - const trans = ins.currTransaction() - if (trans) { - trans.setDefaultName(`Next.js ${route.name}`) - // promise.then(result => { - // // The `result.finished` check is specifically for the - // // "public folder catchall" route. We don't want to set the - // // transaction name if there isn't a matching "public/..." path. - // if (result && result.finished) { - // // XXX HERE why doesn't this work: - // // curl -i localhost:3000/favicon.ico - // // getting "GET unknown route" - // console.log('XXX setting trans.name from route: %s', route.name, result) - // trans.setDefaultName(`Next.js ${route.name}`) - // } - // }) + route.fn = function () { + const trans = ins.currTransaction() + if (trans && !trans[kSetTransNameFn]) { + trans[kSetTransNameFn] = (req, pathname) => { + console.log('XXX kSetTransNameFn called for wrapCatchAllRoute: pathname=%s', pathname) + trans.setDefaultName(`${req.method} ${pathname}`) } - return promise } + return origRouteFn.apply(this, arguments) } } - function wrapGenerateRoutes (orig) { - return function wrappedGenerateRoutes () { - const routes = orig.apply(this, arguments) - console.log('XXX wrappedGenerateRoutes') - // console.log('XXX routes: ', routes) - routes.redirects.forEach(wrapRedirectRoute) - routes.rewrites.beforeFiles.forEach(wrapRewriteRoute) - routes.rewrites.afterFiles.forEach(wrapRewriteRoute) - routes.rewrites.fallback.forEach(wrapRewriteRoute) - routes.fsRoutes.forEach(wrapFsRoute) - wrapCatchAllRoute(routes.catchAllRoute) - return routes - } - } - - // function wrapHandleRequest (orig) { - // return function wrappedHandleRequest (req, _res, parsedUrl) { - // console.log('\nXXX wrappedHandleRequest(req "%s %s", res, parsedUrl=%s)', req.method, req.url, parsedUrl) - // const promise = orig.apply(this, arguments) - // promise.then( - // () => { - // const trans = ins.currTransaction() - // if (trans && trans[kRouteName]) { - // trans.setDefaultName(`${req.method} ${trans[kRouteName]}`) - // console.log('XXX setDefaultName: ', trans.name) - // } - // } - // ) - // return promise - // } - // } - - // function wrapHandleApiRequest (orig) { - // return function wrappedHandleApiRequest () { - // const promise = orig.apply(this, arguments) - // promise.then( - // handled => { - // if (handled) { - // console.log('XXX handled: ', handled) - // // The API request was handled, therefore the route name found - // // in the wrapped `ensureApiPage` is the route name. - // const trans = ins.currTransaction() - // if (trans && trans[kPossibleApiRouteName]) { - // console.log('XXX could set trans name here') - // trans[kRouteName] = trans[kPossibleApiRouteName] - // } - // } - // } - // ) - // return promise - // } - // } function wrapEnsureApiPage (orig) { return function wrappedEnsureApiPage (pathname) { + if (this.constructor !== NextNodeServer) { + return orig.apply(this, arguments) + } const trans = ins.currTransaction() if (trans && trans.req) { // XXX slight limitation on "handled": It could *possibly* be false if @@ -269,6 +196,9 @@ module.exports = function (mod, agent, { version, enabled }) { function wrapFindPageComponents (orig) { return function wrappedFindPageComponents ({ pathname }) { + if (this.constructor !== NextNodeServer) { + return orig.apply(this, arguments) + } const promise = orig.apply(this, arguments) promise.then(findComponentsResult => { if (findComponentsResult) { @@ -278,8 +208,6 @@ module.exports = function (mod, agent, { version, enabled }) { // to use that component's path for the transaction name. if (trans && !trans[kInErrorHandling] && trans.req && trans[kSetTransNameFn]) { log.trace({ pathname }, 'set transaction name from findPageComponents') - // XXX - // trans.setDefaultName(`${trans.req.method} ${pathname}`) trans[kSetTransNameFn](trans.req, pathname) } } @@ -290,6 +218,9 @@ module.exports = function (mod, agent, { version, enabled }) { function wrapRenderErrorToResponse (orig) { return function wrappedRenderErrorToResponse (ctx, err) { + if (this.constructor !== NextNodeServer) { + return orig.apply(this, arguments) + } console.log('XXX wrappedRenderErrorToResponse(ctx, err=%s)', err && err.message) const trans = ins.currTransaction() if (trans) { diff --git a/lib/instrumentation/modules/next/dist/server/next.js b/lib/instrumentation/modules/next/dist/server/next.js index 04ae19c1ed8..ecbd4300acf 100644 --- a/lib/instrumentation/modules/next/dist/server/next.js +++ b/lib/instrumentation/modules/next/dist/server/next.js @@ -29,6 +29,8 @@ module.exports = function (mod, agent, { version, enabled }) { return mod } + return mod // XXX + const ins = agent._instrumentation const log = agent.logger diff --git a/test/nextjs/a-nextjs-app/apmsetup.js b/test/nextjs/a-nextjs-app/apmsetup.js index 3f7d2c22be0..e99e1a72726 100644 --- a/test/nextjs/a-nextjs-app/apmsetup.js +++ b/test/nextjs/a-nextjs-app/apmsetup.js @@ -4,9 +4,9 @@ * compliance with the BSD 2-Clause License. */ -require('elastic-apm-node').start({ +const apm = require('elastic-apm-node').start({ // XXX most of these for dev/debugging - apmServerVersion: '8.4.1', + // apmServerVersion: '8.4.1', cloudProvider: 'none', centralConfig: false, metricsInterval: '0s', @@ -17,3 +17,17 @@ require('elastic-apm-node').start({ apiRequestTime: '3s' // logLevel: 'debug' }) + +// Flush APM data on server process termination. +// https://nextjs.org/docs/deployment#manual-graceful-shutdowns +// +// XXX what version was `NEXT_MANUAL_SIG_HANDLE` added in? +// https://github.com/vercel/next.js/discussions/19693 +process.env.NEXT_MANUAL_SIG_HANDLE = 1 +function flushApmAndExit () { + apm.flush(() => { + process.exit(0) + }) +} +process.on('SIGTERM', flushApmAndExit) +process.on('SIGINT', flushApmAndExit) diff --git a/test/nextjs/a-nextjs-app/components/Header.js b/test/nextjs/a-nextjs-app/components/Header.js index e7bae9474f7..0986d75e88e 100644 --- a/test/nextjs/a-nextjs-app/components/Header.js +++ b/test/nextjs/a-nextjs-app/components/Header.js @@ -16,23 +16,16 @@ function Header () { A Next.js App
- {/*
diff --git a/test/nextjs/a-nextjs-app/next.config.js b/test/nextjs/a-nextjs-app/next.config.js index 1032c6313c4..ff64ede65e5 100644 --- a/test/nextjs/a-nextjs-app/next.config.js +++ b/test/nextjs/a-nextjs-app/next.config.js @@ -11,7 +11,7 @@ const nextConfig = { async redirects () { return [ { - source: '/a-page-redirect', + source: '/redirect-to-a-page', destination: '/a-page', permanent: false } @@ -23,7 +23,7 @@ const nextConfig = { { source: '/rewrite-with-a-has-condition', destination: '/a-page', - has: [{ type: 'query', key: 'overrideMe' }], + has: [{ type: 'query', key: 'overrideMe' }] } ], afterFiles: [ @@ -46,13 +46,11 @@ const nextConfig = { } ], fallback: [ - // These rewrites are checked after both pages/public files - // and dynamic routes are checked { source: '/rewrite-external/:path*', - destination: `https://old.example.com/:path*`, - }, - ], + destination: 'https://old.example.com/:path*' + } + ] } } } diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js new file mode 100644 index 00000000000..6bdbc1b4c9d --- /dev/null +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -0,0 +1,585 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict' + +// Test Next.js instrumentation. +// +// This test roughly does the following: +// - Start a MockAPMServer to capture intake requests. +// - `npm ci` to build the "a-nextjs-app" project. +// - Test instrumentation when using the Next.js production server. +// - `npm run build && npm run start` configured to send to our MockAPMServer. +// - Make every request in `TEST_REQUESTS` to the Next.js app. +// - Stop the Next.js app ("apmsetup.js" will flush the APM agent on SIGTERM). +// - Check all the received APM trace data matches the expected values in +// `TEST_REQUESTS`. +// - Test instrumentation when using the Next.js dev server. +// - `npm run dev` +// - (Same as above.) + +const { exec, spawn } = require('child_process') +const http = require('http') +const semver = require('semver') +const tape = require('tape') + +const { MockAPMServer } = require('../../_mock_apm_server') +const { dottedLookup } = require('../../_utils') + +if (semver.lt(process.version, '12.22.0')) { + console.log(`# SKIP next does not support node ${process.version}`) + process.exit() +} + +let apmServer +let serverUrl + +let TEST_REQUESTS = [ + { + testName: 'trailing slash redirect', + req: { method: 'GET', path: '/a-page/' }, + expectedRes: { + statusCode: 308, + headers: { location: '/a-page' } + }, + expectedApmEvents: [ + { + 'transaction.name': 'Next.js Redirect route /:path+/', + 'transaction.context.response.status_code': 308 + } + ] + }, + { + testName: 'configured (in next.config.js) redirect', + req: { method: 'GET', path: '/redirect-to-a-page' }, + expectedRes: { + statusCode: 307, + headers: { location: '/a-page' } + }, + expectedApmEvents: [ + { + 'transaction.name': 'Next.js Redirect route /redirect-to-a-page', + 'transaction.context.response.status_code': 307 + } + ] + }, + + // Rewrites are configured in "next.config.js". + { + testName: 'rewrite to a page', + req: { method: 'GET', path: '/rewrite-to-a-page' }, + expectedRes: { + statusCode: 200, + headers: { 'content-type': /text\/html/ }, + // This shows that we got the content from "pages/a-page.js". + body: /This is APage/ + }, + expectedApmEvents: [ + { + 'transaction.name': 'Next.js Rewrite route /rewrite-to-a-page -> /a-page', + 'transaction.context.response.status_code': 200 + } + ] + }, + { + testName: 'rewrite to a dynamic page', + // XXX improve this with 'num' query param + req: { method: 'GET', path: '/rewrite-to-a-dynamic-page' }, + expectedRes: { + statusCode: 200, + headers: { 'content-type': /text\/html/ }, + body: /This is ADynamicPage/ + }, + expectedApmEvents: [ + { + 'transaction.name': 'Next.js Rewrite route /rewrite-to-a-dynamic-page -> /a-dynamic-page/3.14', + 'transaction.context.response.status_code': 200 + } + ] + }, + { + testName: 'rewrite to a /public/... folder file', + req: { method: 'GET', path: '/rewrite-to-a-public-file' }, + expectedRes: { + statusCode: 200, + headers: { 'content-type': 'image/x-icon' } + }, + expectedApmEvents: [ + { + 'transaction.name': 'Next.js Rewrite route /rewrite-to-a-public-file -> /favicon.ico', + 'transaction.context.response.status_code': 200 + } + ] + }, + { + testName: 'rewrite to a 404', + req: { method: 'GET', path: '/rewrite-to-a-404' }, + expectedRes: { + statusCode: 404 + }, + expectedApmEvents: [ + { + 'transaction.name': 'Next.js Rewrite route /rewrite-to-a-404 -> /no-such-page', + 'transaction.context.response.status_code': 404 + } + ] + }, + { + testName: 'rewrite to a external site', + req: { method: 'GET', path: '/rewrite-external/foo' }, + expectedRes: { + // This is a 500 because the configured `old.example.com` doesn't resolve. + // Currently we don't capture an error for that, which is a limitation. + statusCode: 500 + }, + expectedApmEvents: [ + { + 'transaction.name': 'Next.js Rewrite route /rewrite-external/:path* -> https://old.example.com/:path*', + 'transaction.context.response.status_code': 500 + } + ] + }, + + { + testName: 'index page', + req: { method: 'GET', path: '/' }, + expectedRes: { + statusCode: 200, + headers: { 'content-type': /text\/html/ }, + body: /This is IndexPage/ + }, + expectedApmEvents: [ + { + 'transaction.name': 'GET /', + 'transaction.context.response.status_code': 200 + } + ] + }, + { + testName: 'a page (Server-Side Generated, SSG)', + req: { method: 'GET', path: '/a-page' }, + expectedRes: { + statusCode: 200, + headers: { 'content-type': /text\/html/ }, + body: /This is APage/ + }, + expectedApmEvents: [ + { + 'transaction.name': 'GET /a-page', + 'transaction.context.response.status_code': 200 + } + ] + }, + { + testName: 'a dynamic page', + req: { method: 'GET', path: '/a-dynamic-page/42' }, + expectedRes: { + statusCode: 200, + headers: { 'content-type': /text\/html/ }, + body: /This is ADynamicPage/ + }, + expectedApmEvents: [ + { + 'transaction.name': 'GET /a-dynamic-page/[num]', + 'transaction.context.response.status_code': 200 + } + ] + }, + { + testName: 'a server-side rendered (SSR) page', + req: { method: 'GET', path: '/an-ssr-page' }, + expectedRes: { + statusCode: 200, + headers: { 'content-type': /text\/html/ }, + body: /This is AnSSRPage/ + }, + expectedApmEvents: [ + { + 'transaction.name': 'GET /an-ssr-page', + 'transaction.context.response.status_code': 200 + } + ] + } +] +process.env.XXX_TEST_FILTER = '' +if (process.env.XXX_TEST_FILTER) { + TEST_REQUESTS = TEST_REQUESTS.filter(testReq => ~testReq.testName.indexOf(process.env.XXX_TEST_FILTER)) +} + +// XXX HERE TODO: +// - api endpoints: +// curl -i localhost:3000/api/an-api-endpoint +// curl -i localhost:3000/api/a-dynamic-api-endpoint/123 +// +// - error endpoints (grok the "bugs" in NOTES) +// curl -i localhost:3000/api/an-api-endpoint-that-throws +// ... the other two /throw-in-... pages + +// ---- utility functions + +/** + * Wait for the test a-nextjs-app server to be ready. + * + * This polls `GET /api/an-api-endpoint` until the expected 200 response is + * received. It times out after ~10s. + * + * @param {Test} t - This is only used to `t.comment(...)` with progress. + * @param {Function} cb - Calls `cb(err)` if there was a timeout, `cb()` on + * success. + */ +function waitForServerReady (t, cb) { + let sentinel = 10 + + const pollForServerReady = () => { + const req = http.get( + 'http://localhost:3000/api/an-api-endpoint', + { + agent: false, + timeout: 500 + }, + res => { + if (res.statusCode !== 200) { + res.resume() + scheduleNextPoll(`statusCode=${res.statusCode}`) + } + const chunks = [] + res.on('data', chunk => { chunks.push(chunk) }) + res.on('end', () => { + try { + const body = Buffer.concat(chunks).toString() + if (body && JSON.parse(body).ping === 'pong') { + cb() + } else { + scheduleNextPoll(`unexpected body: ${body}`) + } + } catch (bodyErr) { + scheduleNextPoll(bodyErr.message) + } + }) + } + ) + req.on('error', err => { + scheduleNextPoll(err.message) + }) + } + + const scheduleNextPoll = (msg) => { + t.comment(`[sentinel=${sentinel}] wait another 1s for server ready: ${msg}`) + sentinel-- + if (sentinel <= 0) { + cb(new Error('timed out')) + } else { + setTimeout(pollForServerReady, 1000) + } + } + + pollForServerReady() +} + +async function makeTestRequest (t, testReq) { + return new Promise((resolve, reject) => { + const url = `http://localhost:3000${testReq.req.path}` + t.comment(`makeTestRequest: ${testReq.testName} (${testReq.req.method} ${url})`) + const req = http.request( + url, + { + method: testReq.req.method + }, + res => { + const chunks = [] + res.on('data', chunk => { chunks.push(chunk) }) + res.on('end', () => { + const body = Buffer.concat(chunks) + console.log('XXX res:', res.statusCode, res.headers, + res.headers['content-type'] && ~res.headers['content-type'].indexOf('text') && body.toString(), + '\n--') + if (testReq.expectedRes.statusCode) { + t.equal(res.statusCode, testReq.expectedRes.statusCode, `res.statusCode === ${testReq.expectedRes.statusCode}`) + } + if (testReq.expectedRes.headers) { + for (const [k, v] of Object.entries(testReq.expectedRes.headers)) { + if (v instanceof RegExp) { + t.ok(v.test(res.headers[k]), `res.headers[${JSON.stringify(k)}] =~ ${v}`) + } else { + t.equal(res.headers[k], v, `res.headers[${JSON.stringify(k)}] === ${JSON.stringify(v)}`) + } + } + } + if (testReq.expectedRes.body) { + if (testReq.expectedRes.body instanceof RegExp) { + t.ok(testReq.expectedRes.body.test(body), `body =~ ${testReq.expectedRes.body}`) + } else { + t.equal(body, testReq.expectedRes.body, 'body') + } + } + resolve() + }) + } + ) + req.on('error', reject) + req.end() + }) +} + +/** + * Assert that the given `apmEvents` (events that the mock APM server received) + * match all the expected APM events in `TEST_REQUESTS`. + */ +function checkExpectedApmEvents (t, apmEvents) { + // metadata + let evt = apmEvents.shift() + t.ok(evt.metadata, 'metadata is first event') + t.equal(evt.metadata.service.name, 'a-nextjs-app', 'metadata.service.name') + // XXX assert framework is "next" or "nextjs" or some value we pick + + // One `GET /api/an-api-endpoint` from waitForServerReady. + evt = apmEvents.shift() + t.equal(evt.transaction.name, 'GET /api/an-api-endpoint', 'waitForServerReady request') + t.equal(evt.transaction.outcome, 'success', 'transaction.outcome') + + // Expected APM events from all TEST_REQUESTS. + TEST_REQUESTS.forEach(testReq => { + t.comment(`check APM events for "${testReq.testName}"`) + testReq.expectedApmEvents.forEach(expectedApmEvent => { + console.log('XXX expectedApmEvent: ', expectedApmEvent) + evt = apmEvents.shift() + console.log('XXX evt:'); console.dir(evt, { depth: 5 }) + for (const [k, v] of Object.entries(expectedApmEvent)) { + t.equal(dottedLookup(evt, k), v, `${k} === ${JSON.stringify(v)}`) + } + }) + }) + + // console.log('XXX remaining apmEvents:', apmEvents) + t.equal(apmEvents.length, 0, 'no additional APM server events: ' + JSON.stringify(apmEvents)) +} + +// ---- tests + +const SKIP_NPM_CI_FOR_DEV = process.env.USER === 'trentm' // XXX +if (!SKIP_NPM_CI_FOR_DEV) { + tape.test('setup: npm ci', t => { + const startTime = Date.now() + exec( + 'npm ci', + { + cwd: __dirname + }, + function (err, stdout, stderr) { + t.error(err, `"npm ci" succeeded (took ${(Date.now() - startTime) / 1000}s)`) + if (err) { + t.comment(`$ npm ci\n-- stdout --\n${stdout}\n-- stderr --\n${stderr}\n--`) + } + t.end() + } + ) + }) +} + +tape.test('setup: mock APM server', t => { + apmServer = new MockAPMServer({ apmServerVersion: '7.15.0' }) + apmServer.start(function (serverUrl_) { + serverUrl = serverUrl_ + t.comment('mock APM serverUrl: ' + serverUrl) + t.end() + }) +}) + +// Test the Next "prod" server. I.e. `npm run build && npm run start`. +tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { + let nextServerProc + + suite.test('setup: npm run build', t => { + const startTime = Date.now() + exec( + 'npm run build', + { + cwd: __dirname + }, + function (err, stdout, stderr) { + t.error(err, `"npm run build" succeeded (took ${(Date.now() - startTime) / 1000}s)`) + if (err) { + t.comment(`$ npm run build\n-- stdout --\n${stdout}\n-- stderr --\n${stderr}\n--`) + } + t.end() + } + ) + }) + + suite.test('setup: start Next.js prod server (npm run start)', t => { + nextServerProc = spawn( + 'npm', + ['run', 'start'], + { + cwd: __dirname, + env: Object.assign({}, process.env, { + ELASTIC_APM_SERVER_URL: serverUrl + }) + } + ) + nextServerProc.on('error', err => { + t.error(err, 'no error from "npm run start"') + }) + nextServerProc.stdout.on('data', data => { + t.comment(`[Next.js server stdout] ${data}`) + }) + nextServerProc.stderr.on('data', data => { + t.comment(`[Next.js server stderr] ${data}`) + }) + + // Allow some time for an early fail of `npm run start`, e.g. if there is + // already a user of port 3000... + const onEarlyClose = code => { + t.fail(`"npm run start" failed early: code=${code}`) + nextServerProc = null + clearTimeout(earlyCloseTimer) + t.end() + } + nextServerProc.on('close', onEarlyClose) + const earlyCloseTimer = setTimeout(() => { + nextServerProc.removeListener('close', onEarlyClose) + + // ... then wait for the server to be ready. + waitForServerReady(t, waitErr => { + if (waitErr) { + t.fail(`error waiting for Next.js server to be ready: ${waitErr.message}`) + nextServerProc.kill('SIGKILL') + nextServerProc = null + } else { + t.comment('Next.js server is ready') + } + t.end() + }) + }, 1000) + }) + + suite.test('make requests', async t => { + if (!nextServerProc) { + t.skip('there is no nextServerProc') + t.end() + return + } + apmServer.clear() + + for (let i = 0; i < TEST_REQUESTS.length; i++) { + await makeTestRequest(t, TEST_REQUESTS[i]) + } + + t.end() + }) + + suite.test('check all APM events', t => { + if (!nextServerProc) { + t.skip('there is no nextServerProc') + t.end() + return + } + + // To ensure we get all the trace data from the instrumented Next.js + // server, we SIGTERM it and rely on the graceful-exit apm.flush() in + // "apmsetup.js" to flush it. + nextServerProc.on('close', code => { + t.equal(code, 0, 'Next.js server exit status was 0') + checkExpectedApmEvents(t, apmServer.events) + t.end() + }) + nextServerProc.kill('SIGTERM') + }) + + suite.end() +}) + +// Test the Next "dev" server. I.e. `npm run dev`. +tape.test('-- dev server tests --', suite => { + let nextServerProc + + suite.test('setup: start Next.js dev server (npm run dev)', t => { + nextServerProc = spawn( + 'npm', + ['run', 'dev'], + { + cwd: __dirname, + env: Object.assign({}, process.env, { + ELASTIC_APM_SERVER_URL: serverUrl + }) + } + ) + nextServerProc.on('error', err => { + t.error(err, 'no error from "npm run dev"') + }) + nextServerProc.stdout.on('data', data => { + t.comment(`[Next.js server stdout] ${data}`) + }) + nextServerProc.stderr.on('data', data => { + t.comment(`[Next.js server stderr] ${data}`) + }) + + // Allow some time for an early fail of `npm run dev`, e.g. if there is + // already a user of port 3000... + const onEarlyClose = code => { + t.fail(`"npm run dev" failed early: code=${code}`) + nextServerProc = null + clearTimeout(earlyCloseTimer) + t.end() + } + nextServerProc.on('close', onEarlyClose) + const earlyCloseTimer = setTimeout(() => { + nextServerProc.removeListener('close', onEarlyClose) + + // ... then wait for the server to be ready. + waitForServerReady(t, waitErr => { + if (waitErr) { + t.fail(`error waiting for Next.js server to be ready: ${waitErr.message}`) + nextServerProc.kill('SIGKILL') + nextServerProc = null + } else { + t.comment('Next.js server is ready') + } + t.end() + }) + }, 1000) + }) + + suite.test('make requests', async t => { + if (!nextServerProc) { + t.skip('there is no nextServerProc') + t.end() + return + } + apmServer.clear() + + for (let i = 0; i < TEST_REQUESTS.length; i++) { + await makeTestRequest(t, TEST_REQUESTS[i]) + } + + t.end() + }) + + suite.test('check all APM events', t => { + if (!nextServerProc) { + t.skip('there is no nextServerProc') + t.end() + return + } + + // To ensure we get all the trace data from the instrumented Next.js + // server, we SIGTERM it and rely on the graceful-exit apm.flush() in + // "apmsetup.js" to flush it. + nextServerProc.on('close', code => { + t.equal(code, 0, 'Next.js server exit status was 0') + checkExpectedApmEvents(t, apmServer.events) + t.end() + }) + nextServerProc.kill('SIGTERM') + }) + + suite.end() +}) + +tape.test('teardown: mock APM server', t => { + apmServer.close() + t.end() +}) diff --git a/test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js b/test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js index 8cff9e46eee..6b57c73e24e 100644 --- a/test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js +++ b/test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js @@ -39,7 +39,7 @@ const ADynamicPage = ({ doubleThat }) => { <>
-
ADynamicPage {num} - doubleThat is {doubleThat}
+
This is ADynamicPage {num} - doubleThat is {doubleThat}
) diff --git a/test/nextjs/a-nextjs-app/pages/a-page.js b/test/nextjs/a-nextjs-app/pages/a-page.js index 0ee2a2d6ba5..76c250e3936 100644 --- a/test/nextjs/a-nextjs-app/pages/a-page.js +++ b/test/nextjs/a-nextjs-app/pages/a-page.js @@ -25,7 +25,7 @@ function APage ({ buildTime }) { <>
-
A Page (built at {new Date(buildTime).toISOString()})
+
This is APage (built at {new Date(buildTime).toISOString()})
Elastic logo
diff --git a/test/nextjs/a-nextjs-app/pages/an-ssr-error-page.js b/test/nextjs/a-nextjs-app/pages/a-throw-in-getServerSideProps.js similarity index 74% rename from test/nextjs/a-nextjs-app/pages/an-ssr-error-page.js rename to test/nextjs/a-nextjs-app/pages/a-throw-in-getServerSideProps.js index 430f1fdc1e0..bc360406d3f 100644 --- a/test/nextjs/a-nextjs-app/pages/an-ssr-error-page.js +++ b/test/nextjs/a-nextjs-app/pages/a-throw-in-getServerSideProps.js @@ -11,18 +11,18 @@ import Header from '../components/Header' // Gets called on every request. export async function getServerSideProps () { - throw new Error('AnSSRErrorPage thrown error') + throw new Error('thrown error in getServerSideProps') } -function AnSSRErrorPage ({ currTime }) { +function AThrowInGetServerSideProps () { return ( <>
-
AnSSRErrorPage (currTime is {new Date(currTime).toISOString()})
+
This is AThrowInGetServerSideProps.
) } -export default AnSSRErrorPage +export default AThrowInGetServerSideProps diff --git a/test/nextjs/a-nextjs-app/pages/a-throw-in-page-handler.js b/test/nextjs/a-nextjs-app/pages/a-throw-in-page-handler.js new file mode 100644 index 00000000000..1b8b362729c --- /dev/null +++ b/test/nextjs/a-nextjs-app/pages/a-throw-in-page-handler.js @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +// Gets called on every request. +export async function getServerSideProps () { + return { + props: { + currTime: Date.now() + } + } +} + +function AThrowInPageHandler ({ currTime }) { + throw new Error('throw in page handler') +} + +export default AThrowInPageHandler diff --git a/test/nextjs/a-nextjs-app/pages/an-ssr-page.js b/test/nextjs/a-nextjs-app/pages/an-ssr-page.js index 68de30b3212..ffdf2ed410d 100644 --- a/test/nextjs/a-nextjs-app/pages/an-ssr-page.js +++ b/test/nextjs/a-nextjs-app/pages/an-ssr-page.js @@ -10,7 +10,6 @@ import Header from '../components/Header' // Gets called on every request. export async function getServerSideProps () { - console.log('XXX AnSSRPage.getServerSideProps') return { props: { currTime: Date.now() @@ -19,12 +18,11 @@ export async function getServerSideProps () { } function AnSSRPage ({ currTime }) { - console.log('XXX AnSSRPage') return ( <>
-
AnSSRPage (currTime is {new Date(currTime).toISOString()})
+
This is AnSSRPage (currTime is {new Date(currTime).toISOString()})
) diff --git a/test/nextjs/a-nextjs-app/pages/index.js b/test/nextjs/a-nextjs-app/pages/index.js index a53672df0d9..b48ecc89490 100644 --- a/test/nextjs/a-nextjs-app/pages/index.js +++ b/test/nextjs/a-nextjs-app/pages/index.js @@ -7,51 +7,59 @@ import Link from 'next/link' import Header from '../components/Header' -function Home () { +function IndexPage () { return ( <>
-
Welcome to A-Next.js-App!
+
Welcome to A-Next.js-App! This is IndexPage.
) } -export default Home +export default IndexPage From ce06f35ccc0a1c8903aa04d83f0317878bb6b3bc Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 6 Oct 2022 17:47:09 -0700 Subject: [PATCH 09/66] drop this unused instrumentation module --- .eslintrc.json | 2 +- lib/instrumentation/index.js | 3 - .../modules/next/dist/server/next.js | 232 ------------------ 3 files changed, 1 insertion(+), 236 deletions(-) delete mode 100644 lib/instrumentation/modules/next/dist/server/next.js diff --git a/.eslintrc.json b/.eslintrc.json index ffa403c327c..872b202c8e6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -13,7 +13,7 @@ "ignorePatterns": [ "/.nyc_output", "/build", - "node_modules", + "node_modules*", "/examples/esbuild/dist", "/examples/typescript/dist", "/lib/opentelemetry-bridge/opentelemetry-core-mini", diff --git a/lib/instrumentation/index.js b/lib/instrumentation/index.js index af3cf4494b1..03f62e718d5 100644 --- a/lib/instrumentation/index.js +++ b/lib/instrumentation/index.js @@ -59,9 +59,6 @@ var MODULES = [ 'mongodb', 'mysql', 'mysql2', - // XXX - // 'next', - // 'next/dist/server/next', 'next/dist/server/api-utils/node', 'next/dist/server/dev/next-dev-server', 'next/dist/server/next-server', diff --git a/lib/instrumentation/modules/next/dist/server/next.js b/lib/instrumentation/modules/next/dist/server/next.js deleted file mode 100644 index ecbd4300acf..00000000000 --- a/lib/instrumentation/modules/next/dist/server/next.js +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and other contributors where applicable. - * Licensed under the BSD 2-Clause License; you may not use this file except in - * compliance with the BSD 2-Clause License. - */ - -'use strict' - -const semver = require('semver') - -const shimmer = require('../../../../shimmer') - -const kInErrorHandling = Symbol('nextJsInErrorHandling') -const kSetTransNameFn = Symbol('nextJsSetTransNameFn') - -const noopFn = () => {} - -module.exports = function (mod, agent, { version, enabled }) { - console.log('XXX hi in "next" instr (at next/dist/server/next.js)') - if (!enabled) { - return mod - } - if (!semver.satisfies(version, '>=11.1.0 <13.0.0')) { - agent.logger.debug('next version %s not supported, skipping', version) - return mod - } - if (typeof mod !== 'function' || mod.default !== mod) { - agent.logger.debug('next module is not the expected form, skipping') - return mod - } - - return mod // XXX - - const ins = agent._instrumentation - const log = agent.logger - - // The top-level module `createServer()` returns a `NextServer` with a - // `.createServer()` method that returns a promise for a next Server subclass - // instance that we want to instrument. - agent.logger.debug('wrapping next (createServer)') - const origCreateServer = mod - function wrappedModCreateServer () { - const nextServerInst = origCreateServer.apply(this, arguments) - console.log('XXX nextServerInst: ', nextServerInst.constructor.prototype) - shimmer.wrap(nextServerInst.constructor.prototype, 'createServer', wrapCreateServer) - return nextServerInst - } - wrappedModCreateServer.default = wrappedModCreateServer - - return wrappedModCreateServer - - function wrapCreateServer (orig) { - return function wrappedCreateServer () { - const promise = orig.apply(this, arguments) - promise.then(serverInst => { - // `serverInst` is an instance of next-server.ts `NextNodeServer` (when - // using `next build && next start`) or next-dev-server.ts `DevServer` - // (when using `next dev`). - const prototype = serverInst.constructor.prototype - console.log('XXX prototype: ', prototype.generateRoutes, serverInst.constructor) - shimmer.wrap(prototype, 'generateRoutes', wrapGenerateRoutes) - shimmer.wrap(prototype, 'ensureApiPage', wrapEnsureApiPage) - shimmer.wrap(prototype, 'findPageComponents', wrapFindPageComponents) - shimmer.wrap(prototype, 'renderErrorToResponse', wrapRenderErrorToResponse) - }) - return promise - } - } - - function wrapGenerateRoutes (orig) { - console.log('XXX wrapGenerateRoutes') - return function wrappedGenerateRoutes () { - if (this.constructor.name === 'DevServer') { - console.log(' XXX do the dev server thing') - } - console.log('XXX wrappedGenerateRoutes') - const routes = orig.apply(this, arguments) - console.log('XXX routes: ', routes) - routes.redirects.forEach(wrapRedirectRoute) - routes.rewrites.beforeFiles.forEach(wrapRewriteRoute) - routes.rewrites.afterFiles.forEach(wrapRewriteRoute) - routes.rewrites.fallback.forEach(wrapRewriteRoute) - routes.fsRoutes.forEach(wrapFsRoute) - wrapCatchAllRoute(routes.catchAllRoute) - return routes - } - } - - // XXX Link to Next.js route type. - function wrapRedirectRoute (route) { - if (typeof route.fn !== 'function') { - return - } - const origRouteFn = route.fn - route.fn = function () { - const trans = ins.currTransaction() - if (trans) { - trans.setDefaultName('Next.js ' + route.name) - trans[kSetTransNameFn] = noopFn - } - return origRouteFn.apply(this, arguments) - } - } - - function wrapRewriteRoute (route) { - if (typeof route.fn !== 'function') { - return - } - const origRouteFn = route.fn - route.fn = function () { - const trans = ins.currTransaction() - if (trans) { - trans.setDefaultName(`Next.js ${route.name} -> ${route.destination}`) - trans[kSetTransNameFn] = noopFn - } - return origRouteFn.apply(this, arguments) - } - } - - function wrapFsRoute (route) { - if (typeof route.fn !== 'function') { - return - } - const origRouteFn = route.fn - // We explicitly handle only the `fsRoute`s that we know by name in the - // Next.js code. We cannot set `trans.name` for all of them because of the - // true catch-all-routes that match any path and only sometimes handled them - // (e.g. 'public folder catchall'). - // XXX splain why we cannot do 'public folder catchall' and equiv. - switch (route.name) { - // XXX splain - case '_next/data catchall': - route.fn = function () { - const trans = ins.currTransaction() - if (trans) { - trans.setDefaultName(`Next.js ${route.name}`) - if (!trans[kSetTransNameFn]) { - trans[kSetTransNameFn] = (_req, pathname) => { - trans.setDefaultName(`Next.js _next/data for ${pathname}`) - } - } - } - return origRouteFn.apply(this, arguments) - } - break - case '_next/static/development/_devMiddlewareManifest.json': - case '_next/static/development/_devPagesManifest.json': - case '_next/development catchall': - case '_next/static catchall': - case '_next/image catchall': - case '_next catchall': - route.fn = function () { - const trans = ins.currTransaction() - if (trans) { - // XXX 'splain result.finished - trans.setDefaultName(`Next.js ${route.name}`) - } - return origRouteFn.apply(this, arguments) - } - break - } - } - - function wrapCatchAllRoute (route) { - if (typeof route.fn !== 'function') { - return - } - const origRouteFn = route.fn - route.fn = function () { - const trans = ins.currTransaction() - if (trans && !trans[kSetTransNameFn]) { - trans[kSetTransNameFn] = (req, pathname) => { - console.log('XXX kSetTransNameFn called for wrapCatchAllRoute: pathname=%s', pathname) - trans.setDefaultName(`${req.method} ${pathname}`) - } - } - return origRouteFn.apply(this, arguments) - } - } - - function wrapEnsureApiPage (orig) { - return function wrappedEnsureApiPage (pathname) { - const trans = ins.currTransaction() - if (trans && trans.req) { - // XXX slight limitation on "handled": It could *possibly* be false if - // exception in `getPagePath`. Trade-off between getting that - // wrong and setting the trans.name early enough for captureError - // usage. - log.trace({ pathname }, 'set transaction name from ensureApiPage') - trans.setDefaultName(`${trans.req.method} ${pathname}`) - } - return orig.apply(this, arguments) - } - } - - function wrapFindPageComponents (orig) { - return function wrappedFindPageComponents ({ pathname }) { - const promise = orig.apply(this, arguments) - promise.then(findComponentsResult => { - if (findComponentsResult) { - const trans = ins.currTransaction() - // If Next.js is doing error handling for this request, then it is - // loading an *error* page component (e.g. "_error.js"). We don't want - // to use that component's path for the transaction name. - if (trans && !trans[kInErrorHandling] && trans.req && trans[kSetTransNameFn]) { - log.trace({ pathname }, 'set transaction name from findPageComponents') - trans[kSetTransNameFn](trans.req, pathname) - } - } - }) - return promise - } - } - - function wrapRenderErrorToResponse (orig) { - return function wrappedRenderErrorToResponse (ctx, err) { - console.log('XXX wrappedRenderErrorToResponse(ctx, err=%s)', err && err.message) - const trans = ins.currTransaction() - if (trans) { - // Signal to subsequent instrumentation for this transaction that - // Next.js is now doing error handling for this request. - // XXX could try setting `kSetTransNameFn` to noop instead. Then only need the one symbol. - trans[kInErrorHandling] = true - } - // Next.js uses `err=null` to handle a 404. - if (err) { - agent.captureError(err) - } - return orig.apply(this, arguments) - } - } -} From dc04846d4b6b95ed44cd1f579bac85c9c3e790f9 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Tue, 11 Oct 2022 16:00:40 -0700 Subject: [PATCH 10/66] fix config.test.js --- test/config.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/config.test.js b/test/config.test.js index 8cee47734d8..afb8916e3bd 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -1030,8 +1030,9 @@ test('disableInstrumentations', function (t) { modules.delete('koa-router') // koa-router@11 supports node >=12 modules.delete('@koa/router') // koa-router@11 supports node >=12 } - modules.delete('next/dist/server/next-server') modules.delete('next/dist/server/api-utils/node') + modules.delete('next/dist/server/dev/next-dev-server') + modules.delete('next/dist/server/next-server') function testSlice (t, name, selector) { var selection = selector(modules) From ff5dd7474b602646b5fc3e8b38573a49a1fb7692 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 12 Oct 2022 14:16:21 -0700 Subject: [PATCH 11/66] more test cases, fixing some edge cases with error capture --- .../next/dist/server/api-utils/node.js | 17 +- .../next/dist/server/dev/next-dev-server.js | 17 +- .../modules/next/dist/server/next-server.js | 22 +- test/nextjs/a-nextjs-app/next.test.js | 260 ++++++++++++------ .../pages/a-throw-in-page-handler.js | 5 + 5 files changed, 225 insertions(+), 96 deletions(-) diff --git a/lib/instrumentation/modules/next/dist/server/api-utils/node.js b/lib/instrumentation/modules/next/dist/server/api-utils/node.js index a3468b3ae30..261ecb262bb 100644 --- a/lib/instrumentation/modules/next/dist/server/api-utils/node.js +++ b/lib/instrumentation/modules/next/dist/server/api-utils/node.js @@ -7,12 +7,15 @@ 'use strict' // XXX comment this module is solely about capturing possible error in a user's API handler. -// It is admittedly a lot of code for just that. +// It is admittedly a lot of code for just that. In a DevServer we *do* also get a +// renderErrorToResponse, but not in a prod server. const semver = require('semver') const shimmer = require('../../../../../shimmer') +const kCapturedTransErr = Symbol.for('nextJsCapturedTransErr') + module.exports = function (mod, agent, { version, enabled }) { console.log('XXX enabled: ', enabled) console.log('XXX version: ', version) @@ -24,6 +27,8 @@ module.exports = function (mod, agent, { version, enabled }) { return mod } + const ins = agent._instrumentation + shimmer.wrap(mod, 'apiResolver', wrapApiResolver) return mod @@ -70,12 +75,22 @@ module.exports = function (mod, agent, { version, enabled }) { try { promise = orig.apply(this, arguments) } catch (syncErr) { + console.log('XXX wrappedApiHandler: capture syncErr') agent.captureError(syncErr) + const trans = ins.currTransaction() + if (trans) { + trans[kCapturedTransErr] = syncErr + } throw syncErr } if (promise) { promise.catch(rejectErr => { + console.log('XXX wrappedApiHandler: capture rejectErr') agent.captureError(rejectErr) + const trans = ins.currTransaction() + if (trans) { + trans[kCapturedTransErr] = rejectErr + } }) } return promise diff --git a/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js index 7660dbe8954..46b49f5f41a 100644 --- a/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js +++ b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js @@ -10,8 +10,8 @@ const semver = require('semver') const shimmer = require('../../../../../shimmer') -const kInErrorHandling = Symbol('nextJsInErrorHandling') -const kSetTransNameFn = Symbol('nextJsSetTransNameFn') +const kInErrorHandling = Symbol.for('nextJsInErrorHandling') +const kSetTransNameFn = Symbol.for('nextJsSetTransNameFn') const noopFn = () => {} @@ -34,7 +34,8 @@ module.exports = function (mod, agent, { version, enabled }) { shimmer.wrap(DevServer.prototype, 'generateRoutes', wrapGenerateRoutes) shimmer.wrap(DevServer.prototype, 'ensureApiPage', wrapEnsureApiPage) shimmer.wrap(DevServer.prototype, 'findPageComponents', wrapFindPageComponents) - shimmer.wrap(DevServer.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse) + // XXX splain the 'NextNodeServer.renderErrorToResponse' base is used. + // shimmer.wrap(DevServer.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse) return mod @@ -142,6 +143,10 @@ module.exports = function (mod, agent, { version, enabled }) { trans[kSetTransNameFn] = (req, pathname) => { console.log('XXX kSetTransNameFn called for wrapCatchAllRoute: pathname=%s', pathname) trans.setDefaultName(`${req.method} ${pathname}`) + // Ensure only the first `findPageComponents` result sets the trans + // name, otherwise a loaded `/_error` for page error handling could + // incorrectly override. + trans[kSetTransNameFn] = noopFn } } return origRouteFn.apply(this, arguments) @@ -161,6 +166,9 @@ module.exports = function (mod, agent, { version, enabled }) { // usage. log.trace({ pathname }, 'set transaction name from ensureApiPage') trans.setDefaultName(`${trans.req.method} ${pathname}`) + // In the DevServer an exception in an API handler will be followed up + // with a rendering of "/_error". + trans[kSetTransNameFn] = noopFn } return orig.apply(this, arguments) } @@ -178,6 +186,7 @@ module.exports = function (mod, agent, { version, enabled }) { // If Next.js is doing error handling for this request, then it is // loading an *error* page component (e.g. "_error.js"). We don't want // to use that component's path for the transaction name. + console.log('XXX next-dev-server wrappedFindPageComponents: trans[kInErrorHandling]=%s', trans[kInErrorHandling], kInErrorHandling) if (trans && !trans[kInErrorHandling] && trans.req && trans[kSetTransNameFn]) { log.trace({ pathname }, 'set transaction name from findPageComponents') trans[kSetTransNameFn](trans.req, pathname) @@ -190,7 +199,9 @@ module.exports = function (mod, agent, { version, enabled }) { function wrapRenderErrorToResponse (orig) { return function wrappedRenderErrorToResponse (ctx, err) { + console.log('XXX next-dev-server.js wrappedRenderErrorToResponse: start') if (this.constructor !== DevServer) { + console.log('XXX wrappedRenderErrorToResponse -> ctor != DevServer abort') return orig.apply(this, arguments) } console.log('XXX wrappedRenderErrorToResponse(ctx, err=%s)', err && err.message) diff --git a/lib/instrumentation/modules/next/dist/server/next-server.js b/lib/instrumentation/modules/next/dist/server/next-server.js index f601020a03f..0c496e89ddf 100644 --- a/lib/instrumentation/modules/next/dist/server/next-server.js +++ b/lib/instrumentation/modules/next/dist/server/next-server.js @@ -42,8 +42,9 @@ const semver = require('semver') const shimmer = require('../../../../shimmer') -const kInErrorHandling = Symbol('nextJsInErrorHandling') -const kSetTransNameFn = Symbol('nextJsSetTransNameFn') +const kInErrorHandling = Symbol.for('nextJsInErrorHandling') +const kSetTransNameFn = Symbol.for('nextJsSetTransNameFn') +const kCapturedTransErr = Symbol.for('nextJsCapturedTransErr') const noopFn = () => {} @@ -96,6 +97,7 @@ module.exports = function (mod, agent, { version, enabled }) { const trans = ins.currTransaction() if (trans) { trans.setDefaultName('Next.js ' + route.name) + // XXX 'splain trans[kSetTransNameFn] = noopFn } return origRouteFn.apply(this, arguments) @@ -170,6 +172,7 @@ module.exports = function (mod, agent, { version, enabled }) { trans[kSetTransNameFn] = (req, pathname) => { console.log('XXX kSetTransNameFn called for wrapCatchAllRoute: pathname=%s', pathname) trans.setDefaultName(`${req.method} ${pathname}`) + console.log('XXX QQQ') // why not _error load re-call of this? } } return origRouteFn.apply(this, arguments) @@ -189,6 +192,7 @@ module.exports = function (mod, agent, { version, enabled }) { // usage. log.trace({ pathname }, 'set transaction name from ensureApiPage') trans.setDefaultName(`${trans.req.method} ${pathname}`) + trans[kSetTransNameFn] = noopFn } return orig.apply(this, arguments) } @@ -218,9 +222,11 @@ module.exports = function (mod, agent, { version, enabled }) { function wrapRenderErrorToResponse (orig) { return function wrappedRenderErrorToResponse (ctx, err) { - if (this.constructor !== NextNodeServer) { - return orig.apply(this, arguments) - } + // XXX + // if (this.constructor !== NextNodeServer) { + // console.log('XXX wrappedRenderErrorToResponse -> ctor != NextNodeServer abort') + // return orig.apply(this, arguments) + // } console.log('XXX wrappedRenderErrorToResponse(ctx, err=%s)', err && err.message) const trans = ins.currTransaction() if (trans) { @@ -228,9 +234,13 @@ module.exports = function (mod, agent, { version, enabled }) { // Next.js is now doing error handling for this request. // XXX could try setting `kSetTransNameFn` to noop instead. Then only need the one symbol. trans[kInErrorHandling] = true + console.log('XXX next-server.js: set kInErrorHandling', kInErrorHandling) } // Next.js uses `err=null` to handle a 404. - if (err) { + // XXX 'splain duplicate for apiResolver() handling in dev mode vs prod mode. + console.log('XXX duplicate?', trans[kCapturedTransErr] === err) + if (err && (!trans || trans[kCapturedTransErr] !== err)) { + console.log('XXX capture error in wrappedRenderErrorToResponse') agent.captureError(err) } return orig.apply(this, arguments) diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index 6bdbc1b4c9d..dfb1cb3acfd 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -21,6 +21,7 @@ // - `npm run dev` // - (Same as above.) +const assert = require('assert') const { exec, spawn } = require('child_process') const http = require('http') const semver = require('semver') @@ -38,6 +39,7 @@ let apmServer let serverUrl let TEST_REQUESTS = [ + // Redirects. { testName: 'trailing slash redirect', req: { method: 'GET', path: '/a-page/' }, @@ -45,12 +47,12 @@ let TEST_REQUESTS = [ statusCode: 308, headers: { location: '/a-page' } }, - expectedApmEvents: [ - { - 'transaction.name': 'Next.js Redirect route /:path+/', - 'transaction.context.response.status_code': 308 - } - ] + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1) + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'Next.js Redirect route /:path+/', 'transaction.name') + t.equal(trans.context.response.status_code, 308, 'transaction.context.response.status_code') + } }, { testName: 'configured (in next.config.js) redirect', @@ -59,12 +61,12 @@ let TEST_REQUESTS = [ statusCode: 307, headers: { location: '/a-page' } }, - expectedApmEvents: [ - { - 'transaction.name': 'Next.js Redirect route /redirect-to-a-page', - 'transaction.context.response.status_code': 307 - } - ] + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1) + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'Next.js Redirect route /redirect-to-a-page', 'transaction.name') + t.equal(trans.context.response.status_code, 307, 'transaction.context.response.status_code') + } }, // Rewrites are configured in "next.config.js". @@ -77,12 +79,12 @@ let TEST_REQUESTS = [ // This shows that we got the content from "pages/a-page.js". body: /This is APage/ }, - expectedApmEvents: [ - { - 'transaction.name': 'Next.js Rewrite route /rewrite-to-a-page -> /a-page', - 'transaction.context.response.status_code': 200 - } - ] + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1) + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'Next.js Rewrite route /rewrite-to-a-page -> /a-page', 'transaction.name') + t.equal(trans.context.response.status_code, 200, 'transaction.context.response.status_code') + } }, { testName: 'rewrite to a dynamic page', @@ -93,12 +95,12 @@ let TEST_REQUESTS = [ headers: { 'content-type': /text\/html/ }, body: /This is ADynamicPage/ }, - expectedApmEvents: [ - { - 'transaction.name': 'Next.js Rewrite route /rewrite-to-a-dynamic-page -> /a-dynamic-page/3.14', - 'transaction.context.response.status_code': 200 - } - ] + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1) + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'Next.js Rewrite route /rewrite-to-a-dynamic-page -> /a-dynamic-page/3.14', 'transaction.name') + t.equal(trans.context.response.status_code, 200, 'transaction.context.response.status_code') + } }, { testName: 'rewrite to a /public/... folder file', @@ -107,12 +109,12 @@ let TEST_REQUESTS = [ statusCode: 200, headers: { 'content-type': 'image/x-icon' } }, - expectedApmEvents: [ - { - 'transaction.name': 'Next.js Rewrite route /rewrite-to-a-public-file -> /favicon.ico', - 'transaction.context.response.status_code': 200 - } - ] + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1) + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'Next.js Rewrite route /rewrite-to-a-public-file -> /favicon.ico', 'transaction.name') + t.equal(trans.context.response.status_code, 200, 'transaction.context.response.status_code') + } }, { testName: 'rewrite to a 404', @@ -120,29 +122,41 @@ let TEST_REQUESTS = [ expectedRes: { statusCode: 404 }, - expectedApmEvents: [ - { - 'transaction.name': 'Next.js Rewrite route /rewrite-to-a-404 -> /no-such-page', - 'transaction.context.response.status_code': 404 - } - ] + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1) + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'Next.js Rewrite route /rewrite-to-a-404 -> /no-such-page', 'transaction.name') + t.equal(trans.context.response.status_code, 404, 'transaction.context.response.status_code') + } }, { testName: 'rewrite to a external site', req: { method: 'GET', path: '/rewrite-external/foo' }, expectedRes: { // This is a 500 because the configured `old.example.com` doesn't resolve. - // Currently we don't capture an error for that, which is a limitation. statusCode: 500 }, - expectedApmEvents: [ - { - 'transaction.name': 'Next.js Rewrite route /rewrite-external/:path* -> https://old.example.com/:path*', - 'transaction.context.response.status_code': 500 + checkApmEvents: (t, apmEventsForReq) => { + t.ok(apmEventsForReq.length === 1 || apmEventsForReq.length === 2, 'expected number of APM events') + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'Next.js Rewrite route /rewrite-external/:path* -> https://old.example.com/:path*', 'transaction.name') + t.equal(trans.context.response.status_code, 500, 'transaction.context.response.status_code') + // Limitation: Currently the instrumentation only captures an error with + // the DevServer, because Next.js special cases dev-mode and calls + // `renderErrorToResponse`. To capture the error with NextNodeServer we + // would need to shim `Server.run()` in base-server.js. + if (apmEventsForReq.length === 2) { + const error = apmEventsForReq[1].error + t.equal(trans.trace_id, error.trace_id, 'transaction and error are in same trace') + t.equal(error.parent_id, trans.id, 'error is a child of the transaction') + t.equal(error.transaction.type, 'request', 'error.transaction.type') + t.equal(error.transaction.name, trans.name, 'error.transaction.name') + t.equal(error.exception.message, 'getaddrinfo ENOTFOUND old.example.com', 'error.exception.message') } - ] + } }, + // The different kinds of pages. { testName: 'index page', req: { method: 'GET', path: '/' }, @@ -151,12 +165,12 @@ let TEST_REQUESTS = [ headers: { 'content-type': /text\/html/ }, body: /This is IndexPage/ }, - expectedApmEvents: [ - { - 'transaction.name': 'GET /', - 'transaction.context.response.status_code': 200 - } - ] + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1) + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'GET /', 'transaction.name') + t.equal(trans.context.response.status_code, 200, 'transaction.context.response.status_code') + } }, { testName: 'a page (Server-Side Generated, SSG)', @@ -166,12 +180,12 @@ let TEST_REQUESTS = [ headers: { 'content-type': /text\/html/ }, body: /This is APage/ }, - expectedApmEvents: [ - { - 'transaction.name': 'GET /a-page', - 'transaction.context.response.status_code': 200 - } - ] + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1) + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'GET /a-page', 'transaction.name') + t.equal(trans.context.response.status_code, 200, 'transaction.context.response.status_code') + } }, { testName: 'a dynamic page', @@ -181,12 +195,12 @@ let TEST_REQUESTS = [ headers: { 'content-type': /text\/html/ }, body: /This is ADynamicPage/ }, - expectedApmEvents: [ - { - 'transaction.name': 'GET /a-dynamic-page/[num]', - 'transaction.context.response.status_code': 200 - } - ] + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1) + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'GET /a-dynamic-page/[num]', 'transaction.name') + t.equal(trans.context.response.status_code, 200, 'transaction.context.response.status_code') + } }, { testName: 'a server-side rendered (SSR) page', @@ -196,27 +210,98 @@ let TEST_REQUESTS = [ headers: { 'content-type': /text\/html/ }, body: /This is AnSSRPage/ }, - expectedApmEvents: [ - { - 'transaction.name': 'GET /an-ssr-page', - 'transaction.context.response.status_code': 200 - } - ] + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1) + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'GET /an-ssr-page', 'transaction.name') + t.equal(trans.context.response.status_code, 200, 'transaction.context.response.status_code') + } + }, + + // API endpoint pages + { + testName: 'an API endpoint page', + req: { method: 'GET', path: '/api/an-api-endpoint' }, + expectedRes: { + statusCode: 200, + headers: { 'content-type': /application\/json/ }, + body: '{"ping":"pong"}' + }, + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1) + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'GET /api/an-api-endpoint', 'transaction.name') + t.equal(trans.context.response.status_code, 200, 'transaction.context.response.status_code') + } + }, + { + testName: 'a dynamic API endpoint page', + req: { method: 'GET', path: '/api/a-dynamic-api-endpoint/123' }, + expectedRes: { + statusCode: 200, + headers: { 'content-type': /application\/json/ }, + body: '{"num":"123","n":123,"double":246,"floor":123}' + }, + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1) + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'GET /api/a-dynamic-api-endpoint/[num]', 'transaction.name') + t.equal(trans.context.response.status_code, 200, 'transaction.context.response.status_code') + } + }, + + // Error capture cases + { + testName: 'an API endpoint that throws', + req: { method: 'GET', path: '/api/an-api-endpoint-that-throws' }, + expectedRes: { + statusCode: 500 + }, + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 2) + const trans = apmEventsForReq[0].transaction + const error = apmEventsForReq[1].error + t.equal(trans.name, 'GET /api/an-api-endpoint-that-throws', 'transaction.name') + t.equal(trans.context.response.status_code, 500, 'transaction.context.response.status_code') + t.ok(error, 'captured an APM error') + t.equal(trans.trace_id, error.trace_id, 'transaction and error are in same trace') + t.equal(error.parent_id, trans.id, 'error is a child of the transaction') + t.equal(error.transaction.type, 'request', 'error.transaction.type') + t.equal(error.transaction.name, trans.name, 'error.transaction.name') + t.equal(error.exception.message, 'An error thrown in anApiEndpointThatThrows handler', 'error.exception.message') + } + }, + { + testName: 'a throw in a page handler', + req: { method: 'GET', path: '/a-throw-in-page-handler' }, + expectedRes: { + statusCode: 500 + }, + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 2) + const trans = apmEventsForReq[0].transaction + const error = apmEventsForReq[1].error + t.equal(trans.name, 'GET /a-throw-in-page-handler', 'transaction.name') + t.equal(trans.context.response.status_code, 500, 'transaction.context.response.status_code') + t.ok(error, 'captured an APM error') + t.equal(trans.trace_id, error.trace_id, 'transaction and error are in same trace') + t.equal(error.parent_id, trans.id, 'error is a child of the transaction') + t.equal(error.transaction.type, 'request', 'error.transaction.type') + t.equal(error.transaction.name, trans.name, 'error.transaction.name') + t.equal(error.exception.message, 'throw in page handler', 'error.exception.message') + } } ] -process.env.XXX_TEST_FILTER = '' +// XXX +// process.env.XXX_TEST_FILTER = 'slash' if (process.env.XXX_TEST_FILTER) { TEST_REQUESTS = TEST_REQUESTS.filter(testReq => ~testReq.testName.indexOf(process.env.XXX_TEST_FILTER)) } // XXX HERE TODO: -// - api endpoints: -// curl -i localhost:3000/api/an-api-endpoint -// curl -i localhost:3000/api/a-dynamic-api-endpoint/123 -// // - error endpoints (grok the "bugs" in NOTES) // curl -i localhost:3000/api/an-api-endpoint-that-throws -// ... the other two /throw-in-... pages +// ... the other two /throw-in-... pages XXX one more! // ---- utility functions @@ -311,8 +396,10 @@ async function makeTestRequest (t, testReq) { if (testReq.expectedRes.body) { if (testReq.expectedRes.body instanceof RegExp) { t.ok(testReq.expectedRes.body.test(body), `body =~ ${testReq.expectedRes.body}`) + } else if (typeof testReq.expectedRes.body === 'string') { + t.equal(body.toString(), testReq.expectedRes.body, 'body') } else { - t.equal(body, testReq.expectedRes.body, 'body') + t.fail(`unsupported type for TEST_REQUESTS[].expectedRes.body: ${typeof testReq.expectedRes.body}`) } } resolve() @@ -341,27 +428,28 @@ function checkExpectedApmEvents (t, apmEvents) { t.equal(evt.transaction.outcome, 'success', 'transaction.outcome') // Expected APM events from all TEST_REQUESTS. + apmEvents.sort((a, b) => { + return (a.transaction || a.error).timestamp < (b.transaction || b.error).timestamp ? -1 : 1 + }) TEST_REQUESTS.forEach(testReq => { t.comment(`check APM events for "${testReq.testName}"`) - testReq.expectedApmEvents.forEach(expectedApmEvent => { - console.log('XXX expectedApmEvent: ', expectedApmEvent) - evt = apmEvents.shift() - console.log('XXX evt:'); console.dir(evt, { depth: 5 }) - for (const [k, v] of Object.entries(expectedApmEvent)) { - t.equal(dottedLookup(evt, k), v, `${k} === ${JSON.stringify(v)}`) - } - }) + // Collect all events until the next transaction. + assert(apmEvents.length > 0 && apmEvents[0].transaction, 'next APM event is a transaction') + const apmEventsForReq = [apmEvents.shift()] + while (apmEvents.length > 0 && !apmEvents[0].transaction) { + apmEventsForReq.push(apmEvents.shift()) + } + testReq.checkApmEvents(t, apmEventsForReq) }) - // console.log('XXX remaining apmEvents:', apmEvents) - t.equal(apmEvents.length, 0, 'no additional APM server events: ' + JSON.stringify(apmEvents)) + t.equal(apmEvents.length, 0, 'no additional unexpected APM server events: ' + JSON.stringify(apmEvents)) } // ---- tests -const SKIP_NPM_CI_FOR_DEV = process.env.USER === 'trentm' // XXX +const SKIP_NPM_CI_FOR_DEV = false // process.env.USER === 'trentm' // XXX if (!SKIP_NPM_CI_FOR_DEV) { - tape.test('setup: npm ci', t => { + tape.test(`setup: npm ci (in ${__dirname})`, t => { const startTime = Date.now() exec( 'npm ci', @@ -493,7 +581,7 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { }) // Test the Next "dev" server. I.e. `npm run dev`. -tape.test('-- dev server tests --', suite => { +tape.test('-- dev server tests --', { skip: false /* XXX */ }, suite => { let nextServerProc suite.test('setup: start Next.js dev server (npm run dev)', t => { diff --git a/test/nextjs/a-nextjs-app/pages/a-throw-in-page-handler.js b/test/nextjs/a-nextjs-app/pages/a-throw-in-page-handler.js index 1b8b362729c..cf51456cf73 100644 --- a/test/nextjs/a-nextjs-app/pages/a-throw-in-page-handler.js +++ b/test/nextjs/a-nextjs-app/pages/a-throw-in-page-handler.js @@ -14,6 +14,11 @@ export async function getServerSideProps () { } function AThrowInPageHandler ({ currTime }) { + // If this is called from a browser-side click of a , e.g. as on the + // index.js page, then this function is executed client-side. If called + // via separately visiting http://localhost:3000/a-throw-in-page-handler + // or via `curl -i ...`, then this is executed server-side. Only in the + // latter case will the Node.js APM agent capture an error, of course. throw new Error('throw in page handler') } From 4c739fa526626b97c09d1fcfccec2c7bcf1d4f86 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 12 Oct 2022 14:20:59 -0700 Subject: [PATCH 12/66] use relative require for elastic-apm-node rather than a relative dep; this to try to avoid 'npm ci' error in ci --- test/nextjs/a-nextjs-app/apmsetup.js | 2 +- test/nextjs/a-nextjs-app/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/nextjs/a-nextjs-app/apmsetup.js b/test/nextjs/a-nextjs-app/apmsetup.js index e99e1a72726..eb8c5f9c35b 100644 --- a/test/nextjs/a-nextjs-app/apmsetup.js +++ b/test/nextjs/a-nextjs-app/apmsetup.js @@ -4,7 +4,7 @@ * compliance with the BSD 2-Clause License. */ -const apm = require('elastic-apm-node').start({ +const apm = require('../../../').start({ // elastic-apm-node // XXX most of these for dev/debugging // apmServerVersion: '8.4.1', cloudProvider: 'none', diff --git a/test/nextjs/a-nextjs-app/package.json b/test/nextjs/a-nextjs-app/package.json index b2713ad87ad..bd8b25b61fd 100644 --- a/test/nextjs/a-nextjs-app/package.json +++ b/test/nextjs/a-nextjs-app/package.json @@ -2,6 +2,7 @@ "name": "a-nextjs-app", "version": "1.0.0", "description": "a Next.js-usage app to test elastic-apm-node instrumentation", + "private": true, "scripts": { "dev": "NODE_OPTIONS='-r ./apmsetup.js' next dev", "build": "next build", @@ -10,7 +11,6 @@ "keywords": [], "license": "BSD-2-Clause", "dependencies": { - "elastic-apm-node": "../../..", "next": "^12.3.1", "react": "^18.2.0", "react-dom": "^18.2.0", From 352fcb62c76a5b6fcdc69300449e159aa3c5add4 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 12 Oct 2022 14:21:57 -0700 Subject: [PATCH 13/66] fix make check --- .../next/dist/server/dev/next-dev-server.js | 23 ------------------- test/nextjs/a-nextjs-app/next.test.js | 1 - 2 files changed, 24 deletions(-) diff --git a/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js index 46b49f5f41a..8ab542f370a 100644 --- a/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js +++ b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js @@ -196,27 +196,4 @@ module.exports = function (mod, agent, { version, enabled }) { return promise } } - - function wrapRenderErrorToResponse (orig) { - return function wrappedRenderErrorToResponse (ctx, err) { - console.log('XXX next-dev-server.js wrappedRenderErrorToResponse: start') - if (this.constructor !== DevServer) { - console.log('XXX wrappedRenderErrorToResponse -> ctor != DevServer abort') - return orig.apply(this, arguments) - } - console.log('XXX wrappedRenderErrorToResponse(ctx, err=%s)', err && err.message) - const trans = ins.currTransaction() - if (trans) { - // Signal to subsequent instrumentation for this transaction that - // Next.js is now doing error handling for this request. - // XXX could try setting `kSetTransNameFn` to noop instead. Then only need the one symbol. - trans[kInErrorHandling] = true - } - // Next.js uses `err=null` to handle a 404. - if (err) { - agent.captureError(err) - } - return orig.apply(this, arguments) - } - } } diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index dfb1cb3acfd..4d961a24c2e 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -28,7 +28,6 @@ const semver = require('semver') const tape = require('tape') const { MockAPMServer } = require('../../_mock_apm_server') -const { dottedLookup } = require('../../_utils') if (semver.lt(process.version, '12.22.0')) { console.log(`# SKIP next does not support node ${process.version}`) From c25c3fe7014d8b5396118b49fefbdb73a248d91d Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 12 Oct 2022 14:23:40 -0700 Subject: [PATCH 14/66] update package-lock for the recently dropped relative agent dep --- test/nextjs/a-nextjs-app/package-lock.json | 307 +++------------------ 1 file changed, 33 insertions(+), 274 deletions(-) diff --git a/test/nextjs/a-nextjs-app/package-lock.json b/test/nextjs/a-nextjs-app/package-lock.json index fb5bc087891..e3b422a4138 100644 --- a/test/nextjs/a-nextjs-app/package-lock.json +++ b/test/nextjs/a-nextjs-app/package-lock.json @@ -9,136 +9,12 @@ "version": "1.0.0", "license": "BSD-2-Clause", "dependencies": { - "elastic-apm-node": "../../..", "next": "^12.3.1", "react": "^18.2.0", "react-dom": "^18.2.0", "sharp": "^0.31.0" } }, - "../../..": { - "name": "elastic-apm-node", - "version": "3.38.0", - "license": "BSD-2-Clause", - "dependencies": { - "@elastic/ecs-pino-format": "^1.2.0", - "@opentelemetry/api": "^1.1.0", - "after-all-results": "^2.0.0", - "async-cache": "^1.1.0", - "async-value-promise": "^1.1.1", - "basic-auth": "^2.0.1", - "cookie": "^0.5.0", - "core-util-is": "^1.0.2", - "elastic-apm-http-client": "11.0.1", - "end-of-stream": "^1.4.4", - "error-callsites": "^2.0.4", - "error-stack-parser": "^2.0.6", - "escape-string-regexp": "^4.0.0", - "fast-safe-stringify": "^2.0.7", - "http-headers": "^3.0.2", - "is-native": "^1.0.1", - "lru-cache": "^6.0.0", - "measured-reporting": "^1.51.1", - "monitor-event-loop-delay": "^1.0.0", - "object-filter-sequence": "^1.0.0", - "object-identity-map": "^1.0.2", - "original-url": "^1.2.3", - "pino": "^6.11.2", - "relative-microtime": "^2.0.0", - "require-in-the-middle": "^5.2.0", - "semver": "^6.3.0", - "set-cookie-serde": "^1.0.0", - "shallow-clone-shim": "^2.0.0", - "source-map": "^0.8.0-beta.0", - "sql-summary": "^1.0.1", - "traverse": "^0.6.6", - "unicode-byte-truncate": "^1.0.0" - }, - "devDependencies": { - "@babel/cli": "^7.8.4", - "@babel/core": "^7.8.4", - "@babel/preset-env": "^7.8.4", - "@elastic/elasticsearch": "^8.2.1", - "@elastic/elasticsearch-canary": "^8.2.0-canary.2", - "@fastify/formbody": "^7.0.1", - "@hapi/hapi": "^20.1.2", - "@koa/router": "^12.0.0", - "@types/node": "^18.0.1", - "@types/pino": "^6.3.8", - "ajv": "^7.2.4", - "apollo-server-core": "^3.0.0", - "apollo-server-express": "^3.0.0", - "aws-sdk": "^2.622.0", - "backport": "^5.1.2", - "benchmark": "^2.1.4", - "bluebird": "^3.7.2", - "body-parser": "^1.19.0", - "cassandra-driver": "^4.4.0", - "clone": "^2.0.0", - "columnify": "^1.5.4", - "connect": "^3.7.0", - "container-info": "^1.0.1", - "dashdash": "^2.0.0", - "dependency-check": "^4.1.0", - "diagnostics_channel": "^1.1.0", - "elasticsearch": "^16.7.3", - "eslint": "^6.8.0", - "eslint-config-standard": "^14.1.1", - "eslint-plugin-import": "^2.25.4", - "eslint-plugin-license-header": "^0.6.0", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.3.1", - "eslint-plugin-standard": "^4.1.0", - "express": "^4.17.1", - "express-graphql": "^0.12.0", - "express-queue": "^0.0.13", - "fastify": "^4.0.2", - "finalhandler": "^1.1.2", - "generic-pool": "^3.7.1", - "get-port": "^5.1.1", - "got": "^11.8.5", - "graphql": "^15.8.0", - "handlebars": "^4.7.3", - "https-pem": "^3.0.0", - "ioredis": "^5.1.0", - "knex": "^0.95.15", - "koa": "^2.11.0", - "koa-bodyparser": "^4.3.0", - "koa-router": "^12.0.0", - "lambda-local": "^2.0.2", - "memcached": "^2.2.2", - "mimic-response": "^2.1.0", - "mkdirp": "^0.5.1", - "module-details-from-path": "^1.0.3", - "mongodb": "^4.2.1", - "mongodb-core": "^3.2.7", - "mysql": "^2.18.1", - "mysql2": "^2.1.0", - "ndjson": "^1.5.0", - "numeral": "^2.0.6", - "nyc": "^15.0.0", - "once": "^1.4.0", - "pg": "^8.7.1", - "pug": "^3.0.1", - "redis": "^3.1.2", - "request": "^2.88.2", - "restify": "^8.5.1", - "rimraf": "^3.0.2", - "tap-junit": "^5.0.1", - "tape": "^5.0.0", - "tedious": "^15.1.0", - "test-all-versions": "^4.1.1", - "thunky": "^1.1.0", - "typescript": "^4.7.4", - "undici": "^5.8.0", - "vasync": "^2.2.0", - "wait-on": "^6.0.1", - "ws": "^7.2.1" - }, - "engines": { - "node": "^8.6.0 || 10 || 12 || 14 || 16 || 17 || 18 || 19" - } - }, "node_modules/@next/env": { "version": "12.3.1", "resolved": "https://registry.npmjs.org/@next/env/-/env-12.3.1.tgz", @@ -400,9 +276,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001410", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001410.tgz", - "integrity": "sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==", + "version": "1.0.30001418", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz", + "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==", "funding": [ { "type": "opencollective", @@ -486,10 +362,6 @@ "node": ">=8" } }, - "node_modules/elastic-apm-node": { - "resolved": "../../..", - "link": true - }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -589,9 +461,12 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/mkdirp-classic": { "version": "0.5.3", @@ -667,9 +542,9 @@ } }, "node_modules/node-abi": { - "version": "3.25.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.25.0.tgz", - "integrity": "sha512-p+0xx5ruIQ+8X57CRIMxbTZRT7tU0Tjn2C/aAK68AEMrbGsCo6IjnDdPNhEyyjWCT4bRtzomXchYd3sSgk3BJQ==", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.26.0.tgz", + "integrity": "sha512-jRVtMFTChbi2i/jqo/i2iP9634KMe+7K1v35mIdj3Mn59i5q27ZYhn+sW6npISM/PQg7HrP2kwtRBMmh5Uvzdg==", "dependencies": { "semver": "^7.3.5" }, @@ -830,9 +705,9 @@ } }, "node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -844,9 +719,9 @@ } }, "node_modules/sharp": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.0.tgz", - "integrity": "sha512-ft96f8WzGxavg0rkLpMw90MTPMUZDyf0tHjPPh8Ob59xt6KzX8EqtotcqZGUm7kwqpX2pmYiyYX2LL0IZ/FDEw==", + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.1.tgz", + "integrity": "sha512-GR8M1wBwOiFKLkm9JPun27OQnNRZdHfSf9VwcdZX6UrRmM1/XnOrLFTF0GAil+y/YK4E6qcM/ugxs80QirsHxg==", "hasInstallScript": true, "dependencies": { "color": "^4.2.3", @@ -1142,9 +1017,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001410", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001410.tgz", - "integrity": "sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==" + "version": "1.0.30001418", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz", + "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==" }, "chownr": { "version": "1.1.4", @@ -1200,122 +1075,6 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" }, - "elastic-apm-node": { - "version": "file:../../..", - "requires": { - "@babel/cli": "^7.8.4", - "@babel/core": "^7.8.4", - "@babel/preset-env": "^7.8.4", - "@elastic/ecs-pino-format": "^1.2.0", - "@elastic/elasticsearch": "^8.2.1", - "@elastic/elasticsearch-canary": "^8.2.0-canary.2", - "@fastify/formbody": "^7.0.1", - "@hapi/hapi": "^20.1.2", - "@koa/router": "^12.0.0", - "@opentelemetry/api": "^1.1.0", - "@types/node": "^18.0.1", - "@types/pino": "^6.3.8", - "after-all-results": "^2.0.0", - "ajv": "^7.2.4", - "apollo-server-core": "^3.0.0", - "apollo-server-express": "^3.0.0", - "async-cache": "^1.1.0", - "async-value-promise": "^1.1.1", - "aws-sdk": "^2.622.0", - "backport": "^5.1.2", - "basic-auth": "^2.0.1", - "benchmark": "^2.1.4", - "bluebird": "^3.7.2", - "body-parser": "^1.19.0", - "cassandra-driver": "^4.4.0", - "clone": "^2.0.0", - "columnify": "^1.5.4", - "connect": "^3.7.0", - "container-info": "^1.0.1", - "cookie": "^0.5.0", - "core-util-is": "^1.0.2", - "dashdash": "^2.0.0", - "dependency-check": "^4.1.0", - "diagnostics_channel": "^1.1.0", - "elastic-apm-http-client": "11.0.1", - "elasticsearch": "^16.7.3", - "end-of-stream": "^1.4.4", - "error-callsites": "^2.0.4", - "error-stack-parser": "^2.0.6", - "escape-string-regexp": "^4.0.0", - "eslint": "^6.8.0", - "eslint-config-standard": "^14.1.1", - "eslint-plugin-import": "^2.25.4", - "eslint-plugin-license-header": "^0.6.0", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.3.1", - "eslint-plugin-standard": "^4.1.0", - "express": "^4.17.1", - "express-graphql": "^0.12.0", - "express-queue": "^0.0.13", - "fast-safe-stringify": "^2.0.7", - "fastify": "^4.0.2", - "finalhandler": "^1.1.2", - "generic-pool": "^3.7.1", - "get-port": "^5.1.1", - "got": "^11.8.5", - "graphql": "^15.8.0", - "handlebars": "^4.7.3", - "http-headers": "^3.0.2", - "https-pem": "^3.0.0", - "ioredis": "^5.1.0", - "is-native": "^1.0.1", - "knex": "^0.95.15", - "koa": "^2.11.0", - "koa-bodyparser": "^4.3.0", - "koa-router": "^12.0.0", - "lambda-local": "^2.0.2", - "lru-cache": "^6.0.0", - "measured-reporting": "^1.51.1", - "memcached": "^2.2.2", - "mimic-response": "^2.1.0", - "mkdirp": "^0.5.1", - "module-details-from-path": "^1.0.3", - "mongodb": "^4.2.1", - "mongodb-core": "^3.2.7", - "monitor-event-loop-delay": "^1.0.0", - "mysql": "^2.18.1", - "mysql2": "^2.1.0", - "ndjson": "^1.5.0", - "numeral": "^2.0.6", - "nyc": "^15.0.0", - "object-filter-sequence": "^1.0.0", - "object-identity-map": "^1.0.2", - "once": "^1.4.0", - "original-url": "^1.2.3", - "pg": "^8.7.1", - "pino": "^6.11.2", - "pug": "^3.0.1", - "redis": "^3.1.2", - "relative-microtime": "^2.0.0", - "request": "^2.88.2", - "require-in-the-middle": "^5.2.0", - "restify": "^8.5.1", - "rimraf": "^3.0.2", - "semver": "^6.3.0", - "set-cookie-serde": "^1.0.0", - "shallow-clone-shim": "^2.0.0", - "source-map": "^0.8.0-beta.0", - "sql-summary": "^1.0.1", - "tap-junit": "^5.0.1", - "tape": "^5.0.0", - "tedious": "^15.1.0", - "test-all-versions": "^4.1.1", - "thunky": "^1.1.0", - "traverse": "^0.6.6", - "typescript": "^4.7.4", - "undici": "^5.8.0", - "unicode-byte-truncate": "^1.0.0", - "vasync": "^2.2.0", - "wait-on": "^6.0.1", - "ws": "^7.2.1" - } - }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1386,9 +1145,9 @@ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" }, "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" }, "mkdirp-classic": { "version": "0.5.3", @@ -1432,9 +1191,9 @@ } }, "node-abi": { - "version": "3.25.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.25.0.tgz", - "integrity": "sha512-p+0xx5ruIQ+8X57CRIMxbTZRT7tU0Tjn2C/aAK68AEMrbGsCo6IjnDdPNhEyyjWCT4bRtzomXchYd3sSgk3BJQ==", + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.26.0.tgz", + "integrity": "sha512-jRVtMFTChbi2i/jqo/i2iP9634KMe+7K1v35mIdj3Mn59i5q27ZYhn+sW6npISM/PQg7HrP2kwtRBMmh5Uvzdg==", "requires": { "semver": "^7.3.5" } @@ -1547,17 +1306,17 @@ } }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "requires": { "lru-cache": "^6.0.0" } }, "sharp": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.0.tgz", - "integrity": "sha512-ft96f8WzGxavg0rkLpMw90MTPMUZDyf0tHjPPh8Ob59xt6KzX8EqtotcqZGUm7kwqpX2pmYiyYX2LL0IZ/FDEw==", + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.1.tgz", + "integrity": "sha512-GR8M1wBwOiFKLkm9JPun27OQnNRZdHfSf9VwcdZX6UrRmM1/XnOrLFTF0GAil+y/YK4E6qcM/ugxs80QirsHxg==", "requires": { "color": "^4.2.3", "detect-libc": "^2.0.1", From e51919ff50012498a293949a7a67e87801327485 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 12 Oct 2022 14:53:19 -0700 Subject: [PATCH 15/66] drop the slow to install/build 'sharp' suggested for Next.js prod (https://nextjs.org/docs/messages/sharp-missing-in-production) to try to fix CI runs; also reduce the test matrix while still in draft --- .ci/.jenkins_nodejs.yml | 25 +- .github/workflows/test.yml | 21 +- test/nextjs/a-nextjs-app/package-lock.json | 815 +-------------------- test/nextjs/a-nextjs-app/package.json | 3 +- 4 files changed, 26 insertions(+), 838 deletions(-) diff --git a/.ci/.jenkins_nodejs.yml b/.ci/.jenkins_nodejs.yml index 8e585cb3ef5..5431028b1bd 100644 --- a/.ci/.jenkins_nodejs.yml +++ b/.ci/.jenkins_nodejs.yml @@ -1,14 +1,15 @@ NODEJS_VERSION: - # - "19" # Uncomment this when there are v19.x releases at https://nodejs.org/download/release/ - - "18" - # - "18.0" # Uncomment this when "18" is 18.1 or greater. + # XXX just v16 for now while we get tests basically running + # # - "19" # Uncomment this when there are v19.x releases at https://nodejs.org/download/release/ + # - "18" + # # - "18.0" # Uncomment this when "18" is 18.1 or greater. - "16" - - "16.0" - - "14" - - "14.0" - - "12" - - "12.0" - - "10" - - "10.0" - - "8" - - "8.6" + # - "16.0" + # - "14" + # - "14.0" + # - "12" + # - "12.0" + # - "10" + # - "10.0" + # - "8" + # - "8.6" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c3e2a2894be..506ce033e84 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -120,17 +120,18 @@ jobs: strategy: matrix: node: - - '18' + # XXX just v16 for now while we get tests basically running + # - '18' - '16' - - '16.0' - - '14' - - '14.0' - - '12' - - '12.0' - - '10' - - '10.0' - - '8' - - '8.6' + # - '16.0' + # - '14' + # - '14.0' + # - '12' + # - '12.0' + # - '10' + # - '10.0' + # - '8' + # - '8.6' runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/test/nextjs/a-nextjs-app/package-lock.json b/test/nextjs/a-nextjs-app/package-lock.json index e3b422a4138..6750bcb6bf7 100644 --- a/test/nextjs/a-nextjs-app/package-lock.json +++ b/test/nextjs/a-nextjs-app/package-lock.json @@ -11,8 +11,7 @@ "dependencies": { "next": "^12.3.1", "react": "^18.2.0", - "react-dom": "^18.2.0", - "sharp": "^0.31.0" + "react-dom": "^18.2.0" } }, "node_modules/@next/env": { @@ -223,58 +222,6 @@ "tslib": "^2.4.0" } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001418", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz", @@ -290,138 +237,6 @@ } ] }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -438,41 +253,6 @@ "loose-envify": "cli.js" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, "node_modules/nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", @@ -484,11 +264,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" - }, "node_modules/next": { "version": "12.3.1", "resolved": "https://registry.npmjs.org/next/-/next-12.3.1.tgz", @@ -541,30 +316,6 @@ } } }, - "node_modules/node-abi": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.26.0.tgz", - "integrity": "sha512-jRVtMFTChbi2i/jqo/i2iP9634KMe+7K1v35mIdj3Mn59i5q27ZYhn+sW6npISM/PQg7HrP2kwtRBMmh5Uvzdg==", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", - "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -593,54 +344,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -664,38 +367,6 @@ "react": "^18.2.0" } }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -704,93 +375,6 @@ "loose-envify": "^1.1.0" } }, - "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.1.tgz", - "integrity": "sha512-GR8M1wBwOiFKLkm9JPun27OQnNRZdHfSf9VwcdZX6UrRmM1/XnOrLFTF0GAil+y/YK4E6qcM/ugxs80QirsHxg==", - "hasInstallScript": true, - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.1", - "node-addon-api": "^5.0.0", - "prebuild-install": "^7.1.1", - "semver": "^7.3.7", - "simple-get": "^4.0.1", - "tar-fs": "^2.1.1", - "tunnel-agent": "^0.6.0" - }, - "engines": { - "node": ">=14.15.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -799,22 +383,6 @@ "node": ">=0.10.0" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/styled-jsx": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.7.tgz", @@ -834,48 +402,11 @@ } } }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -883,21 +414,6 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } }, "dependencies": { @@ -992,132 +508,11 @@ "tslib": "^2.4.0" } }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "caniuse-lite": { "version": "1.0.30001418", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz", "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==" }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "requires": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "requires": { - "mimic-response": "^3.1.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" - }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1131,39 +526,11 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" - }, - "minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" - }, - "mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, "nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" }, - "napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" - }, "next": { "version": "12.3.1", "resolved": "https://registry.npmjs.org/next/-/next-12.3.1.tgz", @@ -1190,27 +557,6 @@ "use-sync-external-store": "1.2.0" } }, - "node-abi": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.26.0.tgz", - "integrity": "sha512-jRVtMFTChbi2i/jqo/i2iP9634KMe+7K1v35mIdj3Mn59i5q27ZYhn+sW6npISM/PQg7HrP2kwtRBMmh5Uvzdg==", - "requires": { - "semver": "^7.3.5" - } - }, - "node-addon-api": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", - "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -1226,45 +572,6 @@ "source-map-js": "^1.0.2" } }, - "prebuild-install": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", - "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "requires": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, "react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -1282,21 +589,6 @@ "scheduler": "^0.23.0" } }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, "scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -1305,132 +597,27 @@ "loose-envify": "^1.1.0" } }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "requires": { - "lru-cache": "^6.0.0" - } - }, - "sharp": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.1.tgz", - "integrity": "sha512-GR8M1wBwOiFKLkm9JPun27OQnNRZdHfSf9VwcdZX6UrRmM1/XnOrLFTF0GAil+y/YK4E6qcM/ugxs80QirsHxg==", - "requires": { - "color": "^4.2.3", - "detect-libc": "^2.0.1", - "node-addon-api": "^5.0.0", - "prebuild-install": "^7.1.1", - "semver": "^7.3.7", - "simple-get": "^4.0.1", - "tar-fs": "^2.1.1", - "tunnel-agent": "^0.6.0" - } - }, - "simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" - }, - "simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "requires": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "requires": { - "is-arrayish": "^0.3.1" - } - }, "source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" - }, "styled-jsx": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.7.tgz", "integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==", "requires": {} }, - "tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, "tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", "requires": {} - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/test/nextjs/a-nextjs-app/package.json b/test/nextjs/a-nextjs-app/package.json index bd8b25b61fd..3ecfa7fafa9 100644 --- a/test/nextjs/a-nextjs-app/package.json +++ b/test/nextjs/a-nextjs-app/package.json @@ -13,7 +13,6 @@ "dependencies": { "next": "^12.3.1", "react": "^18.2.0", - "react-dom": "^18.2.0", - "sharp": "^0.31.0" + "react-dom": "^18.2.0" } } From 4ffd5ef93bff07a189fe231ffe67b72f86785283 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 12 Oct 2022 17:13:05 -0700 Subject: [PATCH 16/66] strip down tests that are run for quicker dev; solve the test hang Test hang fix: spawn `node .../.bin/next ...` directly rather than via npm, else the proc.kill() sends a signal to a defunct process and does nothing). --- .ci/scripts/test.sh | 3 ++ lib/agent.js | 4 ++ test/nextjs/a-nextjs-app/apmsetup.js | 3 ++ test/nextjs/a-nextjs-app/next.test.js | 69 ++++++++++++++++----------- test/test.js | 3 ++ 5 files changed, 54 insertions(+), 28 deletions(-) diff --git a/.ci/scripts/test.sh b/.ci/scripts/test.sh index b22952cc405..8e98ba470bb 100755 --- a/.ci/scripts/test.sh +++ b/.ci/scripts/test.sh @@ -226,6 +226,9 @@ else DOCKER_COMPOSE_FILE=docker-compose-all.yml fi +# XXX no services +DOCKER_COMPOSE_FILE=docker-compose-node-test.yml #XXX + set +e NVM_NODEJS_ORG_MIRROR=${NVM_NODEJS_ORG_MIRROR} \ ELASTIC_APM_CONTEXT_MANAGER=${ELASTIC_APM_CONTEXT_MANAGER} \ diff --git a/lib/agent.js b/lib/agent.js index c9df001974c..2a15f5aebc9 100644 --- a/lib/agent.js +++ b/lib/agent.js @@ -653,6 +653,7 @@ Agent.prototype.handleUncaughtExceptions = function (cb) { // al) or when dropping the event (e.g. because of a filter): // inflightEvents.delete(id) Agent.prototype.flush = function (cb) { + console.log('XXX Agent.flush start') // This 1s timeout is a subjective balance between "long enough for spans // and errors to reasonably encode" and "short enough to not block data // being reported to APM server". @@ -688,6 +689,7 @@ Agent.prototype._flush = function (opts, cb) { // If there are no inflight events then avoid creating additional objects. if (this._inflightEvents.size === 0) { + console.log('XXX Agent._flush: no _inflightEvents events') this._transport.flush({ lambdaEnd }, boundCb) return } @@ -696,6 +698,7 @@ Agent.prototype._flush = function (opts, cb) { // callback when the current set of inflight events complete. const flushingInflightEvents = this._inflightEvents flushingInflightEvents.setDrainHandler((drainErr) => { + console.log('XXX Agent._flush: flushingInflightEvents drain handler, drainErr=', drainErr) // The only possible drainErr is a timeout. This is best effort, so we only // log this and move on. this.logger.debug({ @@ -706,6 +709,7 @@ Agent.prototype._flush = function (opts, cb) { // Then, flush the intake request to APM server. this._transport.flush({ lambdaEnd }, boundCb) }, opts.inflightTimeoutMs) + console.log('XXX Agent._flush: set drain handler') // Create a new empty set to collect subsequent inflight events. this._inflightEvents = new InflightEventSet() diff --git a/test/nextjs/a-nextjs-app/apmsetup.js b/test/nextjs/a-nextjs-app/apmsetup.js index eb8c5f9c35b..ceb9a18c92c 100644 --- a/test/nextjs/a-nextjs-app/apmsetup.js +++ b/test/nextjs/a-nextjs-app/apmsetup.js @@ -7,6 +7,7 @@ const apm = require('../../../').start({ // elastic-apm-node // XXX most of these for dev/debugging // apmServerVersion: '8.4.1', + stackTraceLimit: 10, cloudProvider: 'none', centralConfig: false, metricsInterval: '0s', @@ -25,7 +26,9 @@ const apm = require('../../../').start({ // elastic-apm-node // https://github.com/vercel/next.js/discussions/19693 process.env.NEXT_MANUAL_SIG_HANDLE = 1 function flushApmAndExit () { + console.log('XXX apmsetup: received signal, calling flush') apm.flush(() => { + console.log('XXX flush cb, calling process.exit(0)') process.exit(0) }) } diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index 4d961a24c2e..9a4fa001a73 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -12,13 +12,13 @@ // - Start a MockAPMServer to capture intake requests. // - `npm ci` to build the "a-nextjs-app" project. // - Test instrumentation when using the Next.js production server. -// - `npm run build && npm run start` configured to send to our MockAPMServer. +// - `next build && next start` configured to send to our MockAPMServer. // - Make every request in `TEST_REQUESTS` to the Next.js app. // - Stop the Next.js app ("apmsetup.js" will flush the APM agent on SIGTERM). // - Check all the received APM trace data matches the expected values in // `TEST_REQUESTS`. // - Test instrumentation when using the Next.js dev server. -// - `npm run dev` +// - `next dev` // - (Same as above.) const assert = require('assert') @@ -377,9 +377,9 @@ async function makeTestRequest (t, testReq) { res.on('data', chunk => { chunks.push(chunk) }) res.on('end', () => { const body = Buffer.concat(chunks) - console.log('XXX res:', res.statusCode, res.headers, - res.headers['content-type'] && ~res.headers['content-type'].indexOf('text') && body.toString(), - '\n--') + // console.log('XXX res:', res.statusCode, res.headers, + // res.headers['content-type'] && ~res.headers['content-type'].indexOf('text') && body.toString(), + // '\n--') if (testReq.expectedRes.statusCode) { t.equal(res.statusCode, testReq.expectedRes.statusCode, `res.statusCode === ${testReq.expectedRes.statusCode}`) } @@ -410,6 +410,10 @@ async function makeTestRequest (t, testReq) { }) } +function getEventField (e, fieldName) { + return (e.transaction || e.error || e.span)[fieldName] +} + /** * Assert that the given `apmEvents` (events that the mock APM server received) * match all the expected APM events in `TEST_REQUESTS`. @@ -427,17 +431,19 @@ function checkExpectedApmEvents (t, apmEvents) { t.equal(evt.transaction.outcome, 'success', 'transaction.outcome') // Expected APM events from all TEST_REQUESTS. - apmEvents.sort((a, b) => { - return (a.transaction || a.error).timestamp < (b.transaction || b.error).timestamp ? -1 : 1 - }) + apmEvents = apmEvents + .filter(e => !e.metadata) + .sort((a, b) => { + return getEventField(a, 'timestamp') < getEventField(b, 'timestamp') ? -1 : 1 + }) TEST_REQUESTS.forEach(testReq => { t.comment(`check APM events for "${testReq.testName}"`) - // Collect all events until the next transaction. + // Collect all events for this transaction's trace_id, and pass that to + // the `checkApmEvents` function for this request. assert(apmEvents.length > 0 && apmEvents[0].transaction, 'next APM event is a transaction') - const apmEventsForReq = [apmEvents.shift()] - while (apmEvents.length > 0 && !apmEvents[0].transaction) { - apmEventsForReq.push(apmEvents.shift()) - } + const traceId = apmEvents[0].transaction.trace_id + const apmEventsForReq = apmEvents.filter(e => getEventField(e, 'trace_id') === traceId) + apmEvents = apmEvents.filter(e => getEventField(e, 'trace_id') !== traceId) testReq.checkApmEvents(t, apmEventsForReq) }) @@ -446,7 +452,7 @@ function checkExpectedApmEvents (t, apmEvents) { // ---- tests -const SKIP_NPM_CI_FOR_DEV = false // process.env.USER === 'trentm' // XXX +const SKIP_NPM_CI_FOR_DEV = process.env.USER === 'trentm' // XXX if (!SKIP_NPM_CI_FOR_DEV) { tape.test(`setup: npm ci (in ${__dirname})`, t => { const startTime = Date.now() @@ -475,7 +481,7 @@ tape.test('setup: mock APM server', t => { }) }) -// Test the Next "prod" server. I.e. `npm run build && npm run start`. +// Test the Next "prod" server. I.e. `next build && next start`. tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { let nextServerProc @@ -496,19 +502,21 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { ) }) - suite.test('setup: start Next.js prod server (npm run start)', t => { + suite.test('setup: start Next.js prod server (next start)', t => { + // XXX warning using `npm run start` directly with Docker. nextServerProc = spawn( - 'npm', - ['run', 'start'], + process.execPath, + ['./node_modules/.bin/next', 'start'], { cwd: __dirname, env: Object.assign({}, process.env, { + NODE_OPTIONS: '-r ./apmsetup.js', ELASTIC_APM_SERVER_URL: serverUrl }) } ) nextServerProc.on('error', err => { - t.error(err, 'no error from "npm run start"') + t.error(err, 'no error from "next start"') }) nextServerProc.stdout.on('data', data => { t.comment(`[Next.js server stdout] ${data}`) @@ -517,10 +525,10 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { t.comment(`[Next.js server stderr] ${data}`) }) - // Allow some time for an early fail of `npm run start`, e.g. if there is + // Allow some time for an early fail of `next start`, e.g. if there is // already a user of port 3000... const onEarlyClose = code => { - t.fail(`"npm run start" failed early: code=${code}`) + t.fail(`"next start" failed early: code=${code}`) nextServerProc = null clearTimeout(earlyCloseTimer) t.end() @@ -573,29 +581,34 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { checkExpectedApmEvents(t, apmServer.events) t.end() }) + console.log('XXX before: pid %s is killed? %s', nextServerProc.pid, nextServerProc.killed) nextServerProc.kill('SIGTERM') + console.log('XXX sent SIGTERM to pid', nextServerProc.pid) + console.log('XXX sync after: pid %s is killed? %s', nextServerProc.pid, nextServerProc.killed) }) suite.end() }) -// Test the Next "dev" server. I.e. `npm run dev`. +// Test the Next "dev" server. I.e. `next dev`. tape.test('-- dev server tests --', { skip: false /* XXX */ }, suite => { let nextServerProc - suite.test('setup: start Next.js dev server (npm run dev)', t => { + suite.test('setup: start Next.js dev server (next dev)', t => { + // XXX warning using `npm run dev` directly with Docker. nextServerProc = spawn( - 'npm', - ['run', 'dev'], + process.execPath, + ['./node_modules/.bin/next', 'dev'], { cwd: __dirname, env: Object.assign({}, process.env, { + NODE_OPTIONS: '-r ./apmsetup.js', ELASTIC_APM_SERVER_URL: serverUrl }) } ) nextServerProc.on('error', err => { - t.error(err, 'no error from "npm run dev"') + t.error(err, 'no error from "next dev"') }) nextServerProc.stdout.on('data', data => { t.comment(`[Next.js server stdout] ${data}`) @@ -604,10 +617,10 @@ tape.test('-- dev server tests --', { skip: false /* XXX */ }, suite => { t.comment(`[Next.js server stderr] ${data}`) }) - // Allow some time for an early fail of `npm run dev`, e.g. if there is + // Allow some time for an early fail of `next dev`, e.g. if there is // already a user of port 3000... const onEarlyClose = code => { - t.fail(`"npm run dev" failed early: code=${code}`) + t.fail(`"next dev" failed early: code=${code}`) nextServerProc = null clearTimeout(earlyCloseTimer) t.end() diff --git a/test/test.js b/test/test.js index 2eece3f5ac5..510948515a1 100644 --- a/test/test.js +++ b/test/test.js @@ -181,6 +181,8 @@ if (opts.help) { var directories = getDirectoriesWithTests( './test', [], ['./test/start/env', './test/start/file'] ) +console.log('XXX directories: ', directories) +directories = ['test/nextjs/a-nextjs-app'] // XXX mapSeries(directories, readdir, function (err, directoryFiles) { if (err) throw err @@ -200,6 +202,7 @@ mapSeries(directories, readdir, function (err, directoryFiles) { outDir: opts.output_dir } ] + tests = [] // XXX directoryFiles.forEach(function (files, i) { var directory = directories[i] From b2f31b712611ab8f7fdb9ec566e5310c9d9d405a Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 12 Oct 2022 17:29:17 -0700 Subject: [PATCH 17/66] nope to Next.js instr with contextManager=patch config, skip those --- test/instrumentation/modules/pg/knex.test.js | 2 +- test/nextjs/a-nextjs-app/next.test.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/test/instrumentation/modules/pg/knex.test.js b/test/instrumentation/modules/pg/knex.test.js index 37db7104a17..0612c4864a2 100644 --- a/test/instrumentation/modules/pg/knex.test.js +++ b/test/instrumentation/modules/pg/knex.test.js @@ -30,7 +30,7 @@ if ((semver.gte(knexVersion, '0.18.0') && semver.lt(process.version, '8.6.0')) | // Instrumentation does not work with Knex >=0.95.0 and `contextManager=patch`. // The "patch" context manager is deprecated. if (semver.gte(knexVersion, '0.95.0') && agent._conf.contextManager === 'patch') { - console.log(`# SKIP knex@${knexVersion} and contextManager='patch' is not support`) + console.log(`# SKIP knex@${knexVersion} and contextManager='patch' is not supported`) process.exit() } diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index 9a4fa001a73..a9eb4d8d536 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -33,6 +33,10 @@ if (semver.lt(process.version, '12.22.0')) { console.log(`# SKIP next does not support node ${process.version}`) process.exit() } +if (process.env.ELASTIC_APM_CONTEXT_MANAGER === 'patch') { + console.log('# SKIP Next.js instrumentation does not work with contextManager="patch"') + process.exit() +} let apmServer let serverUrl From ade6c03e607d572028c8425d495c1dcc88519e24 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 12 Oct 2022 17:31:31 -0700 Subject: [PATCH 18/66] restore full testing --- .ci/.jenkins_nodejs.yml | 25 ++++++++++++------------- .ci/scripts/test.sh | 3 --- .github/workflows/test.yml | 21 ++++++++++----------- lib/agent.js | 4 ---- test/nextjs/a-nextjs-app/apmsetup.js | 2 -- test/test.js | 3 --- 6 files changed, 22 insertions(+), 36 deletions(-) diff --git a/.ci/.jenkins_nodejs.yml b/.ci/.jenkins_nodejs.yml index 5431028b1bd..8e585cb3ef5 100644 --- a/.ci/.jenkins_nodejs.yml +++ b/.ci/.jenkins_nodejs.yml @@ -1,15 +1,14 @@ NODEJS_VERSION: - # XXX just v16 for now while we get tests basically running - # # - "19" # Uncomment this when there are v19.x releases at https://nodejs.org/download/release/ - # - "18" - # # - "18.0" # Uncomment this when "18" is 18.1 or greater. + # - "19" # Uncomment this when there are v19.x releases at https://nodejs.org/download/release/ + - "18" + # - "18.0" # Uncomment this when "18" is 18.1 or greater. - "16" - # - "16.0" - # - "14" - # - "14.0" - # - "12" - # - "12.0" - # - "10" - # - "10.0" - # - "8" - # - "8.6" + - "16.0" + - "14" + - "14.0" + - "12" + - "12.0" + - "10" + - "10.0" + - "8" + - "8.6" diff --git a/.ci/scripts/test.sh b/.ci/scripts/test.sh index 8e98ba470bb..b22952cc405 100755 --- a/.ci/scripts/test.sh +++ b/.ci/scripts/test.sh @@ -226,9 +226,6 @@ else DOCKER_COMPOSE_FILE=docker-compose-all.yml fi -# XXX no services -DOCKER_COMPOSE_FILE=docker-compose-node-test.yml #XXX - set +e NVM_NODEJS_ORG_MIRROR=${NVM_NODEJS_ORG_MIRROR} \ ELASTIC_APM_CONTEXT_MANAGER=${ELASTIC_APM_CONTEXT_MANAGER} \ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 506ce033e84..c3e2a2894be 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -120,18 +120,17 @@ jobs: strategy: matrix: node: - # XXX just v16 for now while we get tests basically running - # - '18' + - '18' - '16' - # - '16.0' - # - '14' - # - '14.0' - # - '12' - # - '12.0' - # - '10' - # - '10.0' - # - '8' - # - '8.6' + - '16.0' + - '14' + - '14.0' + - '12' + - '12.0' + - '10' + - '10.0' + - '8' + - '8.6' runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/lib/agent.js b/lib/agent.js index 2a15f5aebc9..c9df001974c 100644 --- a/lib/agent.js +++ b/lib/agent.js @@ -653,7 +653,6 @@ Agent.prototype.handleUncaughtExceptions = function (cb) { // al) or when dropping the event (e.g. because of a filter): // inflightEvents.delete(id) Agent.prototype.flush = function (cb) { - console.log('XXX Agent.flush start') // This 1s timeout is a subjective balance between "long enough for spans // and errors to reasonably encode" and "short enough to not block data // being reported to APM server". @@ -689,7 +688,6 @@ Agent.prototype._flush = function (opts, cb) { // If there are no inflight events then avoid creating additional objects. if (this._inflightEvents.size === 0) { - console.log('XXX Agent._flush: no _inflightEvents events') this._transport.flush({ lambdaEnd }, boundCb) return } @@ -698,7 +696,6 @@ Agent.prototype._flush = function (opts, cb) { // callback when the current set of inflight events complete. const flushingInflightEvents = this._inflightEvents flushingInflightEvents.setDrainHandler((drainErr) => { - console.log('XXX Agent._flush: flushingInflightEvents drain handler, drainErr=', drainErr) // The only possible drainErr is a timeout. This is best effort, so we only // log this and move on. this.logger.debug({ @@ -709,7 +706,6 @@ Agent.prototype._flush = function (opts, cb) { // Then, flush the intake request to APM server. this._transport.flush({ lambdaEnd }, boundCb) }, opts.inflightTimeoutMs) - console.log('XXX Agent._flush: set drain handler') // Create a new empty set to collect subsequent inflight events. this._inflightEvents = new InflightEventSet() diff --git a/test/nextjs/a-nextjs-app/apmsetup.js b/test/nextjs/a-nextjs-app/apmsetup.js index ceb9a18c92c..62c5452d29b 100644 --- a/test/nextjs/a-nextjs-app/apmsetup.js +++ b/test/nextjs/a-nextjs-app/apmsetup.js @@ -26,9 +26,7 @@ const apm = require('../../../').start({ // elastic-apm-node // https://github.com/vercel/next.js/discussions/19693 process.env.NEXT_MANUAL_SIG_HANDLE = 1 function flushApmAndExit () { - console.log('XXX apmsetup: received signal, calling flush') apm.flush(() => { - console.log('XXX flush cb, calling process.exit(0)') process.exit(0) }) } diff --git a/test/test.js b/test/test.js index 510948515a1..2eece3f5ac5 100644 --- a/test/test.js +++ b/test/test.js @@ -181,8 +181,6 @@ if (opts.help) { var directories = getDirectoriesWithTests( './test', [], ['./test/start/env', './test/start/file'] ) -console.log('XXX directories: ', directories) -directories = ['test/nextjs/a-nextjs-app'] // XXX mapSeries(directories, readdir, function (err, directoryFiles) { if (err) throw err @@ -202,7 +200,6 @@ mapSeries(directories, readdir, function (err, directoryFiles) { outDir: opts.output_dir } ] - tests = [] // XXX directoryFiles.forEach(function (files, i) { var directory = directories[i] From 5e2cc5922c06b50e73cc5f618307450c764dd2a5 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 13 Oct 2022 08:10:50 -0700 Subject: [PATCH 19/66] some debugging for tests failing in CI --- test/nextjs/a-nextjs-app/next.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index a9eb4d8d536..8a74776ad92 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -440,11 +440,12 @@ function checkExpectedApmEvents (t, apmEvents) { .sort((a, b) => { return getEventField(a, 'timestamp') < getEventField(b, 'timestamp') ? -1 : 1 }) + console.log('XXX filtered and sorted apmEvents:', apmEvents) TEST_REQUESTS.forEach(testReq => { t.comment(`check APM events for "${testReq.testName}"`) // Collect all events for this transaction's trace_id, and pass that to // the `checkApmEvents` function for this request. - assert(apmEvents.length > 0 && apmEvents[0].transaction, 'next APM event is a transaction') + assert(apmEvents.length > 0 && apmEvents[0].transaction, `next APM event is a transaction: ${JSON.stringify(apmEvents[0])}`) const traceId = apmEvents[0].transaction.trace_id const apmEventsForReq = apmEvents.filter(e => getEventField(e, 'trace_id') === traceId) apmEvents = apmEvents.filter(e => getEventField(e, 'trace_id') !== traceId) From c556305b08399ce301a678fa1192669bd49cfa7f Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 13 Oct 2022 11:26:49 -0700 Subject: [PATCH 20/66] fix tests for v14.0 (skip); and hopefully for Windows CI - skip Next.js tests with node v14's that are =11.1.0 <13.0.0')) { agent.logger.debug('next version %s not supported, skipping', version) return mod diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index 8a74776ad92..184037bf667 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -32,6 +32,13 @@ const { MockAPMServer } = require('../../_mock_apm_server') if (semver.lt(process.version, '12.22.0')) { console.log(`# SKIP next does not support node ${process.version}`) process.exit() +} else if (semver.satisfies(process.version, '>=14.0.0 <14.5.0')) { + // The handling of SSR pages, e.g. `GET /an-ssr-page` in the test a-nextjs-app, + // in next@12.3.1 (I'm not sure of the full `next` version range) relies on + // https://github.com/nodejs/node/pull/33155 which landed in v14.5.0 and + // v12.19.0. + console.log(`# SKIP next does not support fully node ${process.version}`) + process.exit() } if (process.env.ELASTIC_APM_CONTEXT_MANAGER === 'patch') { console.log('# SKIP Next.js instrumentation does not work with contextManager="patch"') @@ -296,7 +303,7 @@ let TEST_REQUESTS = [ } ] // XXX -// process.env.XXX_TEST_FILTER = 'slash' +// process.env.XXX_TEST_FILTER = 'SSR' if (process.env.XXX_TEST_FILTER) { TEST_REQUESTS = TEST_REQUESTS.filter(testReq => ~testReq.testName.indexOf(process.env.XXX_TEST_FILTER)) } @@ -457,7 +464,7 @@ function checkExpectedApmEvents (t, apmEvents) { // ---- tests -const SKIP_NPM_CI_FOR_DEV = process.env.USER === 'trentm' // XXX +const SKIP_NPM_CI_FOR_DEV = false // process.env.USER === 'trentm' // XXX if (!SKIP_NPM_CI_FOR_DEV) { tape.test(`setup: npm ci (in ${__dirname})`, t => { const startTime = Date.now() @@ -510,8 +517,8 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { suite.test('setup: start Next.js prod server (next start)', t => { // XXX warning using `npm run start` directly with Docker. nextServerProc = spawn( - process.execPath, - ['./node_modules/.bin/next', 'start'], + './node_modules/.bin/next', + ['start'], { cwd: __dirname, env: Object.assign({}, process.env, { @@ -602,8 +609,8 @@ tape.test('-- dev server tests --', { skip: false /* XXX */ }, suite => { suite.test('setup: start Next.js dev server (next dev)', t => { // XXX warning using `npm run dev` directly with Docker. nextServerProc = spawn( - process.execPath, - ['./node_modules/.bin/next', 'dev'], + './node_modules/.bin/next', + ['dev'], { cwd: __dirname, env: Object.assign({}, process.env, { From 18bbd38e86459ad213e0885b35d4420902295aff Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 13 Oct 2022 12:39:25 -0700 Subject: [PATCH 21/66] test fixes: skip Windows; give 20s for Next.js server to come up on slower CI There is something I don't grok on a Windows install of next. The "node_modules/.bin/next" is a *shell script*!? The current spawn test code results in this on Windows: [2022-10-13T18:46:50.744Z] Error: spawn ./node_modules/.bin/next ENOENT [2022-10-13T18:46:50.744Z] at Process.ChildProcess._handle.onexit (internal/child_process.js:277:19) [2022-10-13T18:46:50.744Z] at onErrorNT (internal/child_process.js:472:16) [2022-10-13T18:46:50.744Z] at processTicksAndRejections (internal/process/task_queues.js:82:21) Perhaps adding shell would help. Really should grok why shell script vs a node script on macos and Linux. --- test/nextjs/a-nextjs-app/next.test.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index 184037bf667..992e9937d33 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -24,11 +24,16 @@ const assert = require('assert') const { exec, spawn } = require('child_process') const http = require('http') +const os = require('os') const semver = require('semver') const tape = require('tape') const { MockAPMServer } = require('../../_mock_apm_server') +if (os.platform() === 'win32') { + console.log('# SKIP Next.js testing currently is not supported on windows') + process.exit() +} if (semver.lt(process.version, '12.22.0')) { console.log(`# SKIP next does not support node ${process.version}`) process.exit() @@ -326,7 +331,7 @@ if (process.env.XXX_TEST_FILTER) { * success. */ function waitForServerReady (t, cb) { - let sentinel = 10 + let sentinel = 20 const pollForServerReady = () => { const req = http.get( @@ -362,7 +367,7 @@ function waitForServerReady (t, cb) { } const scheduleNextPoll = (msg) => { - t.comment(`[sentinel=${sentinel}] wait another 1s for server ready: ${msg}`) + t.comment(`[sentinel=${sentinel} ${new Date().toISOString()}] wait another 1s for server ready: ${msg}`) sentinel-- if (sentinel <= 0) { cb(new Error('timed out')) From ce94960867ff6d4b6d33bc35f9a4ffb9ebd41a99 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 13 Oct 2022 12:43:18 -0700 Subject: [PATCH 22/66] give shell=true on win32 a try for Next.js testing on win32 --- test/nextjs/a-nextjs-app/next.test.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index 992e9937d33..c9f9d4c4a90 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -30,10 +30,11 @@ const tape = require('tape') const { MockAPMServer } = require('../../_mock_apm_server') -if (os.platform() === 'win32') { - console.log('# SKIP Next.js testing currently is not supported on windows') - process.exit() -} +// XXX +// if (os.platform() === 'win32') { +// console.log('# SKIP Next.js testing currently is not supported on windows') +// process.exit() +// } if (semver.lt(process.version, '12.22.0')) { console.log(`# SKIP next does not support node ${process.version}`) process.exit() @@ -525,6 +526,7 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { './node_modules/.bin/next', ['start'], { + shell: os.platform() === 'win32', cwd: __dirname, env: Object.assign({}, process.env, { NODE_OPTIONS: '-r ./apmsetup.js', @@ -617,6 +619,7 @@ tape.test('-- dev server tests --', { skip: false /* XXX */ }, suite => { './node_modules/.bin/next', ['dev'], { + shell: os.platform() === 'win32', cwd: __dirname, env: Object.assign({}, process.env, { NODE_OPTIONS: '-r ./apmsetup.js', From 382be82977030ae3a098913a09bc9e0badf19e6e Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 13 Oct 2022 13:19:37 -0700 Subject: [PATCH 23/66] more correct fix for node v18 tests: the issue was ipv4 vs ipv6 disagreement on default from server and client (as discussed earlier here https://github.com/elastic/apm-agent-nodejs/pull/2380#issuecomment-948044619) --- test/nextjs/a-nextjs-app/next.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index c9f9d4c4a90..3e207adeb1a 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -332,7 +332,7 @@ if (process.env.XXX_TEST_FILTER) { * success. */ function waitForServerReady (t, cb) { - let sentinel = 20 + let sentinel = 10 const pollForServerReady = () => { const req = http.get( @@ -524,7 +524,7 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { // XXX warning using `npm run start` directly with Docker. nextServerProc = spawn( './node_modules/.bin/next', - ['start'], + ['start', '-H', 'localhost'], { shell: os.platform() === 'win32', cwd: __dirname, @@ -617,7 +617,7 @@ tape.test('-- dev server tests --', { skip: false /* XXX */ }, suite => { // XXX warning using `npm run dev` directly with Docker. nextServerProc = spawn( './node_modules/.bin/next', - ['dev'], + ['dev', '-H', 'localhost'], { shell: os.platform() === 'win32', cwd: __dirname, From 0e0475111856b6ec6871974ebf7d463654a20b3f Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 13 Oct 2022 14:18:27 -0700 Subject: [PATCH 24/66] moar trying for Windows tests to get spawn to work This time was getting: [2022-10-13T20:38:08.562Z] # [Next.js server stderr] '.' is not recognized as an internal or external command, [2022-10-13T20:38:08.562Z] # operable program or batch file. --- test/nextjs/a-nextjs-app/next.test.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index 3e207adeb1a..78351908c49 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -25,6 +25,7 @@ const assert = require('assert') const { exec, spawn } = require('child_process') const http = require('http') const os = require('os') +const path = require('path') const semver = require('semver') const tape = require('tape') @@ -523,7 +524,7 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { suite.test('setup: start Next.js prod server (next start)', t => { // XXX warning using `npm run start` directly with Docker. nextServerProc = spawn( - './node_modules/.bin/next', + path.normalize('./node_modules/.bin/next'), ['start', '-H', 'localhost'], { shell: os.platform() === 'win32', @@ -616,7 +617,7 @@ tape.test('-- dev server tests --', { skip: false /* XXX */ }, suite => { suite.test('setup: start Next.js dev server (next dev)', t => { // XXX warning using `npm run dev` directly with Docker. nextServerProc = spawn( - './node_modules/.bin/next', + path.normalize('./node_modules/.bin/next'), ['dev', '-H', 'localhost'], { shell: os.platform() === 'win32', From 95c3ad1177c1319eb2ddab5b845456da6eea638a Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 13 Oct 2022 15:06:54 -0700 Subject: [PATCH 25/66] nope to Windows testing now, thanks cmd.exe; better dynamic rewrite test case --- test/nextjs/a-nextjs-app/next.config.js | 4 ++-- test/nextjs/a-nextjs-app/next.test.js | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/test/nextjs/a-nextjs-app/next.config.js b/test/nextjs/a-nextjs-app/next.config.js index ff64ede65e5..91844733d52 100644 --- a/test/nextjs/a-nextjs-app/next.config.js +++ b/test/nextjs/a-nextjs-app/next.config.js @@ -33,8 +33,8 @@ const nextConfig = { }, { // XXX improve this to have dynamic value in params - source: '/rewrite-to-a-dynamic-page', - destination: '/a-dynamic-page/3.14' + source: '/rewrite-to-a-dynamic-page/:num', + destination: '/a-dynamic-page/:num' }, { source: '/rewrite-to-a-public-file', diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index 78351908c49..0c1ba61f546 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -31,11 +31,14 @@ const tape = require('tape') const { MockAPMServer } = require('../../_mock_apm_server') -// XXX -// if (os.platform() === 'win32') { -// console.log('# SKIP Next.js testing currently is not supported on windows') -// process.exit() -// } +if (os.platform() === 'win32') { + // The current mechanism using shell=true to spawn on Windows *and* attempting + // to use SIGTERM to terminal the Next.js server doesn't work because cmd.exe + // does an interactive prompt. Lovely. + // Terminate batch job (Y/N)? + console.log('# SKIP Next.js testing currently is not supported on windows') + process.exit() +} if (semver.lt(process.version, '12.22.0')) { console.log(`# SKIP next does not support node ${process.version}`) process.exit() @@ -105,8 +108,7 @@ let TEST_REQUESTS = [ }, { testName: 'rewrite to a dynamic page', - // XXX improve this with 'num' query param - req: { method: 'GET', path: '/rewrite-to-a-dynamic-page' }, + req: { method: 'GET', path: '/rewrite-to-a-dynamic-page/3.14159' }, expectedRes: { statusCode: 200, headers: { 'content-type': /text\/html/ }, @@ -115,7 +117,7 @@ let TEST_REQUESTS = [ checkApmEvents: (t, apmEventsForReq) => { t.equal(apmEventsForReq.length, 1) const trans = apmEventsForReq[0].transaction - t.equal(trans.name, 'Next.js Rewrite route /rewrite-to-a-dynamic-page -> /a-dynamic-page/3.14', 'transaction.name') + t.equal(trans.name, 'Next.js Rewrite route /rewrite-to-a-dynamic-page/:num -> /a-dynamic-page/:num', 'transaction.name') t.equal(trans.context.response.status_code, 200, 'transaction.context.response.status_code') } }, From 0a7f70cd8ad0de9b982c3e8558728f78bfc99dda Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 13 Oct 2022 16:09:31 -0700 Subject: [PATCH 26/66] attempt to avoid invalid XML errors/warnings from Jenkins looking at JUnit XML files including 'next dev' ANSI escapes Failed to read test report file /var/lib/jenkins/workspace/PR-2959-19-d0425736-922e-4205-8d3a-b205f2e2877e/src/github.com/elastic/apm-agent-nodejs/test_output/test-nextjs-a-nextjs-app-next.test.js.tap.junit.xml org.dom4j.DocumentException: Error on line 132 of document : An invalid XML character (Unicode: 0x1b) was found in the element content of the document. at org.dom4j.io.SAXReader.read(SAXReader.java:511) at org.dom4j.io.SAXReader.read(SAXReader.java:392) at hudson.tasks.junit.SuiteResult.parse(SuiteResult.java:177) at hudson.tasks.junit.TestResult.parse(TestResult.java:384) at hudson.tasks.junit.TestResult.parsePossiblyEmpty(TestResult.java:314) at hudson.tasks.junit.TestResult.parse(TestResult.java:256) at hudson.tasks.junit.TestResult.parse(TestResult.java:242) at hudson.tasks.junit.TestResult.parse(TestResult.java:220) at hudson.tasks.junit.TestResult.(TestResult.java:174) at hudson.tasks.junit.JUnitParser$ParseResultCallable.invoke(JUnitParser.java:176) at hudson.FilePath$FileCallableWrapper.call(FilePath.java:3502) at hudson.remoting.UserRequest.perform(UserRequest.java:211) at hudson.remoting.UserRequest.perform(UserRequest.java:54) at hudson.remoting.Request$2.run(Request.java:376) at hudson.remoting.InterceptingExecutorService.lambda$wrap$0(InterceptingExecutorService.java:78) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at hudson.remoting.Engine$1.lambda$newThread$0(Engine.java:122) at java.lang.Thread.run(Thread.java:750) Caused by: org.xml.sax.SAXParseException; lineNumber: 132; columnNumber: 88; An invalid XML character (Unicode: 0x1b) was found in the element content of the document. at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:204) at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:178) at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:399) at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:326) at com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1466) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2922) at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:601) at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:504) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:841) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:770) at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141) at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213) at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:642) at org.dom4j.io.SAXReader.read(SAXReader.java:494) ... 19 more --- test/nextjs/a-nextjs-app/next.test.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index 0c1ba61f546..101caf6606c 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -55,6 +55,9 @@ if (process.env.ELASTIC_APM_CONTEXT_MANAGER === 'patch') { process.exit() } +/* eslint-disable-next-line no-control-regex */ +const ansiRe = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g + let apmServer let serverUrl @@ -634,10 +637,15 @@ tape.test('-- dev server tests --', { skip: false /* XXX */ }, suite => { t.error(err, 'no error from "next dev"') }) nextServerProc.stdout.on('data', data => { - t.comment(`[Next.js server stdout] ${data}`) + // Drop ANSI escape characters, because those include control chars that + // are illegal in XML. When we convert TAP output to JUnit XML for + // Jenkins, then Jenkins complains about invalid XML. `FORCE_COLOR=0` + // can be used to disable ANSI escapes in `next dev`'s usage of chalk, + // but not in its coloured exception output. + t.comment(`[Next.js server stdout] ${data.replace(ansiRe, '')}`) }) nextServerProc.stderr.on('data', data => { - t.comment(`[Next.js server stderr] ${data}`) + t.comment(`[Next.js server stderr] ${data.replace(ansiRe, '')}`) }) // Allow some time for an early fail of `next dev`, e.g. if there is From dd558437aa334ffb99c0d4df0732cd06dcb4a97c Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 13 Oct 2022 16:17:56 -0700 Subject: [PATCH 27/66] fix the recently added stdout/stderr filtering (Buffer -> string) --- test/nextjs/a-nextjs-app/next.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index 101caf6606c..e7c25215bbc 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -55,8 +55,8 @@ if (process.env.ELASTIC_APM_CONTEXT_MANAGER === 'patch') { process.exit() } -/* eslint-disable-next-line no-control-regex */ -const ansiRe = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g +// Match ANSI escapes (from https://stackoverflow.com/a/29497680/14444044). +const ansiRe = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g /* eslint-disable-line no-control-regex */ let apmServer let serverUrl @@ -642,10 +642,10 @@ tape.test('-- dev server tests --', { skip: false /* XXX */ }, suite => { // Jenkins, then Jenkins complains about invalid XML. `FORCE_COLOR=0` // can be used to disable ANSI escapes in `next dev`'s usage of chalk, // but not in its coloured exception output. - t.comment(`[Next.js server stdout] ${data.replace(ansiRe, '')}`) + t.comment(`[Next.js server stdout] ${data.toString().replace(ansiRe, '')}`) }) nextServerProc.stderr.on('data', data => { - t.comment(`[Next.js server stderr] ${data.replace(ansiRe, '')}`) + t.comment(`[Next.js server stderr] ${data.toString().replace(ansiRe, '')}`) }) // Allow some time for an early fail of `next dev`, e.g. if there is From 07534e567709b706ff3fc4b246597a3b9ed43ae3 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 13 Oct 2022 16:46:11 -0700 Subject: [PATCH 28/66] add TAV testing of Next.js --- package.json | 2 +- test/nextjs/a-nextjs-app/.tav.yml | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 test/nextjs/a-nextjs-app/.tav.yml diff --git a/package.json b/package.json index 7ed3fbaa15d..fffba8d985e 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "coverage": "COVERAGE=true ./test/script/run_tests.sh", "test": "./test/script/run_tests.sh", "test:deps": "dependency-check start.js index.js 'lib/**/*.js' 'test/**/*.js' '!test/nextjs/a-nextjs-app' --no-dev -i async_hooks -i perf_hooks -i parseurl -i node:http", - "test:tav": "tav --quiet", + "test:tav": "tav --quiet && (cd test/nextjs/a-nextjs-app && tav --quiet)", "test:docs": "./test/script/docker/run_docs.sh", "test:types": "tsc --project test/types/tsconfig.json && tsc --project test/types/transpile/tsconfig.json && node test/types/transpile/index.js && tsc --project test/types/transpile-default/tsconfig.json && node test/types/transpile-default/index.js", "test:babel": "babel test/babel/src.js --out-file test/babel/out.js && cd test/babel && node out.js", diff --git a/test/nextjs/a-nextjs-app/.tav.yml b/test/nextjs/a-nextjs-app/.tav.yml new file mode 100644 index 00000000000..8b44847dbd1 --- /dev/null +++ b/test/nextjs/a-nextjs-app/.tav.yml @@ -0,0 +1,24 @@ +# Test Next.js versions. +# +# - We instrument Next.js ">=11.1.0 <13.0.0". I don't see much value in testing +# every patch-level release. Instead we will test the latest patch release +# for each minor. (It would be nice if tav supported this natively.) +# - Next.js 11 supports back to node v12.22.0 and v14.5.0. However, when this +# instrumentation was added, Node v12 was already EOL, so there is less value +# in testing it. +next-11: + name: next + versions: '11.1.0 || 11.1.4' + node: '>=14.5.0' + commands: node next.test.js + peerDependencies: + - "react@^17.0.2" + - "react-dom@^17.0.2" +next: + name: next + versions: '12.0.10 || 12.1.6 || 12.2.6 || 12.3.1 || >12.3.1 <13' + node: '>=14.5.0' + commands: node next.test.js + peerDependencies: + - "react@^18.2.0" + - "react-dom@^18.2.0" From 7af00310c13d3d45904e90e3ceed2c04f7a6fa7f Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 13 Oct 2022 17:23:00 -0700 Subject: [PATCH 29/66] needed for tav runs in Jenkins --- .ci/.jenkins_tav.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/.jenkins_tav.yml b/.ci/.jenkins_tav.yml index e4f4f1ae69c..db86aff93b5 100644 --- a/.ci/.jenkins_tav.yml +++ b/.ci/.jenkins_tav.yml @@ -26,6 +26,7 @@ TAV: - mongodb-core - mysql - mysql2 + - next - pg - pug - redis From 5c64f031dc777049c3a8d99c6aed4f4db99c5d8c Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Fri, 14 Oct 2022 10:19:14 -0700 Subject: [PATCH 30/66] test another error case: a throw in getServerSideProps --- test/nextjs/a-nextjs-app/next.test.js | 39 ++++++++++++++++++++------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index e7c25215bbc..baabd67ec8a 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -32,6 +32,7 @@ const tape = require('tape') const { MockAPMServer } = require('../../_mock_apm_server') if (os.platform() === 'win32') { + // Limitation: currently don't support testing on Windows. // The current mechanism using shell=true to spawn on Windows *and* attempting // to use SIGTERM to terminal the Next.js server doesn't work because cmd.exe // does an interactive prompt. Lovely. @@ -45,7 +46,7 @@ if (semver.lt(process.version, '12.22.0')) { } else if (semver.satisfies(process.version, '>=14.0.0 <14.5.0')) { // The handling of SSR pages, e.g. `GET /an-ssr-page` in the test a-nextjs-app, // in next@12.3.1 (I'm not sure of the full `next` version range) relies on - // https://github.com/nodejs/node/pull/33155 which landed in v14.5.0 and + // https://github.com/nodejs/node/pull/33155 which landed in node v14.5.0 and // v12.19.0. console.log(`# SKIP next does not support fully node ${process.version}`) process.exit() @@ -312,19 +313,37 @@ let TEST_REQUESTS = [ t.equal(error.transaction.name, trans.name, 'error.transaction.name') t.equal(error.exception.message, 'throw in page handler', 'error.exception.message') } + }, + { + testName: 'a throw in getServerSideProps', + req: { method: 'GET', path: '/a-throw-in-getServerSideProps' }, + expectedRes: { + statusCode: 500 + }, + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 2) + const trans = apmEventsForReq[0].transaction + const error = apmEventsForReq[1].error + t.equal(trans.name, 'GET /a-throw-in-getServerSideProps', 'transaction.name') + t.equal(trans.context.response.status_code, 500, 'transaction.context.response.status_code') + t.ok(error, 'captured an APM error') + t.equal(trans.trace_id, error.trace_id, 'transaction and error are in same trace') + t.equal(error.parent_id, trans.id, 'error is a child of the transaction') + t.equal(error.transaction.type, 'request', 'error.transaction.type') + t.equal(error.transaction.name, trans.name, 'error.transaction.name') + t.equal(error.exception.message, 'thrown error in getServerSideProps', 'error.exception.message') + } } ] -// XXX -// process.env.XXX_TEST_FILTER = 'SSR' -if (process.env.XXX_TEST_FILTER) { - TEST_REQUESTS = TEST_REQUESTS.filter(testReq => ~testReq.testName.indexOf(process.env.XXX_TEST_FILTER)) +// Dev Note: To limit a test run to a particular test request, provide a +// string value to DEV_TEST_FILTER that matches `testName`. +var DEV_TEST_FILTER = null +// DEV_TEST_FILTER = 'getServerSideProps' // XXX +if (DEV_TEST_FILTER) { + TEST_REQUESTS = TEST_REQUESTS.filter(testReq => ~testReq.testName.indexOf(DEV_TEST_FILTER)) + assert(TEST_REQUESTS.length > 0, 'DEV_TEST_FILTER should not result in an *empty* TEST_REQUESTS') } -// XXX HERE TODO: -// - error endpoints (grok the "bugs" in NOTES) -// curl -i localhost:3000/api/an-api-endpoint-that-throws -// ... the other two /throw-in-... pages XXX one more! - // ---- utility functions /** From be1a74614c491d58a51d6add9cbab57ff62558ef Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Fri, 14 Oct 2022 11:29:08 -0700 Subject: [PATCH 31/66] test the /_next/data/... route -- the more important of the various internal _next/... routes that are instrumented --- .../modules/next/dist/server/next-server.js | 3 +- test/nextjs/a-nextjs-app/next.test.js | 99 ++++++++++++++----- 2 files changed, 75 insertions(+), 27 deletions(-) diff --git a/lib/instrumentation/modules/next/dist/server/next-server.js b/lib/instrumentation/modules/next/dist/server/next-server.js index b20d4b84b55..7aa3d1141e6 100644 --- a/lib/instrumentation/modules/next/dist/server/next-server.js +++ b/lib/instrumentation/modules/next/dist/server/next-server.js @@ -78,7 +78,6 @@ module.exports = function (mod, agent, { version, enabled }) { } const routes = orig.apply(this, arguments) log.debug('wrap Next.js NodeNextServer routes') - // console.log('XXX routes: ', routes) routes.redirects.forEach(wrapRedirectRoute) routes.rewrites.beforeFiles.forEach(wrapRewriteRoute) routes.rewrites.afterFiles.forEach(wrapRewriteRoute) @@ -129,7 +128,7 @@ module.exports = function (mod, agent, { version, enabled }) { const origRouteFn = route.fn // We explicitly handle only the `fsRoute`s that we know by name in the // Next.js code. We cannot set `trans.name` for all of them because of the - // true catch-all-routes that match any path and only sometimes handled them + // true catch-all-routes that match any path and only sometimes handle them // (e.g. 'public folder catchall'). // XXX splain why we cannot do 'public folder catchall' and equiv. switch (route.name) { diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index baabd67ec8a..0c20d16bb17 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -23,6 +23,7 @@ const assert = require('assert') const { exec, spawn } = require('child_process') +const fs = require('fs') const http = require('http') const os = require('os') const path = require('path') @@ -62,11 +63,25 @@ const ansiRe = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZc let apmServer let serverUrl +// TEST_REQUESTS is an array of requests to test against both a prod-server and +// dev-server run of the 'a-nextjs-app' test app. Each entry is: +// +// { +// testName: '', +// // An object with request options, or a `(buildId) => { ... }` that +// // returns request options. +// reqOpts: Object | Function, +// // An object with expectations of the server response. +// expectedRes: { ... }, +// // Make test assertions of the APM events received for the request. +// checkApmEvents: (t, apmEventsForReq) => { ... }, +// } +// let TEST_REQUESTS = [ // Redirects. { testName: 'trailing slash redirect', - req: { method: 'GET', path: '/a-page/' }, + reqOpts: { method: 'GET', path: '/a-page/' }, expectedRes: { statusCode: 308, headers: { location: '/a-page' } @@ -80,7 +95,7 @@ let TEST_REQUESTS = [ }, { testName: 'configured (in next.config.js) redirect', - req: { method: 'GET', path: '/redirect-to-a-page' }, + reqOpts: { method: 'GET', path: '/redirect-to-a-page' }, expectedRes: { statusCode: 307, headers: { location: '/a-page' } @@ -96,7 +111,7 @@ let TEST_REQUESTS = [ // Rewrites are configured in "next.config.js". { testName: 'rewrite to a page', - req: { method: 'GET', path: '/rewrite-to-a-page' }, + reqOpts: { method: 'GET', path: '/rewrite-to-a-page' }, expectedRes: { statusCode: 200, headers: { 'content-type': /text\/html/ }, @@ -112,7 +127,7 @@ let TEST_REQUESTS = [ }, { testName: 'rewrite to a dynamic page', - req: { method: 'GET', path: '/rewrite-to-a-dynamic-page/3.14159' }, + reqOpts: { method: 'GET', path: '/rewrite-to-a-dynamic-page/3.14159' }, expectedRes: { statusCode: 200, headers: { 'content-type': /text\/html/ }, @@ -127,7 +142,7 @@ let TEST_REQUESTS = [ }, { testName: 'rewrite to a /public/... folder file', - req: { method: 'GET', path: '/rewrite-to-a-public-file' }, + reqOpts: { method: 'GET', path: '/rewrite-to-a-public-file' }, expectedRes: { statusCode: 200, headers: { 'content-type': 'image/x-icon' } @@ -141,7 +156,7 @@ let TEST_REQUESTS = [ }, { testName: 'rewrite to a 404', - req: { method: 'GET', path: '/rewrite-to-a-404' }, + reqOpts: { method: 'GET', path: '/rewrite-to-a-404' }, expectedRes: { statusCode: 404 }, @@ -154,7 +169,7 @@ let TEST_REQUESTS = [ }, { testName: 'rewrite to a external site', - req: { method: 'GET', path: '/rewrite-external/foo' }, + reqOpts: { method: 'GET', path: '/rewrite-external/foo' }, expectedRes: { // This is a 500 because the configured `old.example.com` doesn't resolve. statusCode: 500 @@ -182,7 +197,7 @@ let TEST_REQUESTS = [ // The different kinds of pages. { testName: 'index page', - req: { method: 'GET', path: '/' }, + reqOpts: { method: 'GET', path: '/' }, expectedRes: { statusCode: 200, headers: { 'content-type': /text\/html/ }, @@ -197,7 +212,7 @@ let TEST_REQUESTS = [ }, { testName: 'a page (Server-Side Generated, SSG)', - req: { method: 'GET', path: '/a-page' }, + reqOpts: { method: 'GET', path: '/a-page' }, expectedRes: { statusCode: 200, headers: { 'content-type': /text\/html/ }, @@ -212,7 +227,7 @@ let TEST_REQUESTS = [ }, { testName: 'a dynamic page', - req: { method: 'GET', path: '/a-dynamic-page/42' }, + reqOpts: { method: 'GET', path: '/a-dynamic-page/42' }, expectedRes: { statusCode: 200, headers: { 'content-type': /text\/html/ }, @@ -227,7 +242,7 @@ let TEST_REQUESTS = [ }, { testName: 'a server-side rendered (SSR) page', - req: { method: 'GET', path: '/an-ssr-page' }, + reqOpts: { method: 'GET', path: '/an-ssr-page' }, expectedRes: { statusCode: 200, headers: { 'content-type': /text\/html/ }, @@ -244,7 +259,7 @@ let TEST_REQUESTS = [ // API endpoint pages { testName: 'an API endpoint page', - req: { method: 'GET', path: '/api/an-api-endpoint' }, + reqOpts: { method: 'GET', path: '/api/an-api-endpoint' }, expectedRes: { statusCode: 200, headers: { 'content-type': /application\/json/ }, @@ -259,7 +274,7 @@ let TEST_REQUESTS = [ }, { testName: 'a dynamic API endpoint page', - req: { method: 'GET', path: '/api/a-dynamic-api-endpoint/123' }, + reqOpts: { method: 'GET', path: '/api/a-dynamic-api-endpoint/123' }, expectedRes: { statusCode: 200, headers: { 'content-type': /application\/json/ }, @@ -273,10 +288,30 @@ let TEST_REQUESTS = [ } }, + // Various internal Next.js routes. + // The other routes that our instrumentation covers are listed in + // `wrapFsRoute` in "next-server.js" and "next-dev-server.js". + { + testName: '"_next/data catchall" route', + reqOpts: buildId => { + return { method: 'GET', path: `/_next/data/${buildId}/a-page.json` } + }, + expectedRes: { + statusCode: 200, + headers: { 'content-type': /application\/json/ } + }, + checkApmEvents: (t, apmEventsForReq) => { + t.equal(apmEventsForReq.length, 1) + const trans = apmEventsForReq[0].transaction + t.equal(trans.name, 'Next.js _next/data route /a-page', 'transaction.name') + t.equal(trans.context.response.status_code, 200, 'transaction.context.response.status_code') + } + }, + // Error capture cases { testName: 'an API endpoint that throws', - req: { method: 'GET', path: '/api/an-api-endpoint-that-throws' }, + reqOpts: { method: 'GET', path: '/api/an-api-endpoint-that-throws' }, expectedRes: { statusCode: 500 }, @@ -296,7 +331,7 @@ let TEST_REQUESTS = [ }, { testName: 'a throw in a page handler', - req: { method: 'GET', path: '/a-throw-in-page-handler' }, + reqOpts: { method: 'GET', path: '/a-throw-in-page-handler' }, expectedRes: { statusCode: 500 }, @@ -316,7 +351,7 @@ let TEST_REQUESTS = [ }, { testName: 'a throw in getServerSideProps', - req: { method: 'GET', path: '/a-throw-in-getServerSideProps' }, + reqOpts: { method: 'GET', path: '/a-throw-in-getServerSideProps' }, expectedRes: { statusCode: 500 }, @@ -338,7 +373,7 @@ let TEST_REQUESTS = [ // Dev Note: To limit a test run to a particular test request, provide a // string value to DEV_TEST_FILTER that matches `testName`. var DEV_TEST_FILTER = null -// DEV_TEST_FILTER = 'getServerSideProps' // XXX +DEV_TEST_FILTER = '_next/data' // XXX if (DEV_TEST_FILTER) { TEST_REQUESTS = TEST_REQUESTS.filter(testReq => ~testReq.testName.indexOf(DEV_TEST_FILTER)) assert(TEST_REQUESTS.length > 0, 'DEV_TEST_FILTER should not result in an *empty* TEST_REQUESTS') @@ -405,14 +440,19 @@ function waitForServerReady (t, cb) { pollForServerReady() } -async function makeTestRequest (t, testReq) { +async function makeTestRequest (t, testReq, buildId) { return new Promise((resolve, reject) => { - const url = `http://localhost:3000${testReq.req.path}` - t.comment(`makeTestRequest: ${testReq.testName} (${testReq.req.method} ${url})`) + let reqOpts = testReq.reqOpts + if (typeof reqOpts === 'function') { + reqOpts = reqOpts(buildId) + console.log('XXX reqOpts: ', reqOpts) + } + const url = `http://localhost:3000${reqOpts.path}` + t.comment(`makeTestRequest: ${testReq.testName} (${reqOpts.method} ${url})`) const req = http.request( url, { - method: testReq.req.method + method: reqOpts.method }, res => { const chunks = [] @@ -456,6 +496,15 @@ function getEventField (e, fieldName) { return (e.transaction || e.error || e.span)[fieldName] } +/** + * Return the buildId for this Next.js prod server. The buildId is stored + * in ".next/BUILD_ID" by `next build`. + */ +function getNextProdServerBuildId () { + const buildIdPath = path.join(__dirname, '.next', 'BUILD_ID') + return fs.readFileSync(buildIdPath, 'utf8').trim() +} + /** * Assert that the given `apmEvents` (events that the mock APM server received) * match all the expected APM events in `TEST_REQUESTS`. @@ -601,12 +650,12 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { t.end() return } - apmServer.clear() + const buildId = getNextProdServerBuildId() + apmServer.clear() for (let i = 0; i < TEST_REQUESTS.length; i++) { - await makeTestRequest(t, TEST_REQUESTS[i]) + await makeTestRequest(t, TEST_REQUESTS[i], buildId) } - t.end() }) @@ -702,7 +751,7 @@ tape.test('-- dev server tests --', { skip: false /* XXX */ }, suite => { apmServer.clear() for (let i = 0; i < TEST_REQUESTS.length; i++) { - await makeTestRequest(t, TEST_REQUESTS[i]) + await makeTestRequest(t, TEST_REQUESTS[i], 'development') } t.end() From ed075fbcf5ec3d0c6d02b4ba8fe68feacb0ac7d9 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Fri, 14 Oct 2022 12:25:44 -0700 Subject: [PATCH 32/66] feat: skip stack frame lines of context for minimized files An APM error object will typically include lines of context for each frame in the stack trace. If these are lines from a *minimized* file, then each line is mostly goop and typically 500+ chars. This results in mostly useless and over large APM error objects. This change will skip adding context lines if the stack frame file is "*.min.js" and there was no usable source-map. --- lib/stacktraces.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/stacktraces.js b/lib/stacktraces.js index c42cee4af6d..b3ed19935e3 100644 --- a/lib/stacktraces.js +++ b/lib/stacktraces.js @@ -380,6 +380,15 @@ function frameFromCallSite (log, callsite, cwd, sourceLinesAppFrames, sourceLine } } + // If the file looks like it minimized (as we didn't have a source-map in + // the processing above), then skip adding source context because it + // is mostly useless and the typically 500-char lines result in over-large + // APM error objects. + if (filename.endsWith('.min.js')) { + cacheAndCb(frame) + return + } + // Otherwise load the file from disk, if available. fileCache.get(frame.abs_path, function onFileCacheGet (fileErr, lines) { if (fileErr) { From 1739501b7e42fc67ac178325c8c60973cb0884da Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Fri, 14 Oct 2022 13:51:10 -0700 Subject: [PATCH 33/66] set metadata.service.framework.* --- .../modules/next/dist/server/next-server.js | 6 ++++++ test/nextjs/a-nextjs-app/apmsetup.js | 2 +- test/nextjs/a-nextjs-app/next.test.js | 8 ++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/instrumentation/modules/next/dist/server/next-server.js b/lib/instrumentation/modules/next/dist/server/next-server.js index 7aa3d1141e6..47db6a04c28 100644 --- a/lib/instrumentation/modules/next/dist/server/next-server.js +++ b/lib/instrumentation/modules/next/dist/server/next-server.js @@ -63,6 +63,12 @@ module.exports = function (mod, agent, { version, enabled }) { const ins = agent._instrumentation const log = agent.logger + // This isn't perfect. Which framework the agent will report with a + // custom Next.js server using another framework, e.g. + // https://github.com/vercel/next.js/blob/canary/examples/custom-server-fastify/server.js + // depends on which is *imported* first. + agent.setFramework({ name: 'Next.js', version, overwrite: false }) + const NextNodeServer = mod.default shimmer.wrap(NextNodeServer.prototype, 'generateRoutes', wrapGenerateRoutes) shimmer.wrap(NextNodeServer.prototype, 'ensureApiPage', wrapEnsureApiPage) diff --git a/test/nextjs/a-nextjs-app/apmsetup.js b/test/nextjs/a-nextjs-app/apmsetup.js index 62c5452d29b..da05d6c716d 100644 --- a/test/nextjs/a-nextjs-app/apmsetup.js +++ b/test/nextjs/a-nextjs-app/apmsetup.js @@ -7,7 +7,7 @@ const apm = require('../../../').start({ // elastic-apm-node // XXX most of these for dev/debugging // apmServerVersion: '8.4.1', - stackTraceLimit: 10, + // stackTraceLimit: 10, cloudProvider: 'none', centralConfig: false, metricsInterval: '0s', diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index 0c20d16bb17..5ca57a20f00 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -30,6 +30,8 @@ const path = require('path') const semver = require('semver') const tape = require('tape') +const nextPj = require(path.join(__dirname, 'node_modules/next/package.json')) + const { MockAPMServer } = require('../../_mock_apm_server') if (os.platform() === 'win32') { @@ -373,7 +375,7 @@ let TEST_REQUESTS = [ // Dev Note: To limit a test run to a particular test request, provide a // string value to DEV_TEST_FILTER that matches `testName`. var DEV_TEST_FILTER = null -DEV_TEST_FILTER = '_next/data' // XXX +// DEV_TEST_FILTER = 'throw in a page handler' // XXX if (DEV_TEST_FILTER) { TEST_REQUESTS = TEST_REQUESTS.filter(testReq => ~testReq.testName.indexOf(DEV_TEST_FILTER)) assert(TEST_REQUESTS.length > 0, 'DEV_TEST_FILTER should not result in an *empty* TEST_REQUESTS') @@ -513,8 +515,10 @@ function checkExpectedApmEvents (t, apmEvents) { // metadata let evt = apmEvents.shift() t.ok(evt.metadata, 'metadata is first event') + console.dir(evt.metadata, { depth: 5 }) // XXX t.equal(evt.metadata.service.name, 'a-nextjs-app', 'metadata.service.name') - // XXX assert framework is "next" or "nextjs" or some value we pick + t.equal(evt.metadata.service.framework.name, 'Next.js', 'metadata.service.framework.name') + t.equal(evt.metadata.service.framework.version, nextPj.version, 'metadata.service.framework.version') // One `GET /api/an-api-endpoint` from waitForServerReady. evt = apmEvents.shift() From 9f178ea4cb1cfaa3602a2293933ce9f53590c6d7 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Fri, 14 Oct 2022 14:12:30 -0700 Subject: [PATCH 34/66] move nextPj down after some early test file exits (for when testing with next isn't supported) --- test/nextjs/a-nextjs-app/apmsetup.js | 6 +++++- test/nextjs/a-nextjs-app/components/Header.js | 2 -- test/nextjs/a-nextjs-app/next.test.js | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/test/nextjs/a-nextjs-app/apmsetup.js b/test/nextjs/a-nextjs-app/apmsetup.js index da05d6c716d..3256bd5bb95 100644 --- a/test/nextjs/a-nextjs-app/apmsetup.js +++ b/test/nextjs/a-nextjs-app/apmsetup.js @@ -5,12 +5,16 @@ */ const apm = require('../../../').start({ // elastic-apm-node + // trentm-play7, 8.4.1 + serverUrl: 'https://my-deployment-31a70c.apm.us-west2.gcp.elastic-cloud.com', + secretToken: '45g9zaO5n6dp2b5EPN', + // XXX most of these for dev/debugging // apmServerVersion: '8.4.1', // stackTraceLimit: 10, cloudProvider: 'none', centralConfig: false, - metricsInterval: '0s', + // metricsInterval: '0s', useElasticTraceparentHeader: false, // XXX // captureExceptions: false, // XXX logUncaughtExceptions: true, diff --git a/test/nextjs/a-nextjs-app/components/Header.js b/test/nextjs/a-nextjs-app/components/Header.js index 0986d75e88e..f940a9b37e8 100644 --- a/test/nextjs/a-nextjs-app/components/Header.js +++ b/test/nextjs/a-nextjs-app/components/Header.js @@ -23,8 +23,6 @@ function Header () {  |  AnSSRPage  |  - AnSSRErrorPage -  |  ADynamicPage/42
diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/nextjs/a-nextjs-app/next.test.js index 5ca57a20f00..76e392fb392 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/nextjs/a-nextjs-app/next.test.js @@ -30,8 +30,6 @@ const path = require('path') const semver = require('semver') const tape = require('tape') -const nextPj = require(path.join(__dirname, 'node_modules/next/package.json')) - const { MockAPMServer } = require('../../_mock_apm_server') if (os.platform() === 'win32') { @@ -59,6 +57,8 @@ if (process.env.ELASTIC_APM_CONTEXT_MANAGER === 'patch') { process.exit() } +const nextPj = require(path.join(__dirname, 'node_modules/next/package.json')) + // Match ANSI escapes (from https://stackoverflow.com/a/29497680/14444044). const ansiRe = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g /* eslint-disable-line no-control-regex */ From f56988a528d9a34989d8cb6951d9d9446e4af4d6 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Fri, 14 Oct 2022 14:35:47 -0700 Subject: [PATCH 35/66] move tests to test/instrumentation/modules/next/... where it should be --- .eslintrc.json | 4 +-- package.json | 4 +-- .../modules/next}/a-nextjs-app/.tav.yml | 4 +-- .../modules/next}/a-nextjs-app/apmsetup.js | 6 +--- .../next}/a-nextjs-app/components/Header.js | 0 .../modules/next}/a-nextjs-app/next.config.js | 0 .../next}/a-nextjs-app/package-lock.json | 0 .../modules/next}/a-nextjs-app/package.json | 0 .../pages/a-dynamic-page/[num].js | 0 .../next}/a-nextjs-app/pages/a-page.js | 0 .../pages/a-throw-in-getServerSideProps.js | 0 .../pages/a-throw-in-page-handler.js | 0 .../next}/a-nextjs-app/pages/an-ssr-page.js | 0 .../pages/api/a-dynamic-api-endpoint/[num].js | 0 .../pages/api/an-api-endpoint-that-throws.js | 0 .../a-nextjs-app/pages/api/an-api-endpoint.js | 0 .../modules/next}/a-nextjs-app/pages/index.js | 0 .../a-nextjs-app/public/elastic-logo.png | Bin .../next}/a-nextjs-app/public/favicon.ico | Bin .../modules/next}/next.test.js | 28 ++++++++++-------- 20 files changed, 23 insertions(+), 23 deletions(-) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/.tav.yml (91%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/apmsetup.js (83%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/components/Header.js (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/next.config.js (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/package-lock.json (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/package.json (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/pages/a-dynamic-page/[num].js (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/pages/a-page.js (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/pages/a-throw-in-getServerSideProps.js (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/pages/a-throw-in-page-handler.js (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/pages/an-ssr-page.js (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/pages/api/a-dynamic-api-endpoint/[num].js (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/pages/api/an-api-endpoint.js (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/pages/index.js (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/public/elastic-logo.png (100%) rename test/{nextjs => instrumentation/modules/next}/a-nextjs-app/public/favicon.ico (100%) rename test/{nextjs/a-nextjs-app => instrumentation/modules/next}/next.test.js (97%) diff --git a/.eslintrc.json b/.eslintrc.json index 872b202c8e6..cc166de31fc 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -19,8 +19,8 @@ "/lib/opentelemetry-bridge/opentelemetry-core-mini", "/test/babel/out.js", "/test/lambda/fixtures/esbuild-bundled-handler/hello.js", - "/test/nextjs/a-nextjs-app/pages", - "/test/nextjs/a-nextjs-app/components", + "/test/instrumentation/modules/next/a-nextjs-app/pages", + "/test/instrumentation/modules/next/a-nextjs-app/components", "/test/sourcemaps/fixtures/lib", "/test/sourcemaps/fixtures/src", "/test/stacktraces/fixtures/dist", diff --git a/package.json b/package.json index 670b881b0e7..a42c17e3707 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "lint:yaml-files": "./dev-utils/lint-yaml-files.sh # requires node >=10", "coverage": "COVERAGE=true ./test/script/run_tests.sh", "test": "./test/script/run_tests.sh", - "test:deps": "dependency-check start.js index.js 'lib/**/*.js' 'test/**/*.js' '!test/nextjs/a-nextjs-app' --no-dev -i async_hooks -i perf_hooks -i parseurl -i node:http", - "test:tav": "tav --quiet && (cd test/nextjs/a-nextjs-app && tav --quiet)", + "test:deps": "dependency-check start.js index.js 'lib/**/*.js' 'test/**/*.js' '!test/instrumentation/modules/next/a-nextjs-app' --no-dev -i async_hooks -i perf_hooks -i parseurl -i node:http", + "test:tav": "tav --quiet && (cd test/instrumentation/modules/next/a-nextjs-app && tav --quiet)", "test:docs": "./test/script/docker/run_docs.sh", "test:types": "tsc --project test/types/tsconfig.json && tsc --project test/types/transpile/tsconfig.json && node test/types/transpile/index.js && tsc --project test/types/transpile-default/tsconfig.json && node test/types/transpile-default/index.js", "test:babel": "babel test/babel/src.js --out-file test/babel/out.js && cd test/babel && node out.js", diff --git a/test/nextjs/a-nextjs-app/.tav.yml b/test/instrumentation/modules/next/a-nextjs-app/.tav.yml similarity index 91% rename from test/nextjs/a-nextjs-app/.tav.yml rename to test/instrumentation/modules/next/a-nextjs-app/.tav.yml index 8b44847dbd1..4ac794cfa8f 100644 --- a/test/nextjs/a-nextjs-app/.tav.yml +++ b/test/instrumentation/modules/next/a-nextjs-app/.tav.yml @@ -10,7 +10,7 @@ next-11: name: next versions: '11.1.0 || 11.1.4' node: '>=14.5.0' - commands: node next.test.js + commands: node ../next.test.js peerDependencies: - "react@^17.0.2" - "react-dom@^17.0.2" @@ -18,7 +18,7 @@ next: name: next versions: '12.0.10 || 12.1.6 || 12.2.6 || 12.3.1 || >12.3.1 <13' node: '>=14.5.0' - commands: node next.test.js + commands: node ../next.test.js peerDependencies: - "react@^18.2.0" - "react-dom@^18.2.0" diff --git a/test/nextjs/a-nextjs-app/apmsetup.js b/test/instrumentation/modules/next/a-nextjs-app/apmsetup.js similarity index 83% rename from test/nextjs/a-nextjs-app/apmsetup.js rename to test/instrumentation/modules/next/a-nextjs-app/apmsetup.js index 3256bd5bb95..23d4d3f2a34 100644 --- a/test/nextjs/a-nextjs-app/apmsetup.js +++ b/test/instrumentation/modules/next/a-nextjs-app/apmsetup.js @@ -4,11 +4,7 @@ * compliance with the BSD 2-Clause License. */ -const apm = require('../../../').start({ // elastic-apm-node - // trentm-play7, 8.4.1 - serverUrl: 'https://my-deployment-31a70c.apm.us-west2.gcp.elastic-cloud.com', - secretToken: '45g9zaO5n6dp2b5EPN', - +const apm = require('../../../../../').start({ // elastic-apm-node // XXX most of these for dev/debugging // apmServerVersion: '8.4.1', // stackTraceLimit: 10, diff --git a/test/nextjs/a-nextjs-app/components/Header.js b/test/instrumentation/modules/next/a-nextjs-app/components/Header.js similarity index 100% rename from test/nextjs/a-nextjs-app/components/Header.js rename to test/instrumentation/modules/next/a-nextjs-app/components/Header.js diff --git a/test/nextjs/a-nextjs-app/next.config.js b/test/instrumentation/modules/next/a-nextjs-app/next.config.js similarity index 100% rename from test/nextjs/a-nextjs-app/next.config.js rename to test/instrumentation/modules/next/a-nextjs-app/next.config.js diff --git a/test/nextjs/a-nextjs-app/package-lock.json b/test/instrumentation/modules/next/a-nextjs-app/package-lock.json similarity index 100% rename from test/nextjs/a-nextjs-app/package-lock.json rename to test/instrumentation/modules/next/a-nextjs-app/package-lock.json diff --git a/test/nextjs/a-nextjs-app/package.json b/test/instrumentation/modules/next/a-nextjs-app/package.json similarity index 100% rename from test/nextjs/a-nextjs-app/package.json rename to test/instrumentation/modules/next/a-nextjs-app/package.json diff --git a/test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js b/test/instrumentation/modules/next/a-nextjs-app/pages/a-dynamic-page/[num].js similarity index 100% rename from test/nextjs/a-nextjs-app/pages/a-dynamic-page/[num].js rename to test/instrumentation/modules/next/a-nextjs-app/pages/a-dynamic-page/[num].js diff --git a/test/nextjs/a-nextjs-app/pages/a-page.js b/test/instrumentation/modules/next/a-nextjs-app/pages/a-page.js similarity index 100% rename from test/nextjs/a-nextjs-app/pages/a-page.js rename to test/instrumentation/modules/next/a-nextjs-app/pages/a-page.js diff --git a/test/nextjs/a-nextjs-app/pages/a-throw-in-getServerSideProps.js b/test/instrumentation/modules/next/a-nextjs-app/pages/a-throw-in-getServerSideProps.js similarity index 100% rename from test/nextjs/a-nextjs-app/pages/a-throw-in-getServerSideProps.js rename to test/instrumentation/modules/next/a-nextjs-app/pages/a-throw-in-getServerSideProps.js diff --git a/test/nextjs/a-nextjs-app/pages/a-throw-in-page-handler.js b/test/instrumentation/modules/next/a-nextjs-app/pages/a-throw-in-page-handler.js similarity index 100% rename from test/nextjs/a-nextjs-app/pages/a-throw-in-page-handler.js rename to test/instrumentation/modules/next/a-nextjs-app/pages/a-throw-in-page-handler.js diff --git a/test/nextjs/a-nextjs-app/pages/an-ssr-page.js b/test/instrumentation/modules/next/a-nextjs-app/pages/an-ssr-page.js similarity index 100% rename from test/nextjs/a-nextjs-app/pages/an-ssr-page.js rename to test/instrumentation/modules/next/a-nextjs-app/pages/an-ssr-page.js diff --git a/test/nextjs/a-nextjs-app/pages/api/a-dynamic-api-endpoint/[num].js b/test/instrumentation/modules/next/a-nextjs-app/pages/api/a-dynamic-api-endpoint/[num].js similarity index 100% rename from test/nextjs/a-nextjs-app/pages/api/a-dynamic-api-endpoint/[num].js rename to test/instrumentation/modules/next/a-nextjs-app/pages/api/a-dynamic-api-endpoint/[num].js diff --git a/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js b/test/instrumentation/modules/next/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js similarity index 100% rename from test/nextjs/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js rename to test/instrumentation/modules/next/a-nextjs-app/pages/api/an-api-endpoint-that-throws.js diff --git a/test/nextjs/a-nextjs-app/pages/api/an-api-endpoint.js b/test/instrumentation/modules/next/a-nextjs-app/pages/api/an-api-endpoint.js similarity index 100% rename from test/nextjs/a-nextjs-app/pages/api/an-api-endpoint.js rename to test/instrumentation/modules/next/a-nextjs-app/pages/api/an-api-endpoint.js diff --git a/test/nextjs/a-nextjs-app/pages/index.js b/test/instrumentation/modules/next/a-nextjs-app/pages/index.js similarity index 100% rename from test/nextjs/a-nextjs-app/pages/index.js rename to test/instrumentation/modules/next/a-nextjs-app/pages/index.js diff --git a/test/nextjs/a-nextjs-app/public/elastic-logo.png b/test/instrumentation/modules/next/a-nextjs-app/public/elastic-logo.png similarity index 100% rename from test/nextjs/a-nextjs-app/public/elastic-logo.png rename to test/instrumentation/modules/next/a-nextjs-app/public/elastic-logo.png diff --git a/test/nextjs/a-nextjs-app/public/favicon.ico b/test/instrumentation/modules/next/a-nextjs-app/public/favicon.ico similarity index 100% rename from test/nextjs/a-nextjs-app/public/favicon.ico rename to test/instrumentation/modules/next/a-nextjs-app/public/favicon.ico diff --git a/test/nextjs/a-nextjs-app/next.test.js b/test/instrumentation/modules/next/next.test.js similarity index 97% rename from test/nextjs/a-nextjs-app/next.test.js rename to test/instrumentation/modules/next/next.test.js index 76e392fb392..4cecab95cac 100644 --- a/test/nextjs/a-nextjs-app/next.test.js +++ b/test/instrumentation/modules/next/next.test.js @@ -30,7 +30,7 @@ const path = require('path') const semver = require('semver') const tape = require('tape') -const { MockAPMServer } = require('../../_mock_apm_server') +const { MockAPMServer } = require('../../../_mock_apm_server') if (os.platform() === 'win32') { // Limitation: currently don't support testing on Windows. @@ -57,7 +57,8 @@ if (process.env.ELASTIC_APM_CONTEXT_MANAGER === 'patch') { process.exit() } -const nextPj = require(path.join(__dirname, 'node_modules/next/package.json')) +const testAppDir = path.join(__dirname, 'a-nextjs-app') +const nextPj = require(path.join(testAppDir, 'node_modules/next/package.json')) // Match ANSI escapes (from https://stackoverflow.com/a/29497680/14444044). const ansiRe = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g /* eslint-disable-line no-control-regex */ @@ -447,7 +448,6 @@ async function makeTestRequest (t, testReq, buildId) { let reqOpts = testReq.reqOpts if (typeof reqOpts === 'function') { reqOpts = reqOpts(buildId) - console.log('XXX reqOpts: ', reqOpts) } const url = `http://localhost:3000${reqOpts.path}` t.comment(`makeTestRequest: ${testReq.testName} (${reqOpts.method} ${url})`) @@ -503,7 +503,7 @@ function getEventField (e, fieldName) { * in ".next/BUILD_ID" by `next build`. */ function getNextProdServerBuildId () { - const buildIdPath = path.join(__dirname, '.next', 'BUILD_ID') + const buildIdPath = path.join(testAppDir, '.next', 'BUILD_ID') return fs.readFileSync(buildIdPath, 'utf8').trim() } @@ -515,19 +515,23 @@ function checkExpectedApmEvents (t, apmEvents) { // metadata let evt = apmEvents.shift() t.ok(evt.metadata, 'metadata is first event') - console.dir(evt.metadata, { depth: 5 }) // XXX t.equal(evt.metadata.service.name, 'a-nextjs-app', 'metadata.service.name') t.equal(evt.metadata.service.framework.name, 'Next.js', 'metadata.service.framework.name') t.equal(evt.metadata.service.framework.version, nextPj.version, 'metadata.service.framework.version') + // Filter out any metadata from separate requests, and metricsets which we + // aren't testing. + apmEvents = apmEvents + .filter(e => !e.metadata) + .filter(e => !e.metricset) + // One `GET /api/an-api-endpoint` from waitForServerReady. evt = apmEvents.shift() t.equal(evt.transaction.name, 'GET /api/an-api-endpoint', 'waitForServerReady request') t.equal(evt.transaction.outcome, 'success', 'transaction.outcome') - // Expected APM events from all TEST_REQUESTS. + // Sort all the remaining APM events and check expectations from TEST_REQUESTS. apmEvents = apmEvents - .filter(e => !e.metadata) .sort((a, b) => { return getEventField(a, 'timestamp') < getEventField(b, 'timestamp') ? -1 : 1 }) @@ -550,12 +554,12 @@ function checkExpectedApmEvents (t, apmEvents) { const SKIP_NPM_CI_FOR_DEV = false // process.env.USER === 'trentm' // XXX if (!SKIP_NPM_CI_FOR_DEV) { - tape.test(`setup: npm ci (in ${__dirname})`, t => { + tape.test(`setup: npm ci (in ${testAppDir})`, t => { const startTime = Date.now() exec( 'npm ci', { - cwd: __dirname + cwd: testAppDir }, function (err, stdout, stderr) { t.error(err, `"npm ci" succeeded (took ${(Date.now() - startTime) / 1000}s)`) @@ -586,7 +590,7 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { exec( 'npm run build', { - cwd: __dirname + cwd: testAppDir }, function (err, stdout, stderr) { t.error(err, `"npm run build" succeeded (took ${(Date.now() - startTime) / 1000}s)`) @@ -605,7 +609,7 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { ['start', '-H', 'localhost'], { shell: os.platform() === 'win32', - cwd: __dirname, + cwd: testAppDir, env: Object.assign({}, process.env, { NODE_OPTIONS: '-r ./apmsetup.js', ELASTIC_APM_SERVER_URL: serverUrl @@ -698,7 +702,7 @@ tape.test('-- dev server tests --', { skip: false /* XXX */ }, suite => { ['dev', '-H', 'localhost'], { shell: os.platform() === 'win32', - cwd: __dirname, + cwd: testAppDir, env: Object.assign({}, process.env, { NODE_OPTIONS: '-r ./apmsetup.js', ELASTIC_APM_SERVER_URL: serverUrl From 06290116a86972355a613d589d76958666c17564 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Fri, 14 Oct 2022 14:56:54 -0700 Subject: [PATCH 36/66] get the next.js version *after* npm ci, else the package.json isn't there yet --- test/instrumentation/modules/next/next.test.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/instrumentation/modules/next/next.test.js b/test/instrumentation/modules/next/next.test.js index 4cecab95cac..da044bb5cf6 100644 --- a/test/instrumentation/modules/next/next.test.js +++ b/test/instrumentation/modules/next/next.test.js @@ -58,12 +58,12 @@ if (process.env.ELASTIC_APM_CONTEXT_MANAGER === 'patch') { } const testAppDir = path.join(__dirname, 'a-nextjs-app') -const nextPj = require(path.join(testAppDir, 'node_modules/next/package.json')) // Match ANSI escapes (from https://stackoverflow.com/a/29497680/14444044). const ansiRe = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g /* eslint-disable-line no-control-regex */ let apmServer +let nextJsVersion // Determined after `npm ci` is run. let serverUrl // TEST_REQUESTS is an array of requests to test against both a prod-server and @@ -517,7 +517,7 @@ function checkExpectedApmEvents (t, apmEvents) { t.ok(evt.metadata, 'metadata is first event') t.equal(evt.metadata.service.name, 'a-nextjs-app', 'metadata.service.name') t.equal(evt.metadata.service.framework.name, 'Next.js', 'metadata.service.framework.name') - t.equal(evt.metadata.service.framework.version, nextPj.version, 'metadata.service.framework.version') + t.equal(evt.metadata.service.framework.version, nextJsVersion, 'metadata.service.framework.version') // Filter out any metadata from separate requests, and metricsets which we // aren't testing. @@ -573,6 +573,8 @@ if (!SKIP_NPM_CI_FOR_DEV) { } tape.test('setup: mock APM server', t => { + nextJsVersion = require(path.join(testAppDir, 'node_modules/next/package.json')).version + apmServer = new MockAPMServer({ apmServerVersion: '7.15.0' }) apmServer.start(function (serverUrl_) { serverUrl = serverUrl_ From f04b509b6b023ee75e5acd1e68a17f955c0a94fb Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Fri, 14 Oct 2022 15:23:57 -0700 Subject: [PATCH 37/66] get dependabot updates for a-nextjs-app test app --- .github/dependabot.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c9b2ebe3087..aaaa0476a4f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,3 +7,11 @@ updates: open-pull-requests-limit: 10 reviewers: - "elastic/apm-agent-node-js" + + - package-ecosystem: "npm" + directory: "/test/instrumentation/modules/next/a-nextjs-app" + schedule: + interval: "weekly" + open-pull-requests-limit: 5 + reviewers: + - "elastic/apm-agent-node-js" From 8bb630f1aed53b9036bff72979233371fd7812ed Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Mon, 17 Oct 2022 14:04:28 -0700 Subject: [PATCH 38/66] start a Next.js + APM example: this is the state after 'npx create-next-app@latest' --- docs/set-up.asciidoc | 5 +- docs/supported-technologies.asciidoc | 2 +- examples/nextjs/.eslintrc.json | 3 + examples/nextjs/.gitignore | 36 + examples/nextjs/README.md | 34 + examples/nextjs/next.config.js | 7 + examples/nextjs/package.json | 20 + examples/nextjs/pages/_app.js | 7 + examples/nextjs/pages/api/hello.js | 5 + examples/nextjs/pages/index.js | 69 + examples/nextjs/public/favicon.ico | Bin 0 -> 25931 bytes examples/nextjs/public/vercel.svg | 4 + examples/nextjs/styles/Home.module.css | 129 ++ examples/nextjs/styles/globals.css | 26 + examples/nextjs/yarn.lock | 1707 ++++++++++++++++++++++++ 15 files changed, 2052 insertions(+), 2 deletions(-) create mode 100644 examples/nextjs/.eslintrc.json create mode 100644 examples/nextjs/.gitignore create mode 100644 examples/nextjs/README.md create mode 100644 examples/nextjs/next.config.js create mode 100644 examples/nextjs/package.json create mode 100644 examples/nextjs/pages/_app.js create mode 100644 examples/nextjs/pages/api/hello.js create mode 100644 examples/nextjs/pages/index.js create mode 100644 examples/nextjs/public/favicon.ico create mode 100644 examples/nextjs/public/vercel.svg create mode 100644 examples/nextjs/styles/Home.module.css create mode 100644 examples/nextjs/styles/globals.css create mode 100644 examples/nextjs/yarn.lock diff --git a/docs/set-up.asciidoc b/docs/set-up.asciidoc index 992f928866e..c1c5bb938dc 100644 --- a/docs/set-up.asciidoc +++ b/docs/set-up.asciidoc @@ -13,6 +13,7 @@ To get you off the ground, we've prepared guides for setting up the Agent with a * <> * <> * <> +* <> // end::web-frameworks-list[] Alternatively, you can <>. @@ -42,6 +43,8 @@ include::./custom-stack.asciidoc[] include::./lambda.asciidoc[] +include::./nextjs.asciidoc[] + [[starting-the-agent]] === Starting the agent @@ -97,7 +100,7 @@ A limitation of this approach is that you cannot configure the agent with an opt [[start-option-node-require-opt]] ===== `node -r elastic-apm-node/start ...` -Another way to start the agent is with the `-r elastic-apm-node/start` https://nodejs.org/api/cli.html#-r---require-module[command line option to `node`]. This will import and start the APM agent before your application code starts. This method allows you to enable the agent _without touching any code_. This is the recommended start method for <>. +Another way to start the agent is with the `-r elastic-apm-node/start` https://nodejs.org/api/cli.html#-r---require-module[command line option to `node`]. This will import and start the APM agent before your application code starts. This method allows you to enable the agent _without touching any code_. This is the recommended start method for <> and for tracing <>. [source,bash] ---- diff --git a/docs/supported-technologies.asciidoc b/docs/supported-technologies.asciidoc index 825fe40b385..089c703599e 100644 --- a/docs/supported-technologies.asciidoc +++ b/docs/supported-technologies.asciidoc @@ -82,7 +82,6 @@ This agent is compatible with {apm-guide-ref}[APM Server] v6.6 and above. Though you can use Elastic APM <>, we automate a few things for the most popular Node.js modules. - These are the frameworks that we officially support: [options="header"] @@ -95,6 +94,7 @@ These are the frameworks that we officially support: |<> |>=5.2.0 |<> |>=1.0.0 | See also https://www.fastify.io/docs/latest/Reference/LTS/[Fastify's own LTS documentation] |<> |N/A +| <> | >=11.1.0 <13.0.0 | (Experimental) Names incoming HTTP transactions based on Next.js routing and reports errors in user pages. Supports the Next.js production server (`next start`) and development server (`next dev`). See the <>. |======================================================================= [float] diff --git a/examples/nextjs/.eslintrc.json b/examples/nextjs/.eslintrc.json new file mode 100644 index 00000000000..bffb357a712 --- /dev/null +++ b/examples/nextjs/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/examples/nextjs/.gitignore b/examples/nextjs/.gitignore new file mode 100644 index 00000000000..c87c9b392c0 --- /dev/null +++ b/examples/nextjs/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/examples/nextjs/README.md b/examples/nextjs/README.md new file mode 100644 index 00000000000..b12f3e33e7d --- /dev/null +++ b/examples/nextjs/README.md @@ -0,0 +1,34 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/examples/nextjs/next.config.js b/examples/nextjs/next.config.js new file mode 100644 index 00000000000..ae887958d3c --- /dev/null +++ b/examples/nextjs/next.config.js @@ -0,0 +1,7 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + swcMinify: true, +} + +module.exports = nextConfig diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json new file mode 100644 index 00000000000..be16f27d5b7 --- /dev/null +++ b/examples/nextjs/package.json @@ -0,0 +1,20 @@ +{ + "name": "my-app", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "next": "12.3.1", + "react": "18.2.0", + "react-dom": "18.2.0" + }, + "devDependencies": { + "eslint": "8.25.0", + "eslint-config-next": "12.3.1" + } +} diff --git a/examples/nextjs/pages/_app.js b/examples/nextjs/pages/_app.js new file mode 100644 index 00000000000..1e1cec92425 --- /dev/null +++ b/examples/nextjs/pages/_app.js @@ -0,0 +1,7 @@ +import '../styles/globals.css' + +function MyApp({ Component, pageProps }) { + return +} + +export default MyApp diff --git a/examples/nextjs/pages/api/hello.js b/examples/nextjs/pages/api/hello.js new file mode 100644 index 00000000000..df63de88fa6 --- /dev/null +++ b/examples/nextjs/pages/api/hello.js @@ -0,0 +1,5 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction + +export default function handler(req, res) { + res.status(200).json({ name: 'John Doe' }) +} diff --git a/examples/nextjs/pages/index.js b/examples/nextjs/pages/index.js new file mode 100644 index 00000000000..dc4b6403521 --- /dev/null +++ b/examples/nextjs/pages/index.js @@ -0,0 +1,69 @@ +import Head from 'next/head' +import Image from 'next/image' +import styles from '../styles/Home.module.css' + +export default function Home() { + return ( + + ) +} diff --git a/examples/nextjs/public/favicon.ico b/examples/nextjs/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/examples/nextjs/public/vercel.svg b/examples/nextjs/public/vercel.svg new file mode 100644 index 00000000000..fbf0e25a651 --- /dev/null +++ b/examples/nextjs/public/vercel.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/examples/nextjs/styles/Home.module.css b/examples/nextjs/styles/Home.module.css new file mode 100644 index 00000000000..bd50f42ffe6 --- /dev/null +++ b/examples/nextjs/styles/Home.module.css @@ -0,0 +1,129 @@ +.container { + padding: 0 2rem; +} + +.main { + min-height: 100vh; + padding: 4rem 0; + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.footer { + display: flex; + flex: 1; + padding: 2rem 0; + border-top: 1px solid #eaeaea; + justify-content: center; + align-items: center; +} + +.footer a { + display: flex; + justify-content: center; + align-items: center; + flex-grow: 1; +} + +.title a { + color: #0070f3; + text-decoration: none; +} + +.title a:hover, +.title a:focus, +.title a:active { + text-decoration: underline; +} + +.title { + margin: 0; + line-height: 1.15; + font-size: 4rem; +} + +.title, +.description { + text-align: center; +} + +.description { + margin: 4rem 0; + line-height: 1.5; + font-size: 1.5rem; +} + +.code { + background: #fafafa; + border-radius: 5px; + padding: 0.75rem; + font-size: 1.1rem; + font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, + Bitstream Vera Sans Mono, Courier New, monospace; +} + +.grid { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + max-width: 800px; +} + +.card { + margin: 1rem; + padding: 1.5rem; + text-align: left; + color: inherit; + text-decoration: none; + border: 1px solid #eaeaea; + border-radius: 10px; + transition: color 0.15s ease, border-color 0.15s ease; + max-width: 300px; +} + +.card:hover, +.card:focus, +.card:active { + color: #0070f3; + border-color: #0070f3; +} + +.card h2 { + margin: 0 0 1rem 0; + font-size: 1.5rem; +} + +.card p { + margin: 0; + font-size: 1.25rem; + line-height: 1.5; +} + +.logo { + height: 1em; + margin-left: 0.5rem; +} + +@media (max-width: 600px) { + .grid { + width: 100%; + flex-direction: column; + } +} + +@media (prefers-color-scheme: dark) { + .card, + .footer { + border-color: #222; + } + .code { + background: #111; + } + .logo img { + filter: invert(1); + } +} diff --git a/examples/nextjs/styles/globals.css b/examples/nextjs/styles/globals.css new file mode 100644 index 00000000000..4f1842163d2 --- /dev/null +++ b/examples/nextjs/styles/globals.css @@ -0,0 +1,26 @@ +html, +body { + padding: 0; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, + Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; +} + +a { + color: inherit; + text-decoration: none; +} + +* { + box-sizing: border-box; +} + +@media (prefers-color-scheme: dark) { + html { + color-scheme: dark; + } + body { + color: white; + background: black; + } +} diff --git a/examples/nextjs/yarn.lock b/examples/nextjs/yarn.lock new file mode 100644 index 00000000000..fb9f33fa941 --- /dev/null +++ b/examples/nextjs/yarn.lock @@ -0,0 +1,1707 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/runtime-corejs3@^7.10.2": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.19.4.tgz#870dbfd9685b3dad5aeb2d00841bb8b6192e3095" + integrity sha512-HzjQ8+dzdx7dmZy4DQ8KV8aHi/74AjEbBGTFutBmg/pd3dY5/q1sfuOGPTFGEytlQhWoeVXqcK5BwMgIkRkNDQ== + dependencies: + core-js-pure "^3.25.1" + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.10.2", "@babel/runtime@^7.18.9": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.4.tgz#a42f814502ee467d55b38dd1c256f53a7b885c78" + integrity sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA== + dependencies: + regenerator-runtime "^0.13.4" + +"@eslint/eslintrc@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" + integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.4.0" + globals "^13.15.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@humanwhocodes/config-array@^0.10.5": + version "0.10.7" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.7.tgz#6d53769fd0c222767e6452e8ebda825c22e9f0dc" + integrity sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@next/env@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/env/-/env-12.3.1.tgz#18266bd92de3b4aa4037b1927aa59e6f11879260" + integrity sha512-9P9THmRFVKGKt9DYqeC2aKIxm8rlvkK38V1P1sRE7qyoPBIs8l9oo79QoSdPtOWfzkbDAVUqvbQGgTMsb8BtJg== + +"@next/eslint-plugin-next@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.3.1.tgz#b821f27b0f175954d8d18e5d323fce040ecc79a6" + integrity sha512-sw+lTf6r6P0j+g/n9y4qdWWI2syPqZx+uc0+B/fRENqfR3KpSid6MIKqc9gNwGhJASazEQ5b3w8h4cAET213jw== + dependencies: + glob "7.1.7" + +"@next/swc-android-arm-eabi@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.1.tgz#b15ce8ad376102a3b8c0f3c017dde050a22bb1a3" + integrity sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ== + +"@next/swc-android-arm64@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.3.1.tgz#85d205f568a790a137cb3c3f720d961a2436ac9c" + integrity sha512-CmgU2ZNyBP0rkugOOqLnjl3+eRpXBzB/I2sjwcGZ7/Z6RcUJXK5Evz+N0ucOxqE4cZ3gkTeXtSzRrMK2mGYV8Q== + +"@next/swc-darwin-arm64@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.3.1.tgz#b105457d6760a7916b27e46c97cb1a40547114ae" + integrity sha512-hT/EBGNcu0ITiuWDYU9ur57Oa4LybD5DOQp4f22T6zLfpoBMfBibPtR8XktXmOyFHrL/6FC2p9ojdLZhWhvBHg== + +"@next/swc-darwin-x64@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.3.1.tgz#6947b39082271378896b095b6696a7791c6e32b1" + integrity sha512-9S6EVueCVCyGf2vuiLiGEHZCJcPAxglyckTZcEwLdJwozLqN0gtS0Eq0bQlGS3dH49Py/rQYpZ3KVWZ9BUf/WA== + +"@next/swc-freebsd-x64@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.3.1.tgz#2b6c36a4d84aae8b0ea0e0da9bafc696ae27085a" + integrity sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q== + +"@next/swc-linux-arm-gnueabihf@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.3.1.tgz#6e421c44285cfedac1f4631d5de330dd60b86298" + integrity sha512-diL9MSYrEI5nY2wc/h/DBewEDUzr/DqBjIgHJ3RUNtETAOB3spMNHvJk2XKUDjnQuluLmFMloet9tpEqU2TT9w== + +"@next/swc-linux-arm64-gnu@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.1.tgz#8863f08a81f422f910af126159d2cbb9552ef717" + integrity sha512-o/xB2nztoaC7jnXU3Q36vGgOolJpsGG8ETNjxM1VAPxRwM7FyGCPHOMk1XavG88QZSQf+1r+POBW0tLxQOJ9DQ== + +"@next/swc-linux-arm64-musl@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.1.tgz#0038f07cf0b259d70ae0c80890d826dfc775d9f3" + integrity sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg== + +"@next/swc-linux-x64-gnu@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.1.tgz#c66468f5e8181ffb096c537f0dbfb589baa6a9c1" + integrity sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA== + +"@next/swc-linux-x64-musl@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.1.tgz#c6269f3e96ac0395bc722ad97ce410ea5101d305" + integrity sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg== + +"@next/swc-win32-arm64-msvc@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.1.tgz#83c639ee969cee36ce247c3abd1d9df97b5ecade" + integrity sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw== + +"@next/swc-win32-ia32-msvc@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.1.tgz#52995748b92aa8ad053440301bc2c0d9fbcf27c2" + integrity sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA== + +"@next/swc-win32-x64-msvc@12.3.1": + version "12.3.1" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.1.tgz#27d71a95247a9eaee03d47adee7e3bd594514136" + integrity sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@rushstack/eslint-patch@^1.1.3": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728" + integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== + +"@swc/helpers@0.4.11": + version "0.4.11" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.11.tgz#db23a376761b3d31c26502122f349a21b592c8de" + integrity sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw== + dependencies: + tslib "^2.4.0" + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@typescript-eslint/parser@^5.21.0": + version "5.40.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.40.0.tgz#432bddc1fe9154945660f67c1ba6d44de5014840" + integrity sha512-Ah5gqyX2ySkiuYeOIDg7ap51/b63QgWZA7w6AHtFrag7aH0lRQPbLzUjk0c9o5/KZ6JRkTTDKShL4AUrQa6/hw== + dependencies: + "@typescript-eslint/scope-manager" "5.40.0" + "@typescript-eslint/types" "5.40.0" + "@typescript-eslint/typescript-estree" "5.40.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.40.0": + version "5.40.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.40.0.tgz#d6ea782c8e3a2371ba3ea31458dcbdc934668fc4" + integrity sha512-d3nPmjUeZtEWRvyReMI4I1MwPGC63E8pDoHy0BnrYjnJgilBD3hv7XOiETKLY/zTwI7kCnBDf2vWTRUVpYw0Uw== + dependencies: + "@typescript-eslint/types" "5.40.0" + "@typescript-eslint/visitor-keys" "5.40.0" + +"@typescript-eslint/types@5.40.0": + version "5.40.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.40.0.tgz#8de07e118a10b8f63c99e174a3860f75608c822e" + integrity sha512-V1KdQRTXsYpf1Y1fXCeZ+uhjW48Niiw0VGt4V8yzuaDTU8Z1Xl7yQDyQNqyAFcVhpYXIVCEuxSIWTsLDpHgTbw== + +"@typescript-eslint/typescript-estree@5.40.0": + version "5.40.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.40.0.tgz#e305e6a5d65226efa5471ee0f12e0ffaab6d3075" + integrity sha512-b0GYlDj8TLTOqwX7EGbw2gL5EXS2CPEWhF9nGJiGmEcmlpNBjyHsTwbqpyIEPVpl6br4UcBOYlcI2FJVtJkYhg== + dependencies: + "@typescript-eslint/types" "5.40.0" + "@typescript-eslint/visitor-keys" "5.40.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/visitor-keys@5.40.0": + version "5.40.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.40.0.tgz#dd2d38097f68e0d2e1e06cb9f73c0173aca54b68" + integrity sha512-ijJ+6yig+x9XplEpG2K6FUdJeQGGj/15U3S56W9IqXKJqleuD7zJ2AX/miLezwxpd7ZxDAqO87zWufKg+RPZyQ== + dependencies: + "@typescript-eslint/types" "5.40.0" + eslint-visitor-keys "^3.3.0" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.8.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" + integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + +ajv@^6.10.0, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +aria-query@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" + integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== + dependencies: + "@babel/runtime" "^7.10.2" + "@babel/runtime-corejs3" "^7.10.2" + +array-includes@^3.1.4, array-includes@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" + integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + get-intrinsic "^1.1.1" + is-string "^1.0.7" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.flat@^1.2.5: + version "1.3.0" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" + integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.2" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f" + integrity sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.2" + es-shim-unscopables "^1.0.0" + +ast-types-flow@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" + integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag== + +axe-core@^4.4.3: + version "4.4.3" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" + integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w== + +axobject-query@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" + integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +caniuse-lite@^1.0.30001406: + version "1.0.30001419" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001419.tgz#3542722d57d567c8210d5e4d0f9f17336b776457" + integrity sha512-aFO1r+g6R7TW+PNQxKzjITwLOyDhVRLjW0LcwS/HCZGUUKTGNp9+IwLC4xyDSZBygVL/mxaFR3HIV6wEKQuSzw== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +core-js-pure@^3.25.1: + version "3.25.5" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.25.5.tgz#79716ba54240c6aa9ceba6eee08cf79471ba184d" + integrity sha512-oml3M22pHM+igfWHDfdLVq2ShWmjM2V4L+dQEBs0DWVIqEm9WHCwGAlZ6BmyBQGy5sFrJmcx+856D9lVKyGWYg== + +cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +damerau-levenshtein@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" + integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== + +debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +define-properties@^1.1.3, define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: + version "1.20.4" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" + integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.3" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.2" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trimend "^1.0.5" + string.prototype.trimstart "^1.0.5" + unbox-primitive "^1.0.2" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-next@12.3.1: + version "12.3.1" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.3.1.tgz#5d4eb0b7903cea81fd0d5106601d3afb0a453ff4" + integrity sha512-EN/xwKPU6jz1G0Qi6Bd/BqMnHLyRAL0VsaQaWA7F3KkjAgZHi4f1uL1JKGWNxdQpHTW/sdGONBd0bzxUka/DJg== + dependencies: + "@next/eslint-plugin-next" "12.3.1" + "@rushstack/eslint-patch" "^1.1.3" + "@typescript-eslint/parser" "^5.21.0" + eslint-import-resolver-node "^0.3.6" + eslint-import-resolver-typescript "^2.7.1" + eslint-plugin-import "^2.26.0" + eslint-plugin-jsx-a11y "^6.5.1" + eslint-plugin-react "^7.31.7" + eslint-plugin-react-hooks "^4.5.0" + +eslint-import-resolver-node@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" + integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== + dependencies: + debug "^3.2.7" + resolve "^1.20.0" + +eslint-import-resolver-typescript@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz#a90a4a1c80da8d632df25994c4c5fdcdd02b8751" + integrity sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ== + dependencies: + debug "^4.3.4" + glob "^7.2.0" + is-glob "^4.0.3" + resolve "^1.22.0" + tsconfig-paths "^3.14.1" + +eslint-module-utils@^2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" + integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== + dependencies: + debug "^3.2.7" + +eslint-plugin-import@^2.26.0: + version "2.26.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" + integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== + dependencies: + array-includes "^3.1.4" + array.prototype.flat "^1.2.5" + debug "^2.6.9" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.6" + eslint-module-utils "^2.7.3" + has "^1.0.3" + is-core-module "^2.8.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.values "^1.1.5" + resolve "^1.22.0" + tsconfig-paths "^3.14.1" + +eslint-plugin-jsx-a11y@^6.5.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz#93736fc91b83fdc38cc8d115deedfc3091aef1ff" + integrity sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q== + dependencies: + "@babel/runtime" "^7.18.9" + aria-query "^4.2.2" + array-includes "^3.1.5" + ast-types-flow "^0.0.7" + axe-core "^4.4.3" + axobject-query "^2.2.0" + damerau-levenshtein "^1.0.8" + emoji-regex "^9.2.2" + has "^1.0.3" + jsx-ast-utils "^3.3.2" + language-tags "^1.0.5" + minimatch "^3.1.2" + semver "^6.3.0" + +eslint-plugin-react-hooks@^4.5.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" + integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== + +eslint-plugin-react@^7.31.7: + version "7.31.10" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz#6782c2c7fe91c09e715d536067644bbb9491419a" + integrity sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA== + dependencies: + array-includes "^3.1.5" + array.prototype.flatmap "^1.3.0" + doctrine "^2.1.0" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.5" + object.fromentries "^2.0.5" + object.hasown "^1.1.1" + object.values "^1.1.5" + prop-types "^15.8.1" + resolve "^2.0.0-next.3" + semver "^6.3.0" + string.prototype.matchall "^4.0.7" + +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint@8.25.0: + version "8.25.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.25.0.tgz#00eb962f50962165d0c4ee3327708315eaa8058b" + integrity sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A== + dependencies: + "@eslint/eslintrc" "^1.3.3" + "@humanwhocodes/config-array" "^0.10.5" + "@humanwhocodes/module-importer" "^1.0.1" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.4.0" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.1" + globals "^13.15.0" + globby "^11.1.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + +espree@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a" + integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw== + dependencies: + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" + +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9: + version "3.2.12" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" + integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@7.1.7: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.3, glob@^7.2.0: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^13.15.0: + version "13.17.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" + integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== + dependencies: + type-fest "^0.20.2" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.8.1, is-core-module@^2.9.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" + integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +js-sdsl@^4.1.4: + version "4.1.5" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.5.tgz#1ff1645e6b4d1b028cd3f862db88c9d887f26e2a" + integrity sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q== + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" + integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw== + dependencies: + array-includes "^3.1.5" + object.assign "^4.1.3" + +language-subtag-registry@~0.3.2: + version "0.3.22" + resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" + integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== + +language-tags@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a" + integrity sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ== + dependencies: + language-subtag-registry "~0.3.2" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +next@12.3.1: + version "12.3.1" + resolved "https://registry.yarnpkg.com/next/-/next-12.3.1.tgz#127b825ad2207faf869b33393ec8c75fe61e50f1" + integrity sha512-l7bvmSeIwX5lp07WtIiP9u2ytZMv7jIeB8iacR28PuUEFG5j0HGAPnMqyG5kbZNBG2H7tRsrQ4HCjuMOPnANZw== + dependencies: + "@next/env" "12.3.1" + "@swc/helpers" "0.4.11" + caniuse-lite "^1.0.30001406" + postcss "8.4.14" + styled-jsx "5.0.7" + use-sync-external-store "1.2.0" + optionalDependencies: + "@next/swc-android-arm-eabi" "12.3.1" + "@next/swc-android-arm64" "12.3.1" + "@next/swc-darwin-arm64" "12.3.1" + "@next/swc-darwin-x64" "12.3.1" + "@next/swc-freebsd-x64" "12.3.1" + "@next/swc-linux-arm-gnueabihf" "12.3.1" + "@next/swc-linux-arm64-gnu" "12.3.1" + "@next/swc-linux-arm64-musl" "12.3.1" + "@next/swc-linux-x64-gnu" "12.3.1" + "@next/swc-linux-x64-musl" "12.3.1" + "@next/swc-win32-arm64-msvc" "12.3.1" + "@next/swc-win32-ia32-msvc" "12.3.1" + "@next/swc-win32-x64-msvc" "12.3.1" + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.12.2, object-inspect@^1.9.0: + version "1.12.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" + integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.3, object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" + integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +object.fromentries@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" + integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +object.hasown@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3" + integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A== + dependencies: + define-properties "^1.1.4" + es-abstract "^1.19.5" + +object.values@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" + integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +postcss@8.4.14: + version "8.4.14" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" + integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +react-dom@18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react@18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + +regenerator-runtime@^0.13.4: + version "0.13.10" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" + integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw== + +regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + functions-have-names "^1.2.2" + +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^1.20.0, resolve@^1.22.0: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.3: + version "2.0.0-next.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" + integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.7: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +string.prototype.matchall@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" + integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + regexp.prototype.flags "^1.4.1" + side-channel "^1.0.4" + +string.prototype.trimend@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" + integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + +string.prototype.trimstart@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" + integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +styled-jsx@5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.7.tgz#be44afc53771b983769ac654d355ca8d019dff48" + integrity sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tsconfig-paths@^3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" + integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +use-sync-external-store@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 4186e29d23a84334e4f6c3296d6af942756e3194 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Mon, 17 Oct 2022 16:03:12 -0700 Subject: [PATCH 39/66] steps to add APM support to a Next.js app --- .eslintrc.json | 1 + examples/nextjs/README.md | 6 +- examples/nextjs/apmsetup.js | 34 +++ examples/nextjs/package.json | 5 +- examples/nextjs/yarn.lock | 524 ++++++++++++++++++++++++++++++++++- 5 files changed, 560 insertions(+), 10 deletions(-) create mode 100644 examples/nextjs/apmsetup.js diff --git a/.eslintrc.json b/.eslintrc.json index cc166de31fc..d7c877947d5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -16,6 +16,7 @@ "node_modules*", "/examples/esbuild/dist", "/examples/typescript/dist", + "/examples/nextjs", "/lib/opentelemetry-bridge/opentelemetry-core-mini", "/test/babel/out.js", "/test/lambda/fixtures/esbuild-bundled-handler/hello.js", diff --git a/examples/nextjs/README.md b/examples/nextjs/README.md index b12f3e33e7d..f97dd5a24b3 100644 --- a/examples/nextjs/README.md +++ b/examples/nextjs/README.md @@ -1,7 +1,11 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). +This is a [Next.js](https://nextjs.org/) project +1. bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app), and then +2. lightly modified to use Elastic APM to monitor the Next server. ## Getting Started +XXX clear this mostly out, relevant docs for learning Next and for getting started with APM + First, run the development server: ```bash diff --git a/examples/nextjs/apmsetup.js b/examples/nextjs/apmsetup.js new file mode 100644 index 00000000000..da6f60b6d9b --- /dev/null +++ b/examples/nextjs/apmsetup.js @@ -0,0 +1,34 @@ +'use strict' + +// Use this script with Node's `--require` flag to monitor a Next.js app with +// Elastic APM. + +// Support `ELASTIC_APM_...` envvars in ".env*" files as Next.js supports them. +// https://nextjs.org/docs/basic-features/environment-variables +try { + const { loadEnvConfig } = require('@next/env') + const isDev = process.argv[process.argv.length - 1] === 'dev' + loadEnvConfig(__dirname, isDev) +} catch (envErr) { + console.error('apmsetup: warning: failed to load @next/env to read possible .env files') +} + +if (!process.env.ELASTIC_APM_SERVER_URL) { + console.log('apmsetup: ELASTIC_APM_SERVER_URL is not set, disabling APM') +} else { + // APM agent configuration can be passed to `.start()` or specified as + // environment variables. + // https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html + const apm = require('elastic-apm-node').start() + + // Flush APM data on server process termination. + // https://nextjs.org/docs/deployment#manual-graceful-shutdowns + process.env.NEXT_MANUAL_SIG_HANDLE = 1 + function flushApmAndExit () { + apm.flush(() => { + process.exit(0) + }) + } + process.on('SIGTERM', flushApmAndExit) + process.on('SIGINT', flushApmAndExit) +} diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index be16f27d5b7..27c5a6da682 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -3,12 +3,13 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", + "dev": "NODE_OPTIONS=--require=./apmsetup.js next dev", "build": "next build", - "start": "next start", + "start": "NODE_OPTIONS=--require=./apmsetup.js next start", "lint": "next lint" }, "dependencies": { + "elastic-apm-node": "elastic/apm-agent-nodejs#trentm/feat-nextjs", "next": "12.3.1", "react": "18.2.0", "react-dom": "18.2.0" diff --git a/examples/nextjs/yarn.lock b/examples/nextjs/yarn.lock index fb9f33fa941..69ab30edfe0 100644 --- a/examples/nextjs/yarn.lock +++ b/examples/nextjs/yarn.lock @@ -17,6 +17,20 @@ dependencies: regenerator-runtime "^0.13.4" +"@elastic/ecs-helpers@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@elastic/ecs-helpers/-/ecs-helpers-1.1.0.tgz#ee7e6f870f75a2222c5d7179b36a628f1db4779e" + integrity sha512-MDLb2aFeGjg46O5mLpdCzT5yOUDnXToJSrco2ShqGIXxNJaM8uJjX+4nd+hRYV4Vex8YJyDtOFEVBldQct6ndg== + dependencies: + fast-json-stringify "^2.4.1" + +"@elastic/ecs-pino-format@^1.2.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@elastic/ecs-pino-format/-/ecs-pino-format-1.3.0.tgz#6e349a7da342b3c370d15361ba7f850bc2f783bc" + integrity sha512-U8D57gPECYoRCcwREsrXKBtqeyFFF/KAwHi4rG1u/oQhAg91Kzw8ZtUQJXD/DMDieLOqtbItFr2FRBWI3t3wog== + dependencies: + "@elastic/ecs-helpers" "^1.1.0" + "@eslint/eslintrc@^1.3.3": version "1.3.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" @@ -149,6 +163,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@opentelemetry/api@^1.1.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.2.0.tgz#89ef99401cde6208cff98760b67663726ef26686" + integrity sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g== + "@rushstack/eslint-patch@^1.1.3": version "1.2.0" resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728" @@ -220,7 +239,21 @@ acorn@^8.8.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== -ajv@^6.10.0, ajv@^6.12.4: +after-all-results@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/after-all-results/-/after-all-results-2.0.0.tgz#6ac2fc202b500f88da8f4f5530cfa100f4c6a2d0" + integrity sha512-2zHEyuhSJOuCrmas9YV0YL/MFCWLxe1dS6k/ENhgYrb/JqyMnadLN4iIAc9kkZrbElMDyyAGH/0J18OPErOWLg== + +agentkeepalive@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" + integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== + dependencies: + debug "^4.1.0" + depd "^1.1.2" + humanize-ms "^1.2.1" + +ajv@^6.10.0, ajv@^6.11.0, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -296,6 +329,30 @@ ast-types-flow@^0.0.7: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag== +async-cache@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/async-cache/-/async-cache-1.1.0.tgz#4a9a5a89d065ec5d8e5254bd9ee96ba76c532b5a" + integrity sha512-YDQc4vBn5NFhY6g6HhVshyi3Fy9+SQ5ePnE7JLDJn1DoL+i7ER+vMwtTNOYk9leZkYMnOwpBCWqyLDPw8Aig8g== + dependencies: + lru-cache "^4.0.0" + +async-value-promise@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/async-value-promise/-/async-value-promise-1.1.1.tgz#68957819e3eace804f3b4b69477e2bd276c15378" + integrity sha512-c2RFDKjJle1rHa0YxN9Ysu97/QBu3Wa+NOejJxsX+1qVDJrkD3JL/GN1B3gaILAEXJXbu/4Z1lcoCHFESe/APA== + dependencies: + async-value "^1.2.2" + +async-value@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/async-value/-/async-value-1.2.2.tgz#84517a1e7cb6b1a5b5e181fa31be10437b7fb125" + integrity sha512-8rwtYe32OAS1W9CTwvknoyts+mc3ta8N7Pi0h7AjkMaKvsFbr39K+gEfZ7Z81aPXQ1sK5M23lgLy1QfZpcpadQ== + +atomic-sleep@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== + axe-core@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" @@ -311,6 +368,18 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +basic-auth@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + +binary-search@^1.3.3: + version "1.3.6" + resolved "https://registry.yarnpkg.com/binary-search/-/binary-search-1.3.6.tgz#e32426016a0c5092f0f3598836a1c7da3560565c" + integrity sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -326,6 +395,13 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" +breadth-filter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/breadth-filter/-/breadth-filter-2.0.0.tgz#7b3f8737f46ba1946aec19355ecf5df2bdb7e47c" + integrity sha512-thQShDXnFWSk2oVBixRCyrWsFoV5tfOpWKHmxwafHQDNxCfDBk539utpvytNjmlFrTMqz41poLwJvA1MW3z0MQ== + dependencies: + object.entries "^1.0.4" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -369,11 +445,31 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +console-log-level@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/console-log-level/-/console-log-level-1.4.1.tgz#9c5a6bb9ef1ef65b05aba83028b0ff894cdf630a" + integrity sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ== + +container-info@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/container-info/-/container-info-1.1.0.tgz#6fcb94e93eacd397c6316ca2834491ede44e55ee" + integrity sha512-eD2zLAmxGS2kmL4f1jY8BdOqnmpL6X70kvzTBW/9FIQnxoxiBJ4htMsTmtPLPWRs7NHYFvqKQ1VtppV08mdsQA== + +cookie@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + core-js-pure@^3.25.1: version "3.25.5" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.25.5.tgz#79716ba54240c6aa9ceba6eee08cf79471ba184d" integrity sha512-oml3M22pHM+igfWHDfdLVq2ShWmjM2V4L+dQEBs0DWVIqEm9WHCwGAlZ6BmyBQGy5sFrJmcx+856D9lVKyGWYg== +core-util-is@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -402,7 +498,7 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: +debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -414,6 +510,11 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + define-properties@^1.1.3, define-properties@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" @@ -422,6 +523,11 @@ define-properties@^1.1.3, define-properties@^1.1.4: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +depd@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -443,11 +549,83 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +elastic-apm-http-client@11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/elastic-apm-http-client/-/elastic-apm-http-client-11.0.1.tgz#15dbe99d56d62b3f732d1bd2b51bef6094b78801" + integrity sha512-5AOWlhs2WlZpI+DfgGqY/8Rk7KF8WeevaO8R961eBylavU6GWhLRNiJncohn5jsvrqhmeT19azBvy/oYRN7bJw== + dependencies: + agentkeepalive "^4.2.1" + breadth-filter "^2.0.0" + container-info "^1.0.1" + end-of-stream "^1.4.4" + fast-safe-stringify "^2.0.7" + fast-stream-to-buffer "^1.0.0" + object-filter-sequence "^1.0.0" + readable-stream "^3.4.0" + semver "^6.3.0" + stream-chopper "^3.0.1" + +elastic-apm-node@elastic/apm-agent-nodejs#trentm/feat-nextjs: + version "3.38.0" + resolved "https://codeload.github.com/elastic/apm-agent-nodejs/tar.gz/06290116a86972355a613d589d76958666c17564" + dependencies: + "@elastic/ecs-pino-format" "^1.2.0" + "@opentelemetry/api" "^1.1.0" + after-all-results "^2.0.0" + async-cache "^1.1.0" + async-value-promise "^1.1.1" + basic-auth "^2.0.1" + cookie "^0.5.0" + core-util-is "^1.0.2" + elastic-apm-http-client "11.0.1" + end-of-stream "^1.4.4" + error-callsites "^2.0.4" + error-stack-parser "^2.0.6" + escape-string-regexp "^4.0.0" + fast-safe-stringify "^2.0.7" + http-headers "^3.0.2" + is-native "^1.0.1" + lru-cache "^6.0.0" + measured-reporting "^1.51.1" + monitor-event-loop-delay "^1.0.0" + object-filter-sequence "^1.0.0" + object-identity-map "^1.0.2" + original-url "^1.2.3" + pino "^6.11.2" + relative-microtime "^2.0.0" + require-in-the-middle "^5.2.0" + semver "^6.3.0" + set-cookie-serde "^1.0.0" + shallow-clone-shim "^2.0.0" + source-map "^0.8.0-beta.0" + sql-summary "^1.0.1" + traverse "^0.6.6" + unicode-byte-truncate "^1.0.0" + emoji-regex@^9.2.2: version "9.2.2" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +end-of-stream@^1.4.1, end-of-stream@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +error-callsites@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/error-callsites/-/error-callsites-2.0.4.tgz#44f09e6a201e9a1603ead81eacac5ba258fca76e" + integrity sha512-V877Ch4FC4FN178fDK1fsrHN4I1YQIBdtjKrHh3BUHMnh3SMvwUVrqkaOgDpUuevgSNna0RBq6Ox9SGlxYrigA== + +error-stack-parser@^2.0.6: + version "2.1.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== + dependencies: + stackframe "^1.3.4" + es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: version "1.20.4" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" @@ -726,11 +904,38 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fast-json-stringify@^2.4.1: + version "2.7.13" + resolved "https://registry.yarnpkg.com/fast-json-stringify/-/fast-json-stringify-2.7.13.tgz#277aa86c2acba4d9851bd6108ed657aa327ed8c0" + integrity sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA== + dependencies: + ajv "^6.11.0" + deepmerge "^4.2.2" + rfdc "^1.2.0" + string-similarity "^4.0.1" + fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-redact@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.1.2.tgz#d58e69e9084ce9fa4c1a6fa98a3e1ecf5d7839aa" + integrity sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw== + +fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.0.8: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + +fast-stream-to-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-stream-to-buffer/-/fast-stream-to-buffer-1.0.0.tgz#793340cc753e7ec9c7fb6d57a53a0b911cb0f588" + integrity sha512-bI/544WUQlD2iXBibQbOMSmG07Hay7YrpXlKaeGTPT7H7pC0eitt3usak5vUwEvCGK/O7rUAM3iyQValGU22TQ== + dependencies: + end-of-stream "^1.4.1" + fastq@^1.6.0: version "1.13.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" @@ -768,11 +973,21 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" +flatstr@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.12.tgz#c2ba6a08173edbb6c9640e3055b95e287ceb5931" + integrity sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw== + flatted@^3.1.0: version "3.2.7" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +forwarded-parse@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/forwarded-parse/-/forwarded-parse-2.1.2.tgz#08511eddaaa2ddfd56ba11138eee7df117a09325" + integrity sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -913,6 +1128,20 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +http-headers@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/http-headers/-/http-headers-3.0.2.tgz#5147771292f0b39d6778d930a3a59a76fc7ef44d" + integrity sha512-87E1I+2Wg4dxxz4rcxElo3dxO/w1ZtgL1yA0Sb6vH3qU16vRKq1NjWQv9SCY3ly2OQROcoxHZOUpmelS+k6wOw== + dependencies: + next-line "^1.1.0" + +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== + dependencies: + ms "^2.0.0" + ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" @@ -939,7 +1168,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -992,6 +1221,11 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -999,11 +1233,31 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" +is-integer@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-integer/-/is-integer-1.0.7.tgz#6bde81aacddf78b659b6629d629cadc51a886d5c" + integrity sha512-RPQc/s9yBHSvpi+hs9dYiJ2cuFeU6x3TyyIp8O2H6SKEltIvJOzRj9ToyvcStDvPR/pS4rxgr1oBFajQjZ2Szg== + dependencies: + is-finite "^1.0.0" + +is-native@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-native/-/is-native-1.0.1.tgz#cd18cc162e8450d683b5babe79ac99c145449675" + integrity sha512-I4z9hx+4u3/zyvpvGtAR+n7SodJugE+i2jiS8yfq1A9QAZY0KldLQz0SBptLC9ti7kBlpghWUwTKE2BA62eCcw== + dependencies: + is-nil "^1.0.0" + to-source-code "^1.0.0" + is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== +is-nil@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-nil/-/is-nil-1.0.1.tgz#2daba29e0b585063875e7b539d071f5b15937969" + integrity sha512-m2Rm8PhUFDNNhgvwZJjJG74a9h5CHU0fkA8WT+WGlCjyEbZ2jPwgb+ZxHu4np284EqNVyOsgppReK4qy/TwEwg== + is-number-object@^1.0.4: version "1.0.7" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" @@ -1131,6 +1385,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== + loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -1138,6 +1397,14 @@ loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lru-cache@^4.0.0: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -1145,6 +1412,29 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +mapcap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mapcap/-/mapcap-1.0.0.tgz#e8e29d04a160eaf8c92ec4bcbd2c5d07ed037e5a" + integrity sha512-KcNlZSlFPx+r1jYZmxEbTVymG+dIctf10WmWkuhrhrblM+KMoF77HelwihL5cxYlORye79KoR4IlOOk99lUJ0g== + +measured-core@^1.51.1: + version "1.51.1" + resolved "https://registry.yarnpkg.com/measured-core/-/measured-core-1.51.1.tgz#98989705c00bfb0d8a20e665a9f8d6e246a40518" + integrity sha512-DZQP9SEwdqqYRvT2slMK81D/7xwdxXosZZBtLVfPSo6y5P672FBTbzHVdN4IQyUkUpcVOR9pIvtUy5Ryl7NKyg== + dependencies: + binary-search "^1.3.3" + optional-js "^2.0.0" + +measured-reporting@^1.51.1: + version "1.51.1" + resolved "https://registry.yarnpkg.com/measured-reporting/-/measured-reporting-1.51.1.tgz#6aeb209ad55edf3940e8afa75c8f97f541216b31" + integrity sha512-JCt+2u6XT1I5lG3SuYqywE0e62DJuAzBcfMzWGUhIYtPQV2Vm4HiYt/durqmzsAbZV181CEs+o/jMKWJKkYIWw== + dependencies: + console-log-level "^1.4.1" + mapcap "^1.0.0" + measured-core "^1.51.1" + optional-js "^2.0.0" + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -1170,6 +1460,16 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + +monitor-event-loop-delay@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/monitor-event-loop-delay/-/monitor-event-loop-delay-1.0.0.tgz#b5ab78165a3bb93f2b275c50d01430c7f155d1f7" + integrity sha512-YRIr1exCIfBDLZle8WHOfSo7Xg3M+phcZfq9Fx1L6Abo+atGp7cge5pM7PjyBn4s1oZI/BRD4EMrzQBbPpVb5Q== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -1180,7 +1480,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1: +ms@^2.0.0, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -1195,6 +1495,11 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +next-line@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-line/-/next-line-1.1.0.tgz#fcae57853052b6a9bae8208e40dd7d3c2d304603" + integrity sha512-+I10J3wKNoKddNxn0CNpoZ3eTZuqxjNM3b1GImVx22+ePI+Y15P8g/j3WsbP0fhzzrFzrtjOAoq5NCCucswXOQ== + next@12.3.1: version "12.3.1" resolved "https://registry.yarnpkg.com/next/-/next-12.3.1.tgz#127b825ad2207faf869b33393ec8c75fe61e50f1" @@ -1226,6 +1531,18 @@ object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== +object-filter-sequence@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/object-filter-sequence/-/object-filter-sequence-1.0.0.tgz#10bb05402fff100082b80d7e83991b10db411692" + integrity sha512-CsubGNxhIEChNY4cXYuA6KXafztzHqzLLZ/y3Kasf3A+sa3lL9thq3z+7o0pZqzEinjXT6lXDPAfVWI59dUyzQ== + +object-identity-map@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/object-identity-map/-/object-identity-map-1.0.2.tgz#2b4213a4285ca3a8cd2e696782c9964f887524e7" + integrity sha512-a2XZDGyYTngvGS67kWnqVdpoaJWsY7C1GhPJvejWAFCsUioTAaiTu8oBad7c6cI4McZxr4CmvnZeycK05iav5A== + dependencies: + object.entries "^1.1.0" + object-inspect@^1.12.2, object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" @@ -1246,7 +1563,7 @@ object.assign@^4.1.3, object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.5: +object.entries@^1.0.4, object.entries@^1.1.0, object.entries@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== @@ -1281,13 +1598,18 @@ object.values@^1.1.5: define-properties "^1.1.3" es-abstract "^1.19.1" -once@^1.3.0: +once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" +optional-js@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/optional-js/-/optional-js-2.3.0.tgz#81d54c4719afa8845b988143643a5148f9d89490" + integrity sha512-B0LLi+Vg+eko++0z/b8zIv57kp7HKEzaPJo7LowJXMUKYdf+3XJGu/cw03h/JhIOsLnP+cG5QnTHAuicjA5fMw== + optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -1300,6 +1622,13 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +original-url@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/original-url/-/original-url-1.2.3.tgz#133aff4b2d27e38a98d736f7629c56262b7153e1" + integrity sha512-BYm+pKYLtS4mVe/mgT3YKGtWV5HzN/XKiaIu1aK4rsxyjuHeTW9N+xVBEpJcY1onB3nccfH0RbzUEoimMqFUHQ== + dependencies: + forwarded-parse "^2.1.0" + p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -1356,6 +1685,24 @@ picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pino-std-serializers@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz#b56487c402d882eb96cd67c257868016b61ad671" + integrity sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg== + +pino@^6.11.2: + version "6.14.0" + resolved "https://registry.yarnpkg.com/pino/-/pino-6.14.0.tgz#b745ea87a99a6c4c9b374e4f29ca7910d4c69f78" + integrity sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg== + dependencies: + fast-redact "^3.0.0" + fast-safe-stringify "^2.0.8" + flatstr "^1.0.12" + pino-std-serializers "^3.1.0" + process-warning "^1.0.0" + quick-format-unescaped "^4.0.3" + sonic-boom "^1.0.2" + postcss@8.4.14: version "8.4.14" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" @@ -1370,6 +1717,11 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +process-warning@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" + integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== + prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" @@ -1379,6 +1731,11 @@ prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== + punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -1389,6 +1746,11 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-format-unescaped@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" + integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== + react-dom@18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" @@ -1409,6 +1771,15 @@ react@18.2.0: dependencies: loose-envify "^1.1.0" +readable-stream@^3.0.6, readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + regenerator-runtime@^0.13.4: version "0.13.10" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" @@ -1428,12 +1799,26 @@ regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== +relative-microtime@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/relative-microtime/-/relative-microtime-2.0.0.tgz#cceed2af095ecd72ea32011279c79e5fcc7de29b" + integrity sha512-l18ha6HEZc+No/uK4GyAnNxgKW7nvEe35IaeN54sShMojtqik2a6GbTyuiezkjpPaqP874Z3lW5ysBo5irz4NA== + +require-in-the-middle@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.2.0.tgz#4b71e3cc7f59977100af9beb76bf2d056a5a6de2" + integrity sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.20.0, resolve@^1.22.0: +resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -1456,6 +1841,11 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rfdc@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -1470,6 +1860,16 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-regex-test@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" @@ -1498,6 +1898,16 @@ semver@^7.3.7: dependencies: lru-cache "^6.0.0" +set-cookie-serde@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/set-cookie-serde/-/set-cookie-serde-1.0.0.tgz#bcf9c260ed2212ac4005a53eacbaaa37c07ac452" + integrity sha512-Vq8e5GsupfJ7okHIvEPcfs5neCo7MZ1ZuWrO3sllYi3DOWt6bSSCpADzqXjz3k0fXehnoFIrmmhty9IN6U6BXQ== + +shallow-clone-shim@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shallow-clone-shim/-/shallow-clone-shim-2.0.0.tgz#b62bf55aed79f4c1430ea1dc4d293a193f52cf91" + integrity sha512-YRNymdiL3KGOoS67d73TEmk4tdPTO9GSMCoiphQsTcC9EtC+AOmMPjkyBkRoCJfW9ASsaZw1craaiw1dPN2D3Q== + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -1524,11 +1934,48 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +sonic-boom@^1.0.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-1.4.1.tgz#d35d6a74076624f12e6f917ade7b9d75e918f53e" + integrity sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg== + dependencies: + atomic-sleep "^1.0.0" + flatstr "^1.0.12" + source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +source-map@^0.8.0-beta.0: + version "0.8.0-beta.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" + integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== + dependencies: + whatwg-url "^7.0.0" + +sql-summary@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sql-summary/-/sql-summary-1.0.1.tgz#a2dddb5435bae294eb11424a7330dc5bafe09c2b" + integrity sha512-IpCr2tpnNkP3Jera4ncexsZUp0enJBLr+pHCyTweMUBrbJsTgQeLWx1FXLhoBj/MvcnUQpkgOn2EY8FKOkUzww== + +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== + +stream-chopper@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/stream-chopper/-/stream-chopper-3.0.1.tgz#73791ae7bf954c297d6683aec178648efc61dd75" + integrity sha512-f7h+ly8baAE26iIjcp3VbnBkbIRGtrvV0X0xxFM/d7fwLTYnLzDPTXRKNxa2HZzohOrc96NTrR+FaV3mzOelNA== + dependencies: + readable-stream "^3.0.6" + +string-similarity@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.4.tgz#42d01ab0b34660ea8a018da8f56a3309bb8b2a5b" + integrity sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ== + string.prototype.matchall@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" @@ -1561,6 +2008,13 @@ string.prototype.trimstart@^1.0.5: define-properties "^1.1.4" es-abstract "^1.19.5" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -1607,6 +2061,25 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +to-source-code@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/to-source-code/-/to-source-code-1.0.2.tgz#dd136bdb1e1dbd80bbeacf088992678e9070bfea" + integrity sha512-YzWtjmNIf3E75eZYa7m1SCyl0vgOGoTzdpH3svfa8SUm5rqTgl9hnDolrAGOghCF9P2gsITXQoMrlujOoz+RPw== + dependencies: + is-nil "^1.0.0" + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA== + dependencies: + punycode "^2.1.0" + +traverse@^0.6.6: + version "0.6.7" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.7.tgz#46961cd2d57dd8706c36664acde06a248f1173fe" + integrity sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg== + tsconfig-paths@^3.14.1: version "3.14.1" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" @@ -1656,6 +2129,19 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +unicode-byte-truncate@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unicode-byte-truncate/-/unicode-byte-truncate-1.0.0.tgz#aa6f0f3475193fe20c320ac9213e36e62e8764a7" + integrity sha512-GQgHk6DodEoKddKQdjnv7xKS9G09XCfHWX0R4RKht+EbUMSiVEmtWHGFO8HUm+6NvWik3E2/DG4MxTitOLL64A== + dependencies: + is-integer "^1.0.6" + unicode-substring "^0.1.0" + +unicode-substring@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/unicode-substring/-/unicode-substring-0.1.0.tgz#6120ce3c390385dbcd0f60c32b9065c4181d4b36" + integrity sha512-36Xaw9wXi7MB/3/EQZZHkZyyiRNa9i3k9YtPAz2KfqMVH2xutdXyMHn4Igarmnvr+wOrfWa/6njhY+jPpXN2EQ== + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -1668,6 +2154,25 @@ use-sync-external-store@1.2.0: resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -1696,6 +2201,11 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" From 4b9d8c71b650fc171aca3bfad507f2c3606f8b1a Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Mon, 17 Oct 2022 22:33:23 -0700 Subject: [PATCH 40/66] first crack at Next.js docs --- docs/nextjs.asciidoc | 217 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 docs/nextjs.asciidoc diff --git a/docs/nextjs.asciidoc b/docs/nextjs.asciidoc new file mode 100644 index 00000000000..c1d3f3a79d7 --- /dev/null +++ b/docs/nextjs.asciidoc @@ -0,0 +1,217 @@ +:framework: Next.js + +[[nextjs]] + +ifdef::env-github[] +NOTE: For the best reading experience, +please view this documentation at https://www.elastic.co/guide/en/apm/agent/nodejs/current/nextjs.html[elastic.co] +endif::[] + +=== Get started with Next.js + +The Elastic APM Node.js agent can be used to trace the Next.js server (`next +start` or `next dev`) that runs your application without the need for code +changes to your app. APM transactions for incoming HTTP requests to the server +will be named for the https://nextjs.org/docs/routing/introduction[pages] and +https://nextjs.org/docs/api-routes/introduction[API endpoints] in your +application, as well as for internal routes used by Next.js. Errors in +application code run on the server will be reported for viewing in the Kibana +APM app. + +[float] +[[nextjs-status]] +==== Current status + +The Next.js instrumentation is *experimental* while we solicit feedback from +Next.js users. How the instrumentation works might change significantly in +future versions of the agent. + +If you are a Next.js user, please help us provide a better Next.js observability +experience with your feedback on our +https://discuss.elastic.co/tags/c/apm/nodejs[Discuss forum]. + + +[float] +[[nextjs-prerequisites]] +==== Prerequisites + +You need an APM Server to send APM data to. Follow the {apm-guide-ref}/apm-quick-start.html[APM Quick start] if you have not set one up yet. You will need your *APM server url* and an APM server *secret token* (or *API key*) for configuring the APM agent below. + +You will also need a Next.js application to monitor. If you do not have an existing one to use, you can use the following to create a starter app (see https://nextjs.org/docs/getting-started[Next.js Getting Started docs] for more): + +[source,bash] +---- +npx create-next-app@latest # use the defaults +cd my-app +---- + +// XXX update link + +You can also take a look at and use this https://github.com/elastic/apm-agent-nodejs/tree/trentm/feat-nextjs/examples/nextjs/[Next.js + Elastic APM example]. + + +[float] +[[nextjs-setup]] +==== Step 1: Add the APM agent dependency + +Add the `elastic-apm-node` module as a dependency to your application: + +[source,bash] +---- +npm install elastic-apm-node --save # or 'yarn add elastic-apm-node' +---- + +NOTE: XXX Until this is in a release use `npm install "elastic-apm-node@elastic/apm-agent-nodejs#trentm/feat-nextjs"`. + + +[float] +==== Step 2: Start the APM agent + +For the APM agent to instrument the Next.js server, it needs to be started before the Next.js server code is loaded. The best way to do so is by using Node's https://nodejs.org/api/cli.html#-r---require-module[`--require`] option. We could use <> to start the APM agent. However, to integrate a little better with Next.js, we will create a separate "apmsetup.js" file which does a little more than just start the agent. + +Edit the "dev" and "start" scripts in your "package.json" to tell Node to require the "apmsetup.js" module on startup. Like this: + +[source,diff] +---- +diff --git a/package.json b/package.json +--- a/package.json ++++ b/package.json +@@ -3,9 +3,9 @@ + "version": "0.1.0", + "private": true, + "scripts": { +- "dev": "next dev", ++ "dev": "NODE_OPTIONS=--require=./apmsetup.js next dev", + "build": "next build", +- "start": "next start", ++ "start": "NODE_OPTIONS=--require=./apmsetup.js next start", + "lint": "next lint" + }, + "dependencies": { +---- + +Then save the following as "apmsetup.js": + +[source,javascript] +---- +'use strict' + +// Use this script with Node's `--require` flag to monitor a Next.js app with +// Elastic APM. + +// Support `ELASTIC_APM_...` envvars in ".env*" files as Next.js supports them. +// https://nextjs.org/docs/basic-features/environment-variables +try { + const { loadEnvConfig } = require('@next/env') + const isDev = process.argv[process.argv.length - 1] === 'dev' + loadEnvConfig(__dirname, isDev) // <1> +} catch (envErr) { + console.error('apmsetup: warning: failed to load @next/env to read possible .env files') +} + +if (!process.env.ELASTIC_APM_SERVER_URL) { + console.log('apmsetup: ELASTIC_APM_SERVER_URL is not set, disabling APM') +} else { + // APM agent configuration can be passed to `.start()` or specified as + // environment variables. + // https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html + const apm = require('elastic-apm-node').start() // <2> + + // Flush APM data on server process termination. + // https://nextjs.org/docs/deployment#manual-graceful-shutdowns + process.env.NEXT_MANUAL_SIG_HANDLE = 1 // <3> + function flushApmAndExit () { + apm.flush(() => { + process.exit(0) + }) + } + process.on('SIGTERM', flushApmAndExit) + process.on('SIGINT', flushApmAndExit) +} +---- +<1> Read environment variables from ".env*" files https://nextjs.org/docs/basic-features/environment-variables[in the same way as the Next.js server]. +<2> If an Elastic APM server URL is configured, then start the APM agent. +<3> Handle flushing any final APM tracing data on https://nextjs.org/docs/deployment#manual-graceful-shutdowns[graceful termination of the server]. + + +[float] +==== Step 3: Configure the APM agent + +The "apmsetup.js" module supports ".env*" files, so a natural way to configure the APM agent is using those. Create a ".env.local" file with the APM server url and secret token from the <> section above: + +[source,bash] +---- +# .env.local +ELASTIC_APM_SERVER_URL=https://... # E.g. https://my-deployment-name.apm.us-west2.gcp.elastic-cloud.com +ELASTIC_APM_SECRET_TOKEN='...' +---- + +See https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html[the APM configuration guide] for supported configuration variables. See https://nextjs.org/docs/basic-features/environment-variables[the Next.js docs] for more on its support for ".env" files. + + +[float] +==== Step 4: Start your Next.js app and verify monitoring is working + +[source,bash] +---- +npm run dev # or 'npm run build && npm start' for the production server +---- + +Open in your browser to load your Next.js app. If you used the `create-next-app` tool above, it creates a [http://localhost:3000/api/hello]/api/hello API endpoint. You can provide some artificial load by running the following in a separate terminal: + +[source,bash] +---- +while true; do sleep 1; curl -i http://localhost:3000/api/hello; done +---- + +Visit your Kibana APM app and, after a few seconds, you should see a service entry for your Next.js app. (The service name will be pulled from the "name" field in "package.json". It can be overriden with https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html#service-name[`ELASTIC_APM_SERVICE_NAME`].) + +XXX screenshot (can we add screenshots to docs, or too brittle for UI changes?) + + +[float] +[[nextjs-limitations]] +==== Next.js instrumentation limitations + +XXX limitations + +XXX feedback request + +[float] +[[nextjs-performance-monitoring]] +==== Performance monitoring + +include::./shared-set-up.asciidoc[tag=performance-monitoring] + +[float] +[[nextjs-unknown-routes]] +===== Unknown routes + +include::./shared-set-up.asciidoc[tag=unknown-roots] + +XXX Note the limitation on the Next.js catchall route. + + +[float] +[[nextjs-error-logging]] +==== Error logging + +include::./shared-set-up.asciidoc[tag=error-logging] + +[float] +[[nextjs-filter-sensitive-information]] +==== Filter sensitive information + +include::./shared-set-up.asciidoc[tag=filter-sensitive-info] + +[float] +[[nextjs-compatibility]] +==== Compatibility + +include::./shared-set-up.asciidoc[tag=compatibility-link] + +[float] +[[nextjs-troubleshooting]] +==== Troubleshooting + +include::./shared-set-up.asciidoc[tag=troubleshooting-link] From b47fb16806314a36daad88022f992d4c346c739a Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Mon, 17 Oct 2022 22:44:31 -0700 Subject: [PATCH 41/66] add a screenshot --- docs/images/nextjs-my-app-screenshot.png | Bin 0 -> 129502 bytes docs/nextjs.asciidoc | 2 ++ 2 files changed, 2 insertions(+) create mode 100644 docs/images/nextjs-my-app-screenshot.png diff --git a/docs/images/nextjs-my-app-screenshot.png b/docs/images/nextjs-my-app-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..f2bdc6eb183c57f88f5bb02081dff0ba12e86cd3 GIT binary patch literal 129502 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

=s&jCi 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 z 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�`GJ5r92!N*mut6bKdk5FZa+CRPzPFKPEd==<4$A6u=|B`ZQcv@NphWN zRcK_d4na8{&32mBUdiV~2elI5yt+?&tJ@P9!LDW5@mUh(DI|xSzV|_js!H!O+v%+h z8*D*ox=&01J(kaTw_eX{5)@yDNsCC!n;WUZg4cjek@2Z#vJq?~K);QA}jWP(J0AeS)bL zS3R;qGRMam`yWi}&K%1eRIm*~1eiCEM`n~Jl$FcwjbjIjhisTNHth3kQ(u2tf}v0N zKs>X;=_$&^dGtz9C|u&~qPiFZAGF)c&^B|jG4PPn^2&qxnH9k1jr~2OaDGg+3CBIJ z3tzrlk5E+eUOpa`wb}WUtIl0dQSod?$b{I3F?OP#RVzc3S$49{t8_d|ysl4=H#QtI zcS~F^E-LILW6b+*PR66ZDY7^YRF{2^xBwB=o9EA$TED5gSROXZwjS}l5)$CZVV}nF zI((Jhq(r(+4i37rS8kF;aAIwshSaVF%I^BK#9QMnDW|)0NDyRe6+V$&9!dn4m1o?j z@XjqzYC;?I4LrD3L4JvE01=ETJNc_dj#}yl{>=pZ=cYV{S?FSOeoY{i23c}9R*+)3 z!*aL@;?`F-CuAU3ig1r*1;j>S3!06K$j&T9DtH z{mP5HMBE#hoP7oo>9l%0Dq5m>jSJfM6_?+C+{qYV*%e2>Uh)0-DYdI2f{ZUBjDXR) zJaxU-my-5LW83uS&~`)nzI~a(D1E!N&e9Rb9R*xMQkKN2U;0onpb<@iB%LZNbI4kCzI*+z#oqO)VHw zOL>sCfyoqW7ys>I7ruuNcwepLjEIoopn4!y1dNrWRas2#bcMOrshkN9{{>@L{L}cNj=6GpJp`Gn!>ngA4XbD%s4z#kWeuYO=mcnGx7@h{;MvRy_oHMb*y6V6sALB(t`&S zVKNe;>VyY7J##vjRXp6>8pWl#eJC2I-0zeZmln-+hb0bdqPmw1E)B8?Eswka5|Gzt zy>gD}TdFwC%R*r|iN&6~v+kQ^ETb5o_?#TG5~-KrINjQdiyX^J%@d;>hx>eI^YG5) z3+TP1iI>bvSG;%uK^-In1ZN!og0_ax7 zT#aRDY`?AJ#mh`AOF1Dhx_5a1PWSeqt5qyNVfTiZHSl`)gXPfU*5JG{=F7YEG(eL0 zom`<~*c%&eBjbnxuYlpZTc~oM_@!y3CDdd>c7vQ9p;AABSgF-TUWBg$cT0AuUY8s} z^UMS9WE32pV|lw04>K>1A|ikFahKO=d6AIIZU{MZk!BFZUy-#`?T%x{rGx5$zL`Vr~C+3AD;_gfda>{S+FQzwDXOtLJ~UDCZSEds%`z^1xCd{iR3> z+-8v>61u+tSKGx9W2{Ib`!P=P(jHm+p%)#@gY)X~JA?8YrDOrP)_f(@FzHKVys@;< zKzQ7BOrFM_Z(M*c%=S@5#{lZ)wFnECs#j?+zq40yJd=uc%EQ!zoKa^=OL5SHmo?$L zdBuruPXA;ZZCc=u^A+P{wnIHvGQE}t?@fMmV(uUw`vtjPr9$7Ea-E!xs}69H4vmQ0 z!LpJm&2TP|T}frC^X;6g&k$F`j+|+vKKI>cFI&VxuW1@U^|$-MkTci%Yl8PZSXyEJ z+RF4c`9$?m@>~N)+=&L%)c4$Sl|)(onflXuQcMGpyRJllLZ1dJF?PkeK{)MJIin2t}^-_PSiJv;KmE~;y* zFS8$eMsBN}IS3`6nLxTalPBL#e?M>sQo~bq9DlH$_LaMN$gp=P)oK#{Snk}3fm3N1 zdi##+YHyHh*OzG7R#_kFdvjW{$CWFN>{^_qDlBZPlN>W)`K~jR7Ihhu1|F8!p)V6& z?Ds>8a)sF9Xw0w^PU_UYWKso(wW&3H95t=F6(>YHKA*!I0ntJIO<+w zrEYh#_({u-u0W8~jfx{oK<~kciUZ~XRr|~<_g`#2ITL4+}6c=^iJ~vaMw@UQO7EQ?$*0qwL>E zeC%?ZwY45AA~Kz?siKJLEojDc%J5yon2=4|p`UdPcf5B&1 z*2jss`rr<#`+5Zet8(oi-_xefi)06qUPYssP}BV@V&B1AohMGV;yj>`bYFVN{rATVzNd`%g6teAq?!qUrG_*r_`H_B^YMNqki@Ab^gB zMl<>JU0P$i<|msYA1Fj&cigWaG1g-jd=(rf_6-lmND{VHvcLr*dE!CC6=5-yESI_V z-ZdR0RIMxIc=q&`w7vIDCGOcJkny1m^Dge^b>Nz%7-I|}+0B~<4r;`{g~5k74=jFK z6(eijByn@r1+U*%+oFH2Avt$eEPf$#0BI9&%780a_Uw2y8GvH(0-St07n=GHLs>7x3w zGoe0yg2$E(ZybHee9XYzmbVY;gt0N{Sfht&(hNSldE!)Z?!f0R(hP9=j>Oc&_!t;{ z{F20t-SFu8?zkn1xQCEW9;+QA zD}Otz;!8JFEL|HOPv;_bm>7ZT7%o~JB(9ey)J&#ZS!ytg`V_iz+FOgpxgqt$~y>(U(mpg&b+BBv<6Wwp+@6Mw6s>~#tTyMqB0ul2rY;haU=UXanI-0bj?AmBB%18uqCopHlIQau=pE z*#4U)VI3L)Yd?8X^Yv7BchX33DvqGKO-x`Ydpe^*rGz8cUB60(BE3hVB07!~1j1at zTqi5?Hg>pS6&cMNm5RvhS5wD14V%3Num!Lgr|0l->}irRwg5OJjzuFnIP`pfn^jxh zk6Dj8n;0eX(#NwU`(@4DyYiD*5r_5=&%6HKYi5We3pycx^pCD&u*P8S#a|3KhaYGP zYfWla`&|#?H2^8)#1+kADOFZvcLTJ0tcoDnlV@bP)9-ltwr5I`C)rjLRnngm+5324 zrgOvkiX}isZ3bd_f!c_|rP&LXB;SU6-)&X4U_Av0i4W!hKt;c235tO*VBK zG{9L&B??n<;l!G+?@Y8bG*CMJc6 z1ZfT(^_w;8mB%InP0F5mVP4Xe!1D|Twm(WM=AfuuR(}$RvYOmxk|*Y~YvfOL>a^mE zd;tt`KI{7ku2sv|D2gdO?|hTB$2CP3&W3LaR&tmPTU3hBaB_zgQrdPfO4x>doXg;7{dls&RtVy7s z`q8X=O=~;f3gP1rzhfd?6MfD4%fGZ@&0ai)zYW@uY^_UWVk*F;oM(d?Sb`4;(=k(H#&Dt8rcAG4ydSgD?Q00tkjVgUS0`$+6|=16>+PT4*z z7aIgZcvN&K%u7eL)4A?OJ#LX(Q9lG<&zq0LG${IRnO?}^tKOO_ukC*-d4Mi^yXV@~ zmnZZbryEFm=>l0(FfVm~z3s$xopx4LX)<(;d5$%<$dRkV5Gm~04Z+Y5 zIwH(1!Wt~cPNG+yade#a^MzOi5l+K$KhLI# zC(Pi7uRt0E&1So@x;1v3k5H(WG)O4Vw!<#FEI%Fns7jZOrDK?^5N|c?4n!FAdlDJd zl=$38r^UBuK9oFfQ}A2DucjR0=PsH6GrsC-Z5MFSKDbtT^CqVUeXsBS>dJE0a6X11 zPi%BNdPS@7y3OLKOxWUQauROZd02ZxpZo52_~?618o0a_S~^Ml_T0XwMxDRN1GV`s zn#1PJOzZMXI7RY>(<7o;_O4hvN^}6(`}iy1B85l=6dUmuJ=7rj+N*%>m9+#Z-;0yG zm3R4?p9gYylUK!PB1sEDal7;+Q_T3wH#(x%Ck_c-w|ok9BdKp;N^OG<%L{XL@83Vm zZEabO9udE31-L3M*DVY#?+dPm+5BsPCEmpOmLIWSQlEn9Ieov54ukSpy;05VuT(9n z&qNAk;^>GV{daruGi7QYt!MLJT;CKuz-XJEcMJ*$e_jWLQ%c#+mw9VcHoW{h%iofDJN1I=Cly_Gwrk#j1rKqmZ)Xs8$ePzF2zhQ@=d8@({5fqyW0f9Wh3DFB@X z>SF|jKXn>^6Pj{Z0KHCMNk{MpJ^wdVUF;7K`i#$!xc=n%|1M;b1e6fqzAXKJ5Tn2T zEJp;i4<$yJV@Ut45El?U#J>o4`lJ4Tw?#YZFUQpY(bqVCkmdhVsQSyX>i=2EpEi$d z|GuuB1_;a%Q6B1+74^H?qgFBl2yqb3^E|9g@-)i&jGHsuLM+a|DkL7 ziyNI02O9G-;*suO)8ZeGwfLozIe4Q&@ds!B_icf9rGw3I;=e`tFNKMzK>t-yNq+dp z4S)S|Nlz@_}?)EK8j=Dl8)hkqy0Z+li8hzZy|02MRphzTAr}itg+|$J>3Satm*kcNO zTjnp?v;_LMO(er7D%SJpIL<(6U$oVy#Lq5I*}aG%SMXThJ97k$!sjMemH%Z7_`Bki z^L6{d3WQv4Ml@t{jJf!`qD^&bu$d!oy>%B1uJL03;UIs!8S4MAF=Y zN?Ivrxn(&Qh%-E}t>e6$^Z@efx9u6DZzlrBxg5`22zg}m4L!hAEw`Qow-F-$9@bw(+jFmOFjlGp+*XkD z0%Of?=Ut(KKCSP;b5eU*%C#6|!VGvv%nNQ~-|V*sE6Y~u>G6)ww^{r8{Vh!pe|EwE zz_lIAQ-f}@&Q5a7t3@OW2(*#pJGu<-@+odKc=C({NCojq|3}td z#mwwp#r#PYz1HBf%|WH%WUgHMZI%q%1-JhC7}`-x`$zRY0s;JPZ{_ykb7Fw&9L4$? z4)#u8)cPJrny?Q|dQ5b5zH_5SXLyO8@)AGyw3Wlde+{0W&yj2_y5UbMCj2f_>>U}=S#1uwN$ye`1W4&ejB zCp)ZF7hhCu08Ye!nnKjFb7auc$FJ0|jXs;}dVaP{OPbMRIGI5NiA1|l9c9f29`-hO zlQ%8D-hHJ8dIM;6cPpM3@4)!RP_Ir3DY^7%5Huok)O+T|Q>w^$M!XC(= zRZ3TNb#R*Na@6Z}a!oGCgm><}JA>rB?X56fsF^$0F)5OcJa+5HN?4>wYN#jATkhPB zUjo9;LnBc=zTQT9_{94S967|#!0=e6yUPlJ>EPv8u+lLZm-##s(dDMZe^~~M4v{)o z7BlwiUKFUGQk-30amYE>8Ggz1@9(v`*-GIuA2mp>X6#_v^z^LncXRS$1#v4SSy*X% zQr=Dk?l7`lm>_AOsgSDVu`1YwR8fhTD~Xg{Y)kCEvP_waNT)?~LD73yp8Z+@rOhD< z@gZ8#`BNgn0KvfZb|71kHO-)ti2KeP2Cdqsm&dayWI|!N{{F==6y6c!XaW>NOcRD5 z61|mcADMmWD8#|NZbCPb+2<qdz#ZWEC$10Q9^SrfBuKD!!yS0}NJ+PRBlpZcpOYu=hIlW6xoh~wGfVI)(g#t! zQ2PVmKMr1gVU!PTruM%+75}GE*A7C_mfZzX_}(w=E@oJ(7nDCKN%5R8K`v3b4Oz3V zSe-FoEBjGrJ1N`hk~c;=4|tp{VIlv^#ucD+I6E*uqu-GXJF^+F^JwCWs#AtIi(6~Z z%Z;-;y2xJi5@|xK>>iJzqvrn^E#vPua={w&>u4yQnjxlvWX@)SSn7zv{8%1JyZ-wN z(}*%vsH;elN@icvyS8iRIx`kytYs!rFET^Hc3Bs!@|yUcm3)9SWpNtjEGUD6Mhx_#ll=7)7q^v3S0 zg%ijWF z{)+#v=4huuNfd9W>;DoiwOuzIoW1n#>Hz=g`DIs+Ak+W5-Cws`{0eU~mnq))-}V~3 zfa%;rNcrer?F*2@Oh6frfXiDzMfbY~`>*5YzwB{ds+|9}ol2nw$}B>jp+EUkQu)`p z>ww8kY#E6AU#ekL^-G9;FqXOc-wMA0cKmw8SHyp{&;ul!c%UqWl%I>_zZ6CQDr@_1 zSgQZ7gTMdqFI{z&cu4R+wvxZj^>+j|IiESjQ2+0m^uLuY*ZHMuciS2JwSoHQFPkE; zk8zZG^QVCP?<(=g0cFF_NQr*eV*j=K`Ty+1=j!+8n}e2BqF<46D+Q?NJAp6E^F4j10kuZQ?I<>|>kB_a z)-#v)+_5B%zfLxCM6?;{;!G*NNO;O3QOcr7GaY)&8N)`2_4`}L;q)CH8)t{LO&F%h zeDb}QxF1ubq;h^Fi?9FdRof4cemJ5W&st9UsA)o6xgV@pHs(`2{cU`-<0EfuT$lHU zwJ|?bZ%_Qs zdSLSz7_NH6?A8AbZ>SUnLKpEEQU2bk|CxP1h=3Vc%VYAE^|zMzPodezcKAbawQW5s T)?}#%z>kciqC}~fQNaHJ@w)4; literal 0 HcmV?d00001 diff --git a/docs/nextjs.asciidoc b/docs/nextjs.asciidoc index c1d3f3a79d7..2643c1a72f8 100644 --- a/docs/nextjs.asciidoc +++ b/docs/nextjs.asciidoc @@ -166,6 +166,8 @@ while true; do sleep 1; curl -i http://localhost:3000/api/hello; done Visit your Kibana APM app and, after a few seconds, you should see a service entry for your Next.js app. (The service name will be pulled from the "name" field in "package.json". It can be overriden with https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html#service-name[`ELASTIC_APM_SERVICE_NAME`].) +image::./images/nextjs-my-app-screenshot.png[Kibana APM app showing Next.js my-app] + XXX screenshot (can we add screenshots to docs, or too brittle for UI changes?) From 9d14567d2dc02cfb60c0c1da456841d7d8d1bccf Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Tue, 18 Oct 2022 13:48:25 -0700 Subject: [PATCH 42/66] apmsetup.js -> elastic-apm-node/start-next.js; drop the .env file handling, will update docs to suggest elastic-apm-node.js --- examples/nextjs/.gitignore | 2 + examples/nextjs/.npmrc | 1 + examples/nextjs/apmsetup.js | 34 - examples/nextjs/package.json | 4 +- examples/nextjs/yarn.lock | 2217 ---------------------------------- package.json | 6 +- start-next.d.ts | 8 + start-next.js | 17 + 8 files changed, 34 insertions(+), 2255 deletions(-) create mode 100644 examples/nextjs/.npmrc delete mode 100644 examples/nextjs/apmsetup.js delete mode 100644 examples/nextjs/yarn.lock create mode 100644 start-next.d.ts create mode 100644 start-next.js diff --git a/examples/nextjs/.gitignore b/examples/nextjs/.gitignore index c87c9b392c0..a248576abdc 100644 --- a/examples/nextjs/.gitignore +++ b/examples/nextjs/.gitignore @@ -1,5 +1,7 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +/elastic-apm-node.js + # dependencies /node_modules /.pnp diff --git a/examples/nextjs/.npmrc b/examples/nextjs/.npmrc new file mode 100644 index 00000000000..43c97e719a5 --- /dev/null +++ b/examples/nextjs/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/examples/nextjs/apmsetup.js b/examples/nextjs/apmsetup.js deleted file mode 100644 index da6f60b6d9b..00000000000 --- a/examples/nextjs/apmsetup.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict' - -// Use this script with Node's `--require` flag to monitor a Next.js app with -// Elastic APM. - -// Support `ELASTIC_APM_...` envvars in ".env*" files as Next.js supports them. -// https://nextjs.org/docs/basic-features/environment-variables -try { - const { loadEnvConfig } = require('@next/env') - const isDev = process.argv[process.argv.length - 1] === 'dev' - loadEnvConfig(__dirname, isDev) -} catch (envErr) { - console.error('apmsetup: warning: failed to load @next/env to read possible .env files') -} - -if (!process.env.ELASTIC_APM_SERVER_URL) { - console.log('apmsetup: ELASTIC_APM_SERVER_URL is not set, disabling APM') -} else { - // APM agent configuration can be passed to `.start()` or specified as - // environment variables. - // https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html - const apm = require('elastic-apm-node').start() - - // Flush APM data on server process termination. - // https://nextjs.org/docs/deployment#manual-graceful-shutdowns - process.env.NEXT_MANUAL_SIG_HANDLE = 1 - function flushApmAndExit () { - apm.flush(() => { - process.exit(0) - }) - } - process.on('SIGTERM', flushApmAndExit) - process.on('SIGINT', flushApmAndExit) -} diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index 27c5a6da682..a3eb873e34b 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -3,9 +3,9 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "NODE_OPTIONS=--require=./apmsetup.js next dev", + "dev": "NODE_OPTIONS=--require=elastic-apm-node/start-next.js next dev", "build": "next build", - "start": "NODE_OPTIONS=--require=./apmsetup.js next start", + "start": "NODE_OPTIONS=--require=elastic-apm-node/start-next.js next start", "lint": "next lint" }, "dependencies": { diff --git a/examples/nextjs/yarn.lock b/examples/nextjs/yarn.lock deleted file mode 100644 index 69ab30edfe0..00000000000 --- a/examples/nextjs/yarn.lock +++ /dev/null @@ -1,2217 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/runtime-corejs3@^7.10.2": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.19.4.tgz#870dbfd9685b3dad5aeb2d00841bb8b6192e3095" - integrity sha512-HzjQ8+dzdx7dmZy4DQ8KV8aHi/74AjEbBGTFutBmg/pd3dY5/q1sfuOGPTFGEytlQhWoeVXqcK5BwMgIkRkNDQ== - dependencies: - core-js-pure "^3.25.1" - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.10.2", "@babel/runtime@^7.18.9": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.4.tgz#a42f814502ee467d55b38dd1c256f53a7b885c78" - integrity sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA== - dependencies: - regenerator-runtime "^0.13.4" - -"@elastic/ecs-helpers@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@elastic/ecs-helpers/-/ecs-helpers-1.1.0.tgz#ee7e6f870f75a2222c5d7179b36a628f1db4779e" - integrity sha512-MDLb2aFeGjg46O5mLpdCzT5yOUDnXToJSrco2ShqGIXxNJaM8uJjX+4nd+hRYV4Vex8YJyDtOFEVBldQct6ndg== - dependencies: - fast-json-stringify "^2.4.1" - -"@elastic/ecs-pino-format@^1.2.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@elastic/ecs-pino-format/-/ecs-pino-format-1.3.0.tgz#6e349a7da342b3c370d15361ba7f850bc2f783bc" - integrity sha512-U8D57gPECYoRCcwREsrXKBtqeyFFF/KAwHi4rG1u/oQhAg91Kzw8ZtUQJXD/DMDieLOqtbItFr2FRBWI3t3wog== - dependencies: - "@elastic/ecs-helpers" "^1.1.0" - -"@eslint/eslintrc@^1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" - integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.4.0" - globals "^13.15.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@humanwhocodes/config-array@^0.10.5": - version "0.10.7" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.7.tgz#6d53769fd0c222767e6452e8ebda825c22e9f0dc" - integrity sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w== - dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" - minimatch "^3.0.4" - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== - -"@next/env@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/env/-/env-12.3.1.tgz#18266bd92de3b4aa4037b1927aa59e6f11879260" - integrity sha512-9P9THmRFVKGKt9DYqeC2aKIxm8rlvkK38V1P1sRE7qyoPBIs8l9oo79QoSdPtOWfzkbDAVUqvbQGgTMsb8BtJg== - -"@next/eslint-plugin-next@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.3.1.tgz#b821f27b0f175954d8d18e5d323fce040ecc79a6" - integrity sha512-sw+lTf6r6P0j+g/n9y4qdWWI2syPqZx+uc0+B/fRENqfR3KpSid6MIKqc9gNwGhJASazEQ5b3w8h4cAET213jw== - dependencies: - glob "7.1.7" - -"@next/swc-android-arm-eabi@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.1.tgz#b15ce8ad376102a3b8c0f3c017dde050a22bb1a3" - integrity sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ== - -"@next/swc-android-arm64@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.3.1.tgz#85d205f568a790a137cb3c3f720d961a2436ac9c" - integrity sha512-CmgU2ZNyBP0rkugOOqLnjl3+eRpXBzB/I2sjwcGZ7/Z6RcUJXK5Evz+N0ucOxqE4cZ3gkTeXtSzRrMK2mGYV8Q== - -"@next/swc-darwin-arm64@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.3.1.tgz#b105457d6760a7916b27e46c97cb1a40547114ae" - integrity sha512-hT/EBGNcu0ITiuWDYU9ur57Oa4LybD5DOQp4f22T6zLfpoBMfBibPtR8XktXmOyFHrL/6FC2p9ojdLZhWhvBHg== - -"@next/swc-darwin-x64@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.3.1.tgz#6947b39082271378896b095b6696a7791c6e32b1" - integrity sha512-9S6EVueCVCyGf2vuiLiGEHZCJcPAxglyckTZcEwLdJwozLqN0gtS0Eq0bQlGS3dH49Py/rQYpZ3KVWZ9BUf/WA== - -"@next/swc-freebsd-x64@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.3.1.tgz#2b6c36a4d84aae8b0ea0e0da9bafc696ae27085a" - integrity sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q== - -"@next/swc-linux-arm-gnueabihf@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.3.1.tgz#6e421c44285cfedac1f4631d5de330dd60b86298" - integrity sha512-diL9MSYrEI5nY2wc/h/DBewEDUzr/DqBjIgHJ3RUNtETAOB3spMNHvJk2XKUDjnQuluLmFMloet9tpEqU2TT9w== - -"@next/swc-linux-arm64-gnu@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.1.tgz#8863f08a81f422f910af126159d2cbb9552ef717" - integrity sha512-o/xB2nztoaC7jnXU3Q36vGgOolJpsGG8ETNjxM1VAPxRwM7FyGCPHOMk1XavG88QZSQf+1r+POBW0tLxQOJ9DQ== - -"@next/swc-linux-arm64-musl@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.1.tgz#0038f07cf0b259d70ae0c80890d826dfc775d9f3" - integrity sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg== - -"@next/swc-linux-x64-gnu@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.1.tgz#c66468f5e8181ffb096c537f0dbfb589baa6a9c1" - integrity sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA== - -"@next/swc-linux-x64-musl@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.1.tgz#c6269f3e96ac0395bc722ad97ce410ea5101d305" - integrity sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg== - -"@next/swc-win32-arm64-msvc@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.1.tgz#83c639ee969cee36ce247c3abd1d9df97b5ecade" - integrity sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw== - -"@next/swc-win32-ia32-msvc@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.1.tgz#52995748b92aa8ad053440301bc2c0d9fbcf27c2" - integrity sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA== - -"@next/swc-win32-x64-msvc@12.3.1": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.1.tgz#27d71a95247a9eaee03d47adee7e3bd594514136" - integrity sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA== - -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.3": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - -"@opentelemetry/api@^1.1.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.2.0.tgz#89ef99401cde6208cff98760b67663726ef26686" - integrity sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g== - -"@rushstack/eslint-patch@^1.1.3": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728" - integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== - -"@swc/helpers@0.4.11": - version "0.4.11" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.11.tgz#db23a376761b3d31c26502122f349a21b592c8de" - integrity sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw== - dependencies: - tslib "^2.4.0" - -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== - -"@typescript-eslint/parser@^5.21.0": - version "5.40.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.40.0.tgz#432bddc1fe9154945660f67c1ba6d44de5014840" - integrity sha512-Ah5gqyX2ySkiuYeOIDg7ap51/b63QgWZA7w6AHtFrag7aH0lRQPbLzUjk0c9o5/KZ6JRkTTDKShL4AUrQa6/hw== - dependencies: - "@typescript-eslint/scope-manager" "5.40.0" - "@typescript-eslint/types" "5.40.0" - "@typescript-eslint/typescript-estree" "5.40.0" - debug "^4.3.4" - -"@typescript-eslint/scope-manager@5.40.0": - version "5.40.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.40.0.tgz#d6ea782c8e3a2371ba3ea31458dcbdc934668fc4" - integrity sha512-d3nPmjUeZtEWRvyReMI4I1MwPGC63E8pDoHy0BnrYjnJgilBD3hv7XOiETKLY/zTwI7kCnBDf2vWTRUVpYw0Uw== - dependencies: - "@typescript-eslint/types" "5.40.0" - "@typescript-eslint/visitor-keys" "5.40.0" - -"@typescript-eslint/types@5.40.0": - version "5.40.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.40.0.tgz#8de07e118a10b8f63c99e174a3860f75608c822e" - integrity sha512-V1KdQRTXsYpf1Y1fXCeZ+uhjW48Niiw0VGt4V8yzuaDTU8Z1Xl7yQDyQNqyAFcVhpYXIVCEuxSIWTsLDpHgTbw== - -"@typescript-eslint/typescript-estree@5.40.0": - version "5.40.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.40.0.tgz#e305e6a5d65226efa5471ee0f12e0ffaab6d3075" - integrity sha512-b0GYlDj8TLTOqwX7EGbw2gL5EXS2CPEWhF9nGJiGmEcmlpNBjyHsTwbqpyIEPVpl6br4UcBOYlcI2FJVtJkYhg== - dependencies: - "@typescript-eslint/types" "5.40.0" - "@typescript-eslint/visitor-keys" "5.40.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/visitor-keys@5.40.0": - version "5.40.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.40.0.tgz#dd2d38097f68e0d2e1e06cb9f73c0173aca54b68" - integrity sha512-ijJ+6yig+x9XplEpG2K6FUdJeQGGj/15U3S56W9IqXKJqleuD7zJ2AX/miLezwxpd7ZxDAqO87zWufKg+RPZyQ== - dependencies: - "@typescript-eslint/types" "5.40.0" - eslint-visitor-keys "^3.3.0" - -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn@^8.8.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" - integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== - -after-all-results@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/after-all-results/-/after-all-results-2.0.0.tgz#6ac2fc202b500f88da8f4f5530cfa100f4c6a2d0" - integrity sha512-2zHEyuhSJOuCrmas9YV0YL/MFCWLxe1dS6k/ENhgYrb/JqyMnadLN4iIAc9kkZrbElMDyyAGH/0J18OPErOWLg== - -agentkeepalive@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" - integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== - dependencies: - debug "^4.1.0" - depd "^1.1.2" - humanize-ms "^1.2.1" - -ajv@^6.10.0, ajv@^6.11.0, ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -aria-query@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" - integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== - dependencies: - "@babel/runtime" "^7.10.2" - "@babel/runtime-corejs3" "^7.10.2" - -array-includes@^3.1.4, array-includes@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" - integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - get-intrinsic "^1.1.1" - is-string "^1.0.7" - -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array.prototype.flat@^1.2.5: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" - integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" - es-shim-unscopables "^1.0.0" - -array.prototype.flatmap@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f" - integrity sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" - es-shim-unscopables "^1.0.0" - -ast-types-flow@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" - integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag== - -async-cache@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/async-cache/-/async-cache-1.1.0.tgz#4a9a5a89d065ec5d8e5254bd9ee96ba76c532b5a" - integrity sha512-YDQc4vBn5NFhY6g6HhVshyi3Fy9+SQ5ePnE7JLDJn1DoL+i7ER+vMwtTNOYk9leZkYMnOwpBCWqyLDPw8Aig8g== - dependencies: - lru-cache "^4.0.0" - -async-value-promise@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/async-value-promise/-/async-value-promise-1.1.1.tgz#68957819e3eace804f3b4b69477e2bd276c15378" - integrity sha512-c2RFDKjJle1rHa0YxN9Ysu97/QBu3Wa+NOejJxsX+1qVDJrkD3JL/GN1B3gaILAEXJXbu/4Z1lcoCHFESe/APA== - dependencies: - async-value "^1.2.2" - -async-value@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/async-value/-/async-value-1.2.2.tgz#84517a1e7cb6b1a5b5e181fa31be10437b7fb125" - integrity sha512-8rwtYe32OAS1W9CTwvknoyts+mc3ta8N7Pi0h7AjkMaKvsFbr39K+gEfZ7Z81aPXQ1sK5M23lgLy1QfZpcpadQ== - -atomic-sleep@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" - integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== - -axe-core@^4.4.3: - version "4.4.3" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" - integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w== - -axobject-query@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" - integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -basic-auth@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" - integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== - dependencies: - safe-buffer "5.1.2" - -binary-search@^1.3.3: - version "1.3.6" - resolved "https://registry.yarnpkg.com/binary-search/-/binary-search-1.3.6.tgz#e32426016a0c5092f0f3598836a1c7da3560565c" - integrity sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA== - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -breadth-filter@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/breadth-filter/-/breadth-filter-2.0.0.tgz#7b3f8737f46ba1946aec19355ecf5df2bdb7e47c" - integrity sha512-thQShDXnFWSk2oVBixRCyrWsFoV5tfOpWKHmxwafHQDNxCfDBk539utpvytNjmlFrTMqz41poLwJvA1MW3z0MQ== - dependencies: - object.entries "^1.0.4" - -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -caniuse-lite@^1.0.30001406: - version "1.0.30001419" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001419.tgz#3542722d57d567c8210d5e4d0f9f17336b776457" - integrity sha512-aFO1r+g6R7TW+PNQxKzjITwLOyDhVRLjW0LcwS/HCZGUUKTGNp9+IwLC4xyDSZBygVL/mxaFR3HIV6wEKQuSzw== - -chalk@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -console-log-level@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/console-log-level/-/console-log-level-1.4.1.tgz#9c5a6bb9ef1ef65b05aba83028b0ff894cdf630a" - integrity sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ== - -container-info@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/container-info/-/container-info-1.1.0.tgz#6fcb94e93eacd397c6316ca2834491ede44e55ee" - integrity sha512-eD2zLAmxGS2kmL4f1jY8BdOqnmpL6X70kvzTBW/9FIQnxoxiBJ4htMsTmtPLPWRs7NHYFvqKQ1VtppV08mdsQA== - -cookie@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== - -core-js-pure@^3.25.1: - version "3.25.5" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.25.5.tgz#79716ba54240c6aa9ceba6eee08cf79471ba184d" - integrity sha512-oml3M22pHM+igfWHDfdLVq2ShWmjM2V4L+dQEBs0DWVIqEm9WHCwGAlZ6BmyBQGy5sFrJmcx+856D9lVKyGWYg== - -core-util-is@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -damerau-levenshtein@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" - integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== - -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -deep-is@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== - -define-properties@^1.1.3, define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== - dependencies: - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -depd@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -elastic-apm-http-client@11.0.1: - version "11.0.1" - resolved "https://registry.yarnpkg.com/elastic-apm-http-client/-/elastic-apm-http-client-11.0.1.tgz#15dbe99d56d62b3f732d1bd2b51bef6094b78801" - integrity sha512-5AOWlhs2WlZpI+DfgGqY/8Rk7KF8WeevaO8R961eBylavU6GWhLRNiJncohn5jsvrqhmeT19azBvy/oYRN7bJw== - dependencies: - agentkeepalive "^4.2.1" - breadth-filter "^2.0.0" - container-info "^1.0.1" - end-of-stream "^1.4.4" - fast-safe-stringify "^2.0.7" - fast-stream-to-buffer "^1.0.0" - object-filter-sequence "^1.0.0" - readable-stream "^3.4.0" - semver "^6.3.0" - stream-chopper "^3.0.1" - -elastic-apm-node@elastic/apm-agent-nodejs#trentm/feat-nextjs: - version "3.38.0" - resolved "https://codeload.github.com/elastic/apm-agent-nodejs/tar.gz/06290116a86972355a613d589d76958666c17564" - dependencies: - "@elastic/ecs-pino-format" "^1.2.0" - "@opentelemetry/api" "^1.1.0" - after-all-results "^2.0.0" - async-cache "^1.1.0" - async-value-promise "^1.1.1" - basic-auth "^2.0.1" - cookie "^0.5.0" - core-util-is "^1.0.2" - elastic-apm-http-client "11.0.1" - end-of-stream "^1.4.4" - error-callsites "^2.0.4" - error-stack-parser "^2.0.6" - escape-string-regexp "^4.0.0" - fast-safe-stringify "^2.0.7" - http-headers "^3.0.2" - is-native "^1.0.1" - lru-cache "^6.0.0" - measured-reporting "^1.51.1" - monitor-event-loop-delay "^1.0.0" - object-filter-sequence "^1.0.0" - object-identity-map "^1.0.2" - original-url "^1.2.3" - pino "^6.11.2" - relative-microtime "^2.0.0" - require-in-the-middle "^5.2.0" - semver "^6.3.0" - set-cookie-serde "^1.0.0" - shallow-clone-shim "^2.0.0" - source-map "^0.8.0-beta.0" - sql-summary "^1.0.1" - traverse "^0.6.6" - unicode-byte-truncate "^1.0.0" - -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -end-of-stream@^1.4.1, end-of-stream@^1.4.4: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -error-callsites@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/error-callsites/-/error-callsites-2.0.4.tgz#44f09e6a201e9a1603ead81eacac5ba258fca76e" - integrity sha512-V877Ch4FC4FN178fDK1fsrHN4I1YQIBdtjKrHh3BUHMnh3SMvwUVrqkaOgDpUuevgSNna0RBq6Ox9SGlxYrigA== - -error-stack-parser@^2.0.6: - version "2.1.4" - resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" - integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== - dependencies: - stackframe "^1.3.4" - -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: - version "1.20.4" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" - integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - is-callable "^1.2.7" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-weakref "^1.0.2" - object-inspect "^1.12.2" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" - safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" - unbox-primitive "^1.0.2" - -es-shim-unscopables@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" - integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== - dependencies: - has "^1.0.3" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eslint-config-next@12.3.1: - version "12.3.1" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.3.1.tgz#5d4eb0b7903cea81fd0d5106601d3afb0a453ff4" - integrity sha512-EN/xwKPU6jz1G0Qi6Bd/BqMnHLyRAL0VsaQaWA7F3KkjAgZHi4f1uL1JKGWNxdQpHTW/sdGONBd0bzxUka/DJg== - dependencies: - "@next/eslint-plugin-next" "12.3.1" - "@rushstack/eslint-patch" "^1.1.3" - "@typescript-eslint/parser" "^5.21.0" - eslint-import-resolver-node "^0.3.6" - eslint-import-resolver-typescript "^2.7.1" - eslint-plugin-import "^2.26.0" - eslint-plugin-jsx-a11y "^6.5.1" - eslint-plugin-react "^7.31.7" - eslint-plugin-react-hooks "^4.5.0" - -eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== - dependencies: - debug "^3.2.7" - resolve "^1.20.0" - -eslint-import-resolver-typescript@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz#a90a4a1c80da8d632df25994c4c5fdcdd02b8751" - integrity sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ== - dependencies: - debug "^4.3.4" - glob "^7.2.0" - is-glob "^4.0.3" - resolve "^1.22.0" - tsconfig-paths "^3.14.1" - -eslint-module-utils@^2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" - integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== - dependencies: - debug "^3.2.7" - -eslint-plugin-import@^2.26.0: - version "2.26.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" - integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== - dependencies: - array-includes "^3.1.4" - array.prototype.flat "^1.2.5" - debug "^2.6.9" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.3" - has "^1.0.3" - is-core-module "^2.8.1" - is-glob "^4.0.3" - minimatch "^3.1.2" - object.values "^1.1.5" - resolve "^1.22.0" - tsconfig-paths "^3.14.1" - -eslint-plugin-jsx-a11y@^6.5.1: - version "6.6.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz#93736fc91b83fdc38cc8d115deedfc3091aef1ff" - integrity sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q== - dependencies: - "@babel/runtime" "^7.18.9" - aria-query "^4.2.2" - array-includes "^3.1.5" - ast-types-flow "^0.0.7" - axe-core "^4.4.3" - axobject-query "^2.2.0" - damerau-levenshtein "^1.0.8" - emoji-regex "^9.2.2" - has "^1.0.3" - jsx-ast-utils "^3.3.2" - language-tags "^1.0.5" - minimatch "^3.1.2" - semver "^6.3.0" - -eslint-plugin-react-hooks@^4.5.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" - integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== - -eslint-plugin-react@^7.31.7: - version "7.31.10" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz#6782c2c7fe91c09e715d536067644bbb9491419a" - integrity sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA== - dependencies: - array-includes "^3.1.5" - array.prototype.flatmap "^1.3.0" - doctrine "^2.1.0" - estraverse "^5.3.0" - jsx-ast-utils "^2.4.1 || ^3.0.0" - minimatch "^3.1.2" - object.entries "^1.1.5" - object.fromentries "^2.0.5" - object.hasown "^1.1.1" - object.values "^1.1.5" - prop-types "^15.8.1" - resolve "^2.0.0-next.3" - semver "^6.3.0" - string.prototype.matchall "^4.0.7" - -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== - -eslint@8.25.0: - version "8.25.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.25.0.tgz#00eb962f50962165d0c4ee3327708315eaa8058b" - integrity sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A== - dependencies: - "@eslint/eslintrc" "^1.3.3" - "@humanwhocodes/config-array" "^0.10.5" - "@humanwhocodes/module-importer" "^1.0.1" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.4.0" - esquery "^1.4.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.1" - globals "^13.15.0" - globby "^11.1.0" - grapheme-splitter "^1.0.4" - ignore "^5.2.0" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - js-sdsl "^4.1.4" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.1" - regexpp "^3.2.0" - strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" - text-table "^0.2.0" - -espree@^9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a" - integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw== - dependencies: - acorn "^8.8.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" - -esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-glob@^3.2.9: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-json-stringify@^2.4.1: - version "2.7.13" - resolved "https://registry.yarnpkg.com/fast-json-stringify/-/fast-json-stringify-2.7.13.tgz#277aa86c2acba4d9851bd6108ed657aa327ed8c0" - integrity sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA== - dependencies: - ajv "^6.11.0" - deepmerge "^4.2.2" - rfdc "^1.2.0" - string-similarity "^4.0.1" - -fast-levenshtein@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - -fast-redact@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.1.2.tgz#d58e69e9084ce9fa4c1a6fa98a3e1ecf5d7839aa" - integrity sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw== - -fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.0.8: - version "2.1.1" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" - integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== - -fast-stream-to-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-stream-to-buffer/-/fast-stream-to-buffer-1.0.0.tgz#793340cc753e7ec9c7fb6d57a53a0b911cb0f588" - integrity sha512-bI/544WUQlD2iXBibQbOMSmG07Hay7YrpXlKaeGTPT7H7pC0eitt3usak5vUwEvCGK/O7rUAM3iyQValGU22TQ== - dependencies: - end-of-stream "^1.4.1" - -fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== - dependencies: - reusify "^1.0.4" - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== - dependencies: - flatted "^3.1.0" - rimraf "^3.0.2" - -flatstr@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.12.tgz#c2ba6a08173edbb6c9640e3055b95e287ceb5931" - integrity sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw== - -flatted@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== - -forwarded-parse@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/forwarded-parse/-/forwarded-parse-2.1.2.tgz#08511eddaaa2ddfd56ba11138eee7df117a09325" - integrity sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" - -functions-have-names@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" - integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - -glob-parent@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-parent@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== - dependencies: - is-glob "^4.0.3" - -glob@7.1.7: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.1.3, glob@^7.2.0: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globals@^13.15.0: - version "13.17.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" - integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== - dependencies: - type-fest "^0.20.2" - -globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== - -has-bigints@^1.0.1, has-bigints@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== - dependencies: - get-intrinsic "^1.1.1" - -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -http-headers@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/http-headers/-/http-headers-3.0.2.tgz#5147771292f0b39d6778d930a3a59a76fc7ef44d" - integrity sha512-87E1I+2Wg4dxxz4rcxElo3dxO/w1ZtgL1yA0Sb6vH3qU16vRKq1NjWQv9SCY3ly2OQROcoxHZOUpmelS+k6wOw== - dependencies: - next-line "^1.1.0" - -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" - integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== - dependencies: - ms "^2.0.0" - -ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== - -import-fresh@^3.0.0, import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== - dependencies: - get-intrinsic "^1.1.0" - has "^1.0.3" - side-channel "^1.0.4" - -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-callable@^1.1.4, is-callable@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - -is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" - integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== - dependencies: - has "^1.0.3" - -is-date-object@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== - dependencies: - has-tostringtag "^1.0.0" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-finite@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" - integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-integer@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-integer/-/is-integer-1.0.7.tgz#6bde81aacddf78b659b6629d629cadc51a886d5c" - integrity sha512-RPQc/s9yBHSvpi+hs9dYiJ2cuFeU6x3TyyIp8O2H6SKEltIvJOzRj9ToyvcStDvPR/pS4rxgr1oBFajQjZ2Szg== - dependencies: - is-finite "^1.0.0" - -is-native@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-native/-/is-native-1.0.1.tgz#cd18cc162e8450d683b5babe79ac99c145449675" - integrity sha512-I4z9hx+4u3/zyvpvGtAR+n7SodJugE+i2jiS8yfq1A9QAZY0KldLQz0SBptLC9ti7kBlpghWUwTKE2BA62eCcw== - dependencies: - is-nil "^1.0.0" - to-source-code "^1.0.0" - -is-negative-zero@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== - -is-nil@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-nil/-/is-nil-1.0.1.tgz#2daba29e0b585063875e7b539d071f5b15937969" - integrity sha512-m2Rm8PhUFDNNhgvwZJjJG74a9h5CHU0fkA8WT+WGlCjyEbZ2jPwgb+ZxHu4np284EqNVyOsgppReK4qy/TwEwg== - -is-number-object@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" - integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== - dependencies: - has-tostringtag "^1.0.0" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-shared-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" - integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== - dependencies: - call-bind "^1.0.2" - -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-weakref@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== - dependencies: - call-bind "^1.0.2" - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -js-sdsl@^4.1.4: - version "4.1.5" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.5.tgz#1ff1645e6b4d1b028cd3f862db88c9d887f26e2a" - integrity sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q== - -"js-tokens@^3.0.0 || ^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - -"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" - integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw== - dependencies: - array-includes "^3.1.5" - object.assign "^4.1.3" - -language-subtag-registry@~0.3.2: - version "0.3.22" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" - integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== - -language-tags@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a" - integrity sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ== - dependencies: - language-subtag-registry "~0.3.2" - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== - -loose-envify@^1.1.0, loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -lru-cache@^4.0.0: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -mapcap@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/mapcap/-/mapcap-1.0.0.tgz#e8e29d04a160eaf8c92ec4bcbd2c5d07ed037e5a" - integrity sha512-KcNlZSlFPx+r1jYZmxEbTVymG+dIctf10WmWkuhrhrblM+KMoF77HelwihL5cxYlORye79KoR4IlOOk99lUJ0g== - -measured-core@^1.51.1: - version "1.51.1" - resolved "https://registry.yarnpkg.com/measured-core/-/measured-core-1.51.1.tgz#98989705c00bfb0d8a20e665a9f8d6e246a40518" - integrity sha512-DZQP9SEwdqqYRvT2slMK81D/7xwdxXosZZBtLVfPSo6y5P672FBTbzHVdN4IQyUkUpcVOR9pIvtUy5Ryl7NKyg== - dependencies: - binary-search "^1.3.3" - optional-js "^2.0.0" - -measured-reporting@^1.51.1: - version "1.51.1" - resolved "https://registry.yarnpkg.com/measured-reporting/-/measured-reporting-1.51.1.tgz#6aeb209ad55edf3940e8afa75c8f97f541216b31" - integrity sha512-JCt+2u6XT1I5lG3SuYqywE0e62DJuAzBcfMzWGUhIYtPQV2Vm4HiYt/durqmzsAbZV181CEs+o/jMKWJKkYIWw== - dependencies: - console-log-level "^1.4.1" - mapcap "^1.0.0" - measured-core "^1.51.1" - optional-js "^2.0.0" - -merge2@^1.3.0, merge2@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== - dependencies: - braces "^3.0.2" - picomatch "^2.3.1" - -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.6: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== - -module-details-from-path@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" - integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== - -monitor-event-loop-delay@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/monitor-event-loop-delay/-/monitor-event-loop-delay-1.0.0.tgz#b5ab78165a3bb93f2b275c50d01430c7f155d1f7" - integrity sha512-YRIr1exCIfBDLZle8WHOfSo7Xg3M+phcZfq9Fx1L6Abo+atGp7cge5pM7PjyBn4s1oZI/BRD4EMrzQBbPpVb5Q== - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@^2.0.0, ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -nanoid@^3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" - integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== - -next-line@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/next-line/-/next-line-1.1.0.tgz#fcae57853052b6a9bae8208e40dd7d3c2d304603" - integrity sha512-+I10J3wKNoKddNxn0CNpoZ3eTZuqxjNM3b1GImVx22+ePI+Y15P8g/j3WsbP0fhzzrFzrtjOAoq5NCCucswXOQ== - -next@12.3.1: - version "12.3.1" - resolved "https://registry.yarnpkg.com/next/-/next-12.3.1.tgz#127b825ad2207faf869b33393ec8c75fe61e50f1" - integrity sha512-l7bvmSeIwX5lp07WtIiP9u2ytZMv7jIeB8iacR28PuUEFG5j0HGAPnMqyG5kbZNBG2H7tRsrQ4HCjuMOPnANZw== - dependencies: - "@next/env" "12.3.1" - "@swc/helpers" "0.4.11" - caniuse-lite "^1.0.30001406" - postcss "8.4.14" - styled-jsx "5.0.7" - use-sync-external-store "1.2.0" - optionalDependencies: - "@next/swc-android-arm-eabi" "12.3.1" - "@next/swc-android-arm64" "12.3.1" - "@next/swc-darwin-arm64" "12.3.1" - "@next/swc-darwin-x64" "12.3.1" - "@next/swc-freebsd-x64" "12.3.1" - "@next/swc-linux-arm-gnueabihf" "12.3.1" - "@next/swc-linux-arm64-gnu" "12.3.1" - "@next/swc-linux-arm64-musl" "12.3.1" - "@next/swc-linux-x64-gnu" "12.3.1" - "@next/swc-linux-x64-musl" "12.3.1" - "@next/swc-win32-arm64-msvc" "12.3.1" - "@next/swc-win32-ia32-msvc" "12.3.1" - "@next/swc-win32-x64-msvc" "12.3.1" - -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-filter-sequence@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/object-filter-sequence/-/object-filter-sequence-1.0.0.tgz#10bb05402fff100082b80d7e83991b10db411692" - integrity sha512-CsubGNxhIEChNY4cXYuA6KXafztzHqzLLZ/y3Kasf3A+sa3lL9thq3z+7o0pZqzEinjXT6lXDPAfVWI59dUyzQ== - -object-identity-map@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/object-identity-map/-/object-identity-map-1.0.2.tgz#2b4213a4285ca3a8cd2e696782c9964f887524e7" - integrity sha512-a2XZDGyYTngvGS67kWnqVdpoaJWsY7C1GhPJvejWAFCsUioTAaiTu8oBad7c6cI4McZxr4CmvnZeycK05iav5A== - dependencies: - object.entries "^1.1.0" - -object-inspect@^1.12.2, object-inspect@^1.9.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.3, object.assign@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" - integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - has-symbols "^1.0.3" - object-keys "^1.1.1" - -object.entries@^1.0.4, object.entries@^1.1.0, object.entries@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" - integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -object.fromentries@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" - integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -object.hasown@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3" - integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A== - dependencies: - define-properties "^1.1.4" - es-abstract "^1.19.5" - -object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -once@^1.3.0, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -optional-js@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/optional-js/-/optional-js-2.3.0.tgz#81d54c4719afa8845b988143643a5148f9d89490" - integrity sha512-B0LLi+Vg+eko++0z/b8zIv57kp7HKEzaPJo7LowJXMUKYdf+3XJGu/cw03h/JhIOsLnP+cG5QnTHAuicjA5fMw== - -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.3" - -original-url@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/original-url/-/original-url-1.2.3.tgz#133aff4b2d27e38a98d736f7629c56262b7153e1" - integrity sha512-BYm+pKYLtS4mVe/mgT3YKGtWV5HzN/XKiaIu1aK4rsxyjuHeTW9N+xVBEpJcY1onB3nccfH0RbzUEoimMqFUHQ== - dependencies: - forwarded-parse "^2.1.0" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - -picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pino-std-serializers@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz#b56487c402d882eb96cd67c257868016b61ad671" - integrity sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg== - -pino@^6.11.2: - version "6.14.0" - resolved "https://registry.yarnpkg.com/pino/-/pino-6.14.0.tgz#b745ea87a99a6c4c9b374e4f29ca7910d4c69f78" - integrity sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg== - dependencies: - fast-redact "^3.0.0" - fast-safe-stringify "^2.0.8" - flatstr "^1.0.12" - pino-std-serializers "^3.1.0" - process-warning "^1.0.0" - quick-format-unescaped "^4.0.3" - sonic-boom "^1.0.2" - -postcss@8.4.14: - version "8.4.14" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" - integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== - dependencies: - nanoid "^3.3.4" - picocolors "^1.0.0" - source-map-js "^1.0.2" - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -process-warning@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" - integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== - -prop-types@^15.8.1: - version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== - -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -quick-format-unescaped@^4.0.3: - version "4.0.4" - resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" - integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== - -react-dom@18.2.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" - integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== - dependencies: - loose-envify "^1.1.0" - scheduler "^0.23.0" - -react-is@^16.13.1: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react@18.2.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" - integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== - dependencies: - loose-envify "^1.1.0" - -readable-stream@^3.0.6, readable-stream@^3.4.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -regenerator-runtime@^0.13.4: - version "0.13.10" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" - integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw== - -regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" - -regexpp@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - -relative-microtime@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/relative-microtime/-/relative-microtime-2.0.0.tgz#cceed2af095ecd72ea32011279c79e5fcc7de29b" - integrity sha512-l18ha6HEZc+No/uK4GyAnNxgKW7nvEe35IaeN54sShMojtqik2a6GbTyuiezkjpPaqP874Z3lW5ysBo5irz4NA== - -require-in-the-middle@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.2.0.tgz#4b71e3cc7f59977100af9beb76bf2d056a5a6de2" - integrity sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg== - dependencies: - debug "^4.1.1" - module-details-from-path "^1.0.3" - resolve "^1.22.1" - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^2.0.0-next.3: - version "2.0.0-next.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" - integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rfdc@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" - integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -safe-buffer@5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-regex-test@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" - integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - is-regex "^1.1.4" - -scheduler@^0.23.0: - version "0.23.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" - integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== - dependencies: - loose-envify "^1.1.0" - -semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.7: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== - dependencies: - lru-cache "^6.0.0" - -set-cookie-serde@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/set-cookie-serde/-/set-cookie-serde-1.0.0.tgz#bcf9c260ed2212ac4005a53eacbaaa37c07ac452" - integrity sha512-Vq8e5GsupfJ7okHIvEPcfs5neCo7MZ1ZuWrO3sllYi3DOWt6bSSCpADzqXjz3k0fXehnoFIrmmhty9IN6U6BXQ== - -shallow-clone-shim@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shallow-clone-shim/-/shallow-clone-shim-2.0.0.tgz#b62bf55aed79f4c1430ea1dc4d293a193f52cf91" - integrity sha512-YRNymdiL3KGOoS67d73TEmk4tdPTO9GSMCoiphQsTcC9EtC+AOmMPjkyBkRoCJfW9ASsaZw1craaiw1dPN2D3Q== - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -sonic-boom@^1.0.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-1.4.1.tgz#d35d6a74076624f12e6f917ade7b9d75e918f53e" - integrity sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg== - dependencies: - atomic-sleep "^1.0.0" - flatstr "^1.0.12" - -source-map-js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== - -source-map@^0.8.0-beta.0: - version "0.8.0-beta.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" - integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== - dependencies: - whatwg-url "^7.0.0" - -sql-summary@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sql-summary/-/sql-summary-1.0.1.tgz#a2dddb5435bae294eb11424a7330dc5bafe09c2b" - integrity sha512-IpCr2tpnNkP3Jera4ncexsZUp0enJBLr+pHCyTweMUBrbJsTgQeLWx1FXLhoBj/MvcnUQpkgOn2EY8FKOkUzww== - -stackframe@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" - integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== - -stream-chopper@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/stream-chopper/-/stream-chopper-3.0.1.tgz#73791ae7bf954c297d6683aec178648efc61dd75" - integrity sha512-f7h+ly8baAE26iIjcp3VbnBkbIRGtrvV0X0xxFM/d7fwLTYnLzDPTXRKNxa2HZzohOrc96NTrR+FaV3mzOelNA== - dependencies: - readable-stream "^3.0.6" - -string-similarity@^4.0.1: - version "4.0.4" - resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.4.tgz#42d01ab0b34660ea8a018da8f56a3309bb8b2a5b" - integrity sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ== - -string.prototype.matchall@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" - integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - regexp.prototype.flags "^1.4.1" - side-channel "^1.0.4" - -string.prototype.trimend@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" - integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - -string.prototype.trimstart@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" - integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== - -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -styled-jsx@5.0.7: - version "5.0.7" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.7.tgz#be44afc53771b983769ac654d355ca8d019dff48" - integrity sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA== - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -to-source-code@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/to-source-code/-/to-source-code-1.0.2.tgz#dd136bdb1e1dbd80bbeacf088992678e9070bfea" - integrity sha512-YzWtjmNIf3E75eZYa7m1SCyl0vgOGoTzdpH3svfa8SUm5rqTgl9hnDolrAGOghCF9P2gsITXQoMrlujOoz+RPw== - dependencies: - is-nil "^1.0.0" - -tr46@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" - integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA== - dependencies: - punycode "^2.1.0" - -traverse@^0.6.6: - version "0.6.7" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.7.tgz#46961cd2d57dd8706c36664acde06a248f1173fe" - integrity sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg== - -tsconfig-paths@^3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.1" - minimist "^1.2.6" - strip-bom "^3.0.0" - -tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== - -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -unbox-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" - integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== - dependencies: - call-bind "^1.0.2" - has-bigints "^1.0.2" - has-symbols "^1.0.3" - which-boxed-primitive "^1.0.2" - -unicode-byte-truncate@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unicode-byte-truncate/-/unicode-byte-truncate-1.0.0.tgz#aa6f0f3475193fe20c320ac9213e36e62e8764a7" - integrity sha512-GQgHk6DodEoKddKQdjnv7xKS9G09XCfHWX0R4RKht+EbUMSiVEmtWHGFO8HUm+6NvWik3E2/DG4MxTitOLL64A== - dependencies: - is-integer "^1.0.6" - unicode-substring "^0.1.0" - -unicode-substring@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/unicode-substring/-/unicode-substring-0.1.0.tgz#6120ce3c390385dbcd0f60c32b9065c4181d4b36" - integrity sha512-36Xaw9wXi7MB/3/EQZZHkZyyiRNa9i3k9YtPAz2KfqMVH2xutdXyMHn4Igarmnvr+wOrfWa/6njhY+jPpXN2EQ== - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -use-sync-external-store@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" - integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== - -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - -whatwg-url@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" - integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/package.json b/package.json index dac6fd62832..74ef9abfb21 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "lint:yaml-files": "./dev-utils/lint-yaml-files.sh # requires node >=10", "coverage": "COVERAGE=true ./test/script/run_tests.sh", "test": "./test/script/run_tests.sh", - "test:deps": "dependency-check start.js index.js 'lib/**/*.js' 'test/**/*.js' '!test/instrumentation/modules/next/a-nextjs-app' --no-dev -i async_hooks -i perf_hooks -i parseurl -i node:http", + "test:deps": "dependency-check index.js start.js start-next.js 'lib/**/*.js' 'test/**/*.js' '!test/instrumentation/modules/next/a-nextjs-app' --no-dev -i async_hooks -i perf_hooks -i parseurl -i node:http", "test:tav": "tav --quiet && (cd test/instrumentation/modules/next/a-nextjs-app && tav --quiet)", "test:docs": "./test/script/docker/run_docs.sh", "test:types": "tsc --project test/types/tsconfig.json && tsc --project test/types/transpile/tsconfig.json && node test/types/transpile/index.js && tsc --project test/types/transpile-default/tsconfig.json && node test/types/transpile-default/index.js", @@ -38,8 +38,10 @@ "lib", "types", "start.js", + "start-next.js", "index.d.ts", - "start.d.ts" + "start.d.ts", + "start-next.d.ts" ], "repository": { "type": "git", diff --git a/start-next.d.ts b/start-next.d.ts new file mode 100644 index 00000000000..b5ac73aa553 --- /dev/null +++ b/start-next.d.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +import * as agent from './'; +export = agent; diff --git a/start-next.js b/start-next.js new file mode 100644 index 00000000000..1822bd22063 --- /dev/null +++ b/start-next.js @@ -0,0 +1,17 @@ +'use strict' + +// Use this module via `node --require=elastic-apm-node/start-next ...` +// monitor a Next.js app with Elastic APM. + +const apm = require('./').start() + +// Flush APM data on server process termination. +// https://nextjs.org/docs/deployment#manual-graceful-shutdowns +process.env.NEXT_MANUAL_SIG_HANDLE = 1 +function flushApmAndExit () { + apm.flush(() => { + process.exit(0) + }) +} +process.on('SIGTERM', flushApmAndExit) +process.on('SIGINT', flushApmAndExit) From 10ff2d56f148b0372ba81e719376778341733cf9 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Tue, 18 Oct 2022 13:54:33 -0700 Subject: [PATCH 43/66] fix lint --- start-next.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/start-next.js b/start-next.js index 1822bd22063..011e19cf099 100644 --- a/start-next.js +++ b/start-next.js @@ -1,3 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + 'use strict' // Use this module via `node --require=elastic-apm-node/start-next ...` From f267e38bba8ca644a5ea2bf58cecbe88cad0021e Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Tue, 18 Oct 2022 15:33:18 -0700 Subject: [PATCH 44/66] good for now for docs --- docs/nextjs.asciidoc | 201 +++++++++++++++++++++---------------------- docs/set-up.asciidoc | 16 ++-- start-next.js | 2 + 3 files changed, 108 insertions(+), 111 deletions(-) diff --git a/docs/nextjs.asciidoc b/docs/nextjs.asciidoc index 2643c1a72f8..4fbb05c572f 100644 --- a/docs/nextjs.asciidoc +++ b/docs/nextjs.asciidoc @@ -11,33 +11,33 @@ endif::[] The Elastic APM Node.js agent can be used to trace the Next.js server (`next start` or `next dev`) that runs your application without the need for code -changes to your app. APM transactions for incoming HTTP requests to the server -will be named for the https://nextjs.org/docs/routing/introduction[pages] and -https://nextjs.org/docs/api-routes/introduction[API endpoints] in your -application, as well as for internal routes used by Next.js. Errors in -application code run on the server will be reported for viewing in the Kibana -APM app. +changes to your app. The APM transactions for incoming HTTP requests to the +server will be named for the https://nextjs.org/docs/routing/introduction[pages] +and https://nextjs.org/docs/api-routes/introduction[API endpoints] in your +application, as well as for internal routes used by Next.js. Errors in code run +on the server will be reported for viewing in the Kibana APM app. -[float] -[[nextjs-status]] -==== Current status - -The Next.js instrumentation is *experimental* while we solicit feedback from -Next.js users. How the instrumentation works might change significantly in -future versions of the agent. +Note that the Node.js APM agent can only instrument _server-side_ code. To +monitor the client-side parts of a Next.js application, see the +https://www.elastic.co/guide/en/apm/agent/rum-js/current/intro.html[Elastic RUM agent]. -If you are a Next.js user, please help us provide a better Next.js observability -experience with your feedback on our -https://discuss.elastic.co/tags/c/apm/nodejs[Discuss forum]. +NOTE: This Next.js instrumentation is a _technical preview_ while we solicit feedback +from Next.js users. That means that how the instrumentation works might change +in future versions of the agent. If you are a Next.js user, please help us provide a better Next.js observability experience with your feedback on our https://discuss.elastic.co/tags/c/apm/nodejs[Discuss forum]. [float] [[nextjs-prerequisites]] ==== Prerequisites -You need an APM Server to send APM data to. Follow the {apm-guide-ref}/apm-quick-start.html[APM Quick start] if you have not set one up yet. You will need your *APM server url* and an APM server *secret token* (or *API key*) for configuring the APM agent below. +You need an APM Server to send APM data to. Follow the +{apm-guide-ref}/apm-quick-start.html[APM Quick start] if you have not set one up +yet. You will need your *APM server url* and an APM server *secret token* (or +*API key*) for configuring the APM agent below. -You will also need a Next.js application to monitor. If you do not have an existing one to use, you can use the following to create a starter app (see https://nextjs.org/docs/getting-started[Next.js Getting Started docs] for more): +You will also need a Next.js application to monitor. If you do not have an +existing one to use, you can use the following to create a starter app (see +https://nextjs.org/docs/getting-started[Next.js Getting Started docs] for more): [source,bash] ---- @@ -45,10 +45,9 @@ npx create-next-app@latest # use the defaults cd my-app ---- -// XXX update link - -You can also take a look at and use this https://github.com/elastic/apm-agent-nodejs/tree/trentm/feat-nextjs/examples/nextjs/[Next.js + Elastic APM example]. +You can also take a look at and use this https://github.com/elastic/apm-agent-nodejs/tree/main/examples/nextjs/[Next.js + Elastic APM example app]. +NOTE: XXX Until this is merged, the example is at: [float] [[nextjs-setup]] @@ -67,139 +66,133 @@ NOTE: XXX Until this is in a release use `npm install "elastic-apm-node@elastic/ [float] ==== Step 2: Start the APM agent -For the APM agent to instrument the Next.js server, it needs to be started before the Next.js server code is loaded. The best way to do so is by using Node's https://nodejs.org/api/cli.html#-r---require-module[`--require`] option. We could use <> to start the APM agent. However, to integrate a little better with Next.js, we will create a separate "apmsetup.js" file which does a little more than just start the agent. +For the APM agent to instrument the Next.js server, it needs to be started +before the Next.js server code is loaded. The best way to do so is by using +Node's https://nodejs.org/api/cli.html#-r---require-module[`--require`] option +to load the "elastic-apm-node/start-next.js" module -- this will start the agent +(plus a little more for Next.js integration). -Edit the "dev" and "start" scripts in your "package.json" to tell Node to require the "apmsetup.js" module on startup. Like this: +Edit the "dev" and "start" scripts in your "package.json" as follows: -[source,diff] +[source,json] ---- -diff --git a/package.json b/package.json ---- a/package.json -+++ b/package.json -@@ -3,9 +3,9 @@ - "version": "0.1.0", - "private": true, - "scripts": { -- "dev": "next dev", -+ "dev": "NODE_OPTIONS=--require=./apmsetup.js next dev", - "build": "next build", -- "start": "next start", -+ "start": "NODE_OPTIONS=--require=./apmsetup.js next start", - "lint": "next lint" - }, - "dependencies": { +{ + // ... + "scripts": { + "dev": "NODE_OPTIONS=--require=elastic-apm-node/start-next.js next dev", + "build": "next build", + "start": "NODE_OPTIONS=--require=elastic-apm-node/start-next.js next start", + "lint": "next lint" + }, + // ... +} ---- -Then save the following as "apmsetup.js": + +[float] +==== Step 3: Configure the APM agent + +The APM agent can be +https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuring-the-agent.html[configured] +with environment variables or with an "elastic-apm-node.js" module in the +current working directory. Note that because the APM agent is being loaded +before the Next.js server, the +https://nextjs.org/docs/basic-features/environment-variables[Next.js-supported +".env" files] *cannot* be used to configure the APM agent. We will use an +"elastic-apm-node.js" file here. + +Create an "elastic-apm-node.js" file in the application root with the APM server +url and secret token values from the <> section above: [source,javascript] ---- -'use strict' - -// Use this script with Node's `--require` flag to monitor a Next.js app with -// Elastic APM. - -// Support `ELASTIC_APM_...` envvars in ".env*" files as Next.js supports them. -// https://nextjs.org/docs/basic-features/environment-variables -try { - const { loadEnvConfig } = require('@next/env') - const isDev = process.argv[process.argv.length - 1] === 'dev' - loadEnvConfig(__dirname, isDev) // <1> -} catch (envErr) { - console.error('apmsetup: warning: failed to load @next/env to read possible .env files') -} - -if (!process.env.ELASTIC_APM_SERVER_URL) { - console.log('apmsetup: ELASTIC_APM_SERVER_URL is not set, disabling APM') -} else { - // APM agent configuration can be passed to `.start()` or specified as - // environment variables. - // https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html - const apm = require('elastic-apm-node').start() // <2> - - // Flush APM data on server process termination. - // https://nextjs.org/docs/deployment#manual-graceful-shutdowns - process.env.NEXT_MANUAL_SIG_HANDLE = 1 // <3> - function flushApmAndExit () { - apm.flush(() => { - process.exit(0) - }) - } - process.on('SIGTERM', flushApmAndExit) - process.on('SIGINT', flushApmAndExit) +// elastic-apm-node.js +module.exports = { + serverUrl: 'https://...', // E.g. https://my-deployment-name.apm.us-west2.gcp.elastic-cloud.com + secretToken: '...' } ---- -<1> Read environment variables from ".env*" files https://nextjs.org/docs/basic-features/environment-variables[in the same way as the Next.js server]. -<2> If an Elastic APM server URL is configured, then start the APM agent. -<3> Handle flushing any final APM tracing data on https://nextjs.org/docs/deployment#manual-graceful-shutdowns[graceful termination of the server]. - -[float] -==== Step 3: Configure the APM agent - -The "apmsetup.js" module supports ".env*" files, so a natural way to configure the APM agent is using those. Create a ".env.local" file with the APM server url and secret token from the <> section above: +The equivalent using environment variables is: [source,bash] ---- -# .env.local -ELASTIC_APM_SERVER_URL=https://... # E.g. https://my-deployment-name.apm.us-west2.gcp.elastic-cloud.com -ELASTIC_APM_SECRET_TOKEN='...' +export ELASTIC_APM_SERVER_URL='https://...' +export ELASTIC_APM_SECRET_TOKEN='...' ---- -See https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html[the APM configuration guide] for supported configuration variables. See https://nextjs.org/docs/basic-features/environment-variables[the Next.js docs] for more on its support for ".env" files. +See https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html[the +APM configuration guide] for full details on supported configuration variables. [float] -==== Step 4: Start your Next.js app and verify monitoring is working +==== Step 4: Start your Next.js app [source,bash] ---- npm run dev # or 'npm run build && npm start' for the production server ---- -Open in your browser to load your Next.js app. If you used the `create-next-app` tool above, it creates a [http://localhost:3000/api/hello]/api/hello API endpoint. You can provide some artificial load by running the following in a separate terminal: +Open in your browser to load your Next.js app. If you +used the `create-next-app` tool above, it defines an +http://localhost:3000/api/hello[/api/hello] API endpoint. You can provide some +artificial load by running the following in a separate terminal: [source,bash] ---- while true; do sleep 1; curl -i http://localhost:3000/api/hello; done ---- -Visit your Kibana APM app and, after a few seconds, you should see a service entry for your Next.js app. (The service name will be pulled from the "name" field in "package.json". It can be overriden with https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html#service-name[`ELASTIC_APM_SERVICE_NAME`].) +Visit your Kibana APM app and, after a few seconds, you should see a service +entry for your Next.js app. (The service name will be pulled from the "name" +field in "package.json". It can be overriden with +https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html#service-name[`serviceName`].) Here is an example: image::./images/nextjs-my-app-screenshot.png[Kibana APM app showing Next.js my-app] -XXX screenshot (can we add screenshots to docs, or too brittle for UI changes?) - [float] [[nextjs-limitations]] -==== Next.js instrumentation limitations +==== Limitations and future work + +This Next.js instrumentation has some limitations to be aware of. + +Next.js build tooling bundles dependencies (using Webpack) for both client _and_ +server-side code execution. The Node.js APM agent does not work when bundled. +See <> for details. The implication for Next.js instrumentation +is that you cannot directly import and use the APM agent in your code. That +means that using the <> for manual instrumentation is not currently +possible. -XXX limitations +This instrumentation supports naming APM transactions for many internal Next.js +routes. For example, for +https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props[server-side +rendering (SSR)] Next.js client code will make requests of the form `GET +/next/_data/$buildId/$page.json`, for which the APM agent names the transaction +`Next.js _next/data route $page`. However, there is a limitation with the +Next.js "public folder catchall" route. HTTP requests that resolve to files in +your "public/" directory, for example `GET /favicon.ico`, will result in a +transaction named `GET unknown route`. See <> below. + +If you notice other limitations or have any suggestions, please give us feedback +on our https://discuss.elastic.co/tags/c/apm/nodejs[Discuss forum]. -XXX feedback request [float] [[nextjs-performance-monitoring]] ==== Performance monitoring -include::./shared-set-up.asciidoc[tag=performance-monitoring] +Elastic APM automatically measures the performance of your Next.js application. +It records spans for database queries, external HTTP requests, and other slow +operations that happen during requests to your Next.js app. Spans are grouped in +transactions -- by default one for each incoming HTTP request. [float] [[nextjs-unknown-routes]] -===== Unknown routes +==== Unknown routes include::./shared-set-up.asciidoc[tag=unknown-roots] -XXX Note the limitation on the Next.js catchall route. - - -[float] -[[nextjs-error-logging]] -==== Error logging - -include::./shared-set-up.asciidoc[tag=error-logging] - [float] [[nextjs-filter-sensitive-information]] ==== Filter sensitive information diff --git a/docs/set-up.asciidoc b/docs/set-up.asciidoc index c1c5bb938dc..4f3a377cff8 100644 --- a/docs/set-up.asciidoc +++ b/docs/set-up.asciidoc @@ -6,13 +6,14 @@ To get you off the ground, we've prepared guides for setting up the Agent with a // This tagged region is used throughout the documentation to link to the framework guides // Updates made here will be applied elsewhere as well. // tag::web-frameworks-list[] +* <> * <> +* <> * <> * <> +* <> * <> -* <> * <> -* <> * <> // end::web-frameworks-list[] @@ -27,23 +28,24 @@ Other useful documentation includes: * <> * <> +include::./lambda.asciidoc[] + include::./express.asciidoc[] +include::./fastify.asciidoc[] + include::./hapi.asciidoc[] include::./koa.asciidoc[] -include::./restify.asciidoc[] +include::./nextjs.asciidoc[] -include::./fastify.asciidoc[] +include::./restify.asciidoc[] include::./typescript.asciidoc[] include::./custom-stack.asciidoc[] -include::./lambda.asciidoc[] - -include::./nextjs.asciidoc[] [[starting-the-agent]] === Starting the agent diff --git a/start-next.js b/start-next.js index 011e19cf099..dbf9a1dcf0d 100644 --- a/start-next.js +++ b/start-next.js @@ -21,3 +21,5 @@ function flushApmAndExit () { } process.on('SIGTERM', flushApmAndExit) process.on('SIGINT', flushApmAndExit) + +module.exports = apm From e0c7ac030b656d291e481cba9435dc4dc2265eb6 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Tue, 18 Oct 2022 15:45:04 -0700 Subject: [PATCH 45/66] get test Next.js app to use start-next.js starter module --- .../modules/next/a-nextjs-app/apmsetup.js | 34 ------------------- .../modules/next/a-nextjs-app/package.json | 6 ++-- 2 files changed, 3 insertions(+), 37 deletions(-) delete mode 100644 test/instrumentation/modules/next/a-nextjs-app/apmsetup.js diff --git a/test/instrumentation/modules/next/a-nextjs-app/apmsetup.js b/test/instrumentation/modules/next/a-nextjs-app/apmsetup.js deleted file mode 100644 index 23d4d3f2a34..00000000000 --- a/test/instrumentation/modules/next/a-nextjs-app/apmsetup.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and other contributors where applicable. - * Licensed under the BSD 2-Clause License; you may not use this file except in - * compliance with the BSD 2-Clause License. - */ - -const apm = require('../../../../../').start({ // elastic-apm-node - // XXX most of these for dev/debugging - // apmServerVersion: '8.4.1', - // stackTraceLimit: 10, - cloudProvider: 'none', - centralConfig: false, - // metricsInterval: '0s', - useElasticTraceparentHeader: false, // XXX - // captureExceptions: false, // XXX - logUncaughtExceptions: true, - // usePathAsTransactionName: true, - apiRequestTime: '3s' - // logLevel: 'debug' -}) - -// Flush APM data on server process termination. -// https://nextjs.org/docs/deployment#manual-graceful-shutdowns -// -// XXX what version was `NEXT_MANUAL_SIG_HANDLE` added in? -// https://github.com/vercel/next.js/discussions/19693 -process.env.NEXT_MANUAL_SIG_HANDLE = 1 -function flushApmAndExit () { - apm.flush(() => { - process.exit(0) - }) -} -process.on('SIGTERM', flushApmAndExit) -process.on('SIGINT', flushApmAndExit) diff --git a/test/instrumentation/modules/next/a-nextjs-app/package.json b/test/instrumentation/modules/next/a-nextjs-app/package.json index 3ecfa7fafa9..eb021b830e5 100644 --- a/test/instrumentation/modules/next/a-nextjs-app/package.json +++ b/test/instrumentation/modules/next/a-nextjs-app/package.json @@ -1,12 +1,12 @@ { "name": "a-nextjs-app", "version": "1.0.0", - "description": "a Next.js-usage app to test elastic-apm-node instrumentation", + "description": "a Next.js app to test elastic-apm-node instrumentation", "private": true, "scripts": { - "dev": "NODE_OPTIONS='-r ./apmsetup.js' next dev", + "dev": "NODE_OPTIONS='-r ../../../../../start-next.js' next dev", "build": "next build", - "start": "NODE_OPTIONS='-r ./apmsetup.js' next start" + "start": "NODE_OPTIONS='-r ../../../../../start-next.js' next start" }, "keywords": [], "license": "BSD-2-Clause", From 73f581661bd8946b62be887c718b735ec1f4e149 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Tue, 18 Oct 2022 16:03:06 -0700 Subject: [PATCH 46/66] missed part of converting the test Next.js app over the using 'start-next.js' --- test/instrumentation/modules/next/next.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/instrumentation/modules/next/next.test.js b/test/instrumentation/modules/next/next.test.js index da044bb5cf6..ca041aef429 100644 --- a/test/instrumentation/modules/next/next.test.js +++ b/test/instrumentation/modules/next/next.test.js @@ -613,7 +613,7 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { shell: os.platform() === 'win32', cwd: testAppDir, env: Object.assign({}, process.env, { - NODE_OPTIONS: '-r ./apmsetup.js', + NODE_OPTIONS: '-r ../../../../../start-next.js', ELASTIC_APM_SERVER_URL: serverUrl }) } @@ -706,7 +706,7 @@ tape.test('-- dev server tests --', { skip: false /* XXX */ }, suite => { shell: os.platform() === 'win32', cwd: testAppDir, env: Object.assign({}, process.env, { - NODE_OPTIONS: '-r ./apmsetup.js', + NODE_OPTIONS: '-r ../../../../../start-next.js', ELASTIC_APM_SERVER_URL: serverUrl }) } From ac78bf4b5439866628075063c02931549f521a60 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 19 Oct 2022 15:07:00 -0700 Subject: [PATCH 47/66] next instr docs; clearing out XXXs --- examples/nextjs/README.md | 40 ++++------ examples/nextjs/elastic-apm-node.js.template | 5 ++ examples/nextjs/package.json | 1 + lib/instrumentation/index.js | 4 - lib/instrumentation/modules/next/README.md | 74 +++++++++++++++++++ .../next/dist/server/api-utils/node.js | 4 +- .../next/dist/server/dev/next-dev-server.js | 2 + .../modules/next/dist/server/next-server.js | 42 ++--------- lib/instrumentation/transaction.js | 1 - .../next/a-nextjs-app/components/Header.js | 2 - .../modules/next/a-nextjs-app/next.config.js | 1 - .../pages/a-dynamic-page/[num].js | 3 - .../instrumentation/modules/next/next.test.js | 61 +++++++-------- 13 files changed, 136 insertions(+), 104 deletions(-) create mode 100644 examples/nextjs/elastic-apm-node.js.template create mode 100644 lib/instrumentation/modules/next/README.md diff --git a/examples/nextjs/README.md b/examples/nextjs/README.md index f97dd5a24b3..998b3ba2b8b 100644 --- a/examples/nextjs/README.md +++ b/examples/nextjs/README.md @@ -1,38 +1,30 @@ -This is a [Next.js](https://nextjs.org/) project +This is a [Next.js](https://nextjs.org/) application 1. bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app), and then -2. lightly modified to use Elastic APM to monitor the Next server. +2. modified to use the Elastic APM Node.js agent to monitor the Next server. ## Getting Started -XXX clear this mostly out, relevant docs for learning Next and for getting started with APM +1. `npm install` -First, run the development server: +2. Configure an APM server URL and token for the APM agent. See the [APM Quick start](https://www.elastic.co/guide/en/apm/guide/current/apm-quick-start.html) for setting up an Elastic Stack. -```bash -npm run dev -# or -yarn dev -``` + ```bash + cp elastic-apm-node.js.template elastic-apm-node.js + vi elastic-apm-node.js + ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. +3. Run the Next.js server: -[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. + ```bash + npm run dev # the development server + npm run build && npm start # or the production server + ``` -The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +An [API route](https://nextjs.org/docs/api-routes/introduction) can be accessed at . ## Learn More -To learn more about Next.js, take a look at the following resources: - +- [Get started with Next.js and Elastic APM](https://www.elastic.co/guide/en/apm/agent/nodejs/master/nextjs.html) - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/examples/nextjs/elastic-apm-node.js.template b/examples/nextjs/elastic-apm-node.js.template new file mode 100644 index 00000000000..c973ab37dc3 --- /dev/null +++ b/examples/nextjs/elastic-apm-node.js.template @@ -0,0 +1,5 @@ +module.exports = { + // Configure the APM server URL and token that the APM agent should use. + // serverUrl: 'https://...', // e.g.: http://my-deployment-name.apm.us-west2.gcp.elastic-cloud.com', + // secretToken: '...' +} diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index a3eb873e34b..fa8fcbf7224 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -8,6 +8,7 @@ "start": "NODE_OPTIONS=--require=elastic-apm-node/start-next.js next start", "lint": "next lint" }, + "XXX": "change to 'main' branch for merge, and issue to update this on release", "dependencies": { "elastic-apm-node": "elastic/apm-agent-nodejs#trentm/feat-nextjs", "next": "12.3.1", diff --git a/lib/instrumentation/index.js b/lib/instrumentation/index.js index 03f62e718d5..7882b685671 100644 --- a/lib/instrumentation/index.js +++ b/lib/instrumentation/index.js @@ -324,10 +324,6 @@ Instrumentation.prototype._patchModule = function (exports, name, version, enabl continue } - // XXX This doesn't catch, and should. If there are multiple patches for a - // given module, then a crash in an early one will break usage of the - // subsequent ones. Also, I'm not sure of the impact on the `hook()` - // call. The failure here is also silent, so hard to notice. exports = patch(exports, this._agent, { version, enabled }) } } diff --git a/lib/instrumentation/modules/next/README.md b/lib/instrumentation/modules/next/README.md new file mode 100644 index 00000000000..e55a5404cbf --- /dev/null +++ b/lib/instrumentation/modules/next/README.md @@ -0,0 +1,74 @@ +# Next.js instrumentation dev notes + +Instrumenting the Next.js servers (the dev and prod servers) involves shimming +a number of files. An overview of the instrumentation is provided here. +Some of the complexity here is that I found the Next.js server internals not +always ammenable to instrumentation. There isn't a clean separation between +"determine the route", "run the handler for that route", "expose an error +or the result". + +There are a number of ways to deploy (https://nextjs.org/docs/deployment) a +Next.js app. This instrumentation works with "Self-Hosting", and using Next.js's +built-in server. There is a Next.js "server" class hierarchy: + + class Server (in base-server.ts) + class NextNodeServer (in next-server.ts, used for `next start`) + class DevServer (in dev/next-dev-server.js, used for `next dev`) + + +## dist/server/next-server.js + +This file in the "next" package implements the `NextNodeServer`, the Next.js +"production" server used by `next start`. Most instrumentation is on this class. + +The Next.js server is a vanilla Node.js `http.createServer` (http-only, https +termination isn't supported) using `NextNodeServer.handleRequest` as the request +handler, so every request to the server is a call to that method. + +User routes are defined by files under "pages/". Generally, an incoming request path: + GET /a-page +is resolved to one of those pages: + ./pages/a-page.js +The user files under "./pages/" are loaded by `NextNodeServer.findPageComponents`. +**We instrument `findPageComponents` to capture the resolved page name to use +for the transaction name.** + +There are also other built-in routes to handle redirects, rewrites, static-file +serving (e.g. `GET /favicon.ico -> ./public/favicon.ico`), and various internal +`/_next/...` routes used by the Next.js client code for bundle loading, +server-side generated page data, etc. At server start, a call to +`.generateRoutes()` is called which returns a somewhat regular data structure +with routing data. **We instrument *most* of these routes to set +`transaction.name` appropriately for most of these internal routes.** A notable +limitation is the `public folder catchall` route that could not be cleanly +instrumented. + +An error in rendering a page results in `renderErrorToResponse(err)` being +called to handle that error. **We instrument `renderErrorToResponse` to +`apm.captureError()` those errors.** (Limitation: There are some edge cases +where this method is not used to handle an exception. This instrumentation isn't +capturing those.) + +*API* routes ("pages/api/...") are handled differently from other pages. +The `catchAllRoute` route handler calls `handleApiRequest`, which resolves +the URL path to a possibly dynamic route name (e.g. `/api/widgets/[id]`, +**we instrument `ensureApiPage` to get that resolve route name**), loads the +webpack-compiled user module for that route, and calls `apiResolver` in +"api-utils/node.ts" to execute. **We instrument that `apiResolve()` function +to capture any errors in the user's handler.** + + +## dist/server/dev/next-dev-server.js + +This file defines the `DevServer` used by `next dev`. It subclasses +`NextNodeServer`. The instrumentation in this file is **very** similar to that +in "next-server.js". However, some of it needs to be repeated on the `DevServer` +class to capture results specific to the dev-server. For example +`DevServer.generateRoutes()` includes some additional routes. + + +## dist/server/api-utils/node.js + +See the `apiResolve()` mention above. + + diff --git a/lib/instrumentation/modules/next/dist/server/api-utils/node.js b/lib/instrumentation/modules/next/dist/server/api-utils/node.js index 261ecb262bb..bd069d75cde 100644 --- a/lib/instrumentation/modules/next/dist/server/api-utils/node.js +++ b/lib/instrumentation/modules/next/dist/server/api-utils/node.js @@ -6,9 +6,7 @@ 'use strict' -// XXX comment this module is solely about capturing possible error in a user's API handler. -// It is admittedly a lot of code for just that. In a DevServer we *do* also get a -// renderErrorToResponse, but not in a prod server. +// See "lib/instrumentation/modules/next/README.md". const semver = require('semver') diff --git a/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js index 8ab542f370a..758590840be 100644 --- a/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js +++ b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js @@ -6,6 +6,8 @@ 'use strict' +// See "lib/instrumentation/modules/next/README.md". + const semver = require('semver') const shimmer = require('../../../../../shimmer') diff --git a/lib/instrumentation/modules/next/dist/server/next-server.js b/lib/instrumentation/modules/next/dist/server/next-server.js index 47db6a04c28..4db1ba7a7e1 100644 --- a/lib/instrumentation/modules/next/dist/server/next-server.js +++ b/lib/instrumentation/modules/next/dist/server/next-server.js @@ -6,37 +6,7 @@ 'use strict' -// XXX where to have discussion on duplication (almost) between this and next-dev-server.js - -// XXX this should go somewhere else? -// The main (but not only) module for instrumenting the Next.js server. -// -// Some notes on how the Next.js node server and the instrumentation works. -// -// - There are a number of ways to deploy (https://nextjs.org/docs/deployment) -// a Next.js app. This instrumentation works with "Self-Hosting", and using -// Next.js's built-in server (`class NextNodeServer`). This is the server -// that is used for `next build && next start` and a subclass of that server -// for `next dev`. -// - The Next.js server is a vanilla Node.js `http.createServer` using -// `NextNodeServer.handleRequest` as the request handler, so every request -// to the server is a call to that method. -// - Routes are defined by files under "pages/". An incoming request path is -// resolved a built-in Next.js route handler or one of those pages -- loaded -// by `NextNodeServer.findPageComponents`. -// - An error in rendering a page results in `renderErrorToResponse(err)` being -// called to handle that error. (Limitation: There are some edge cases where -// this method is not used to handle an exception. This instrumentation isn't -// capturing those.) -// - *API* routes ("pages/api/...") are handled differently from other pages. -// The `catchAllRoute` route handler calls `handleApiRequest`, which resolves -// the URL path to a possibly dynamic route name (e.g. `/api/widgets/[id]`, -// we instrument `ensureApiPage` to get that resolve route name), loads the -// webpack-compiled user module for that route, and calls `apiResolver` in -// "api-utils/node.ts" to execute. We instrument that `apiResolve()` function -// to capture any errors in the user's handler. -// - There is open discussion here for other ways to support error capture -// for Next.js: https://github.com/vercel/next.js/discussions/32230 +// See "lib/instrumentation/modules/next/README.md". const semver = require('semver') @@ -50,11 +20,9 @@ const kCapturedTransErr = Symbol.for('nextJsCapturedTransErr') const noopFn = () => {} module.exports = function (mod, agent, { version, enabled }) { - console.log('XXX hi in "next-server" instr') if (!enabled) { return mod } - // XXX could share this block if (!semver.satisfies(version, '>=11.1.0 <13.0.0')) { agent.logger.debug('next version %s not supported, skipping', version) return mod @@ -177,9 +145,11 @@ module.exports = function (mod, agent, { version, enabled }) { const trans = ins.currTransaction() if (trans && !trans[kSetTransNameFn]) { trans[kSetTransNameFn] = (req, pathname) => { - console.log('XXX kSetTransNameFn called for wrapCatchAllRoute: pathname=%s', pathname) trans.setDefaultName(`${req.method} ${pathname}`) - console.log('XXX QQQ') // why not _error load re-call of this? + // Ensure only the first `findPageComponents` result sets the trans + // name, otherwise a loaded `/_error` for page error handling could + // incorrectly override. + trans[kSetTransNameFn] = noopFn } } return origRouteFn.apply(this, arguments) @@ -229,7 +199,7 @@ module.exports = function (mod, agent, { version, enabled }) { function wrapRenderErrorToResponse (orig) { return function wrappedRenderErrorToResponse (ctx, err) { - // XXX + // XXX 'splain // if (this.constructor !== NextNodeServer) { // console.log('XXX wrappedRenderErrorToResponse -> ctor != NextNodeServer abort') // return orig.apply(this, arguments) diff --git a/lib/instrumentation/transaction.js b/lib/instrumentation/transaction.js index 69ad8e4f86f..f66e8b31707 100644 --- a/lib/instrumentation/transaction.js +++ b/lib/instrumentation/transaction.js @@ -313,7 +313,6 @@ Transaction.prototype._encode = function () { } Transaction.prototype.setDefaultName = function (name) { - console.log('XXX Transaction.setDefaultName(name=%s) ended? %s', name, this.ended) this._agent.logger.debug('setting default transaction name: %s %o', name, { trans: this.id, parent: this.parentId, trace: this.traceId }) this._defaultName = name } diff --git a/test/instrumentation/modules/next/a-nextjs-app/components/Header.js b/test/instrumentation/modules/next/a-nextjs-app/components/Header.js index f940a9b37e8..103bfe3a070 100644 --- a/test/instrumentation/modules/next/a-nextjs-app/components/Header.js +++ b/test/instrumentation/modules/next/a-nextjs-app/components/Header.js @@ -7,8 +7,6 @@ import Head from 'next/head' import Link from 'next/link' -// XXX v demo app inits @elastic/apm-rum here - function Header () { return ( <> diff --git a/test/instrumentation/modules/next/a-nextjs-app/next.config.js b/test/instrumentation/modules/next/a-nextjs-app/next.config.js index 91844733d52..de686f23cbb 100644 --- a/test/instrumentation/modules/next/a-nextjs-app/next.config.js +++ b/test/instrumentation/modules/next/a-nextjs-app/next.config.js @@ -32,7 +32,6 @@ const nextConfig = { destination: '/a-page' }, { - // XXX improve this to have dynamic value in params source: '/rewrite-to-a-dynamic-page/:num', destination: '/a-dynamic-page/:num' }, diff --git a/test/instrumentation/modules/next/a-nextjs-app/pages/a-dynamic-page/[num].js b/test/instrumentation/modules/next/a-nextjs-app/pages/a-dynamic-page/[num].js index 6b57c73e24e..f6a6bb7f32f 100644 --- a/test/instrumentation/modules/next/a-nextjs-app/pages/a-dynamic-page/[num].js +++ b/test/instrumentation/modules/next/a-nextjs-app/pages/a-dynamic-page/[num].js @@ -10,7 +10,6 @@ import Header from '../../components/Header' // Run at build time to determine a set of dynamic paths to prerender at build time. // https://nextjs.org/docs/basic-features/data-fetching/get-static-paths export async function getStaticPaths () { - console.log('XXX ADynamicPage.getStaticPaths') return { paths: [ { params: { num: '41' } }, @@ -22,7 +21,6 @@ export async function getStaticPaths () { } export async function getStaticProps ({ params }) { - console.log('XXX ADynamicPage.getStaticProps') return { props: { doubleThat: Number(params.num) * 2 @@ -31,7 +29,6 @@ export async function getStaticProps ({ params }) { } const ADynamicPage = ({ doubleThat }) => { - console.log('XXX ADynamicPage') const router = useRouter() const { num } = router.query diff --git a/test/instrumentation/modules/next/next.test.js b/test/instrumentation/modules/next/next.test.js index ca041aef429..d89dc4f7373 100644 --- a/test/instrumentation/modules/next/next.test.js +++ b/test/instrumentation/modules/next/next.test.js @@ -376,7 +376,6 @@ let TEST_REQUESTS = [ // Dev Note: To limit a test run to a particular test request, provide a // string value to DEV_TEST_FILTER that matches `testName`. var DEV_TEST_FILTER = null -// DEV_TEST_FILTER = 'throw in a page handler' // XXX if (DEV_TEST_FILTER) { TEST_REQUESTS = TEST_REQUESTS.filter(testReq => ~testReq.testName.indexOf(DEV_TEST_FILTER)) assert(TEST_REQUESTS.length > 0, 'DEV_TEST_FILTER should not result in an *empty* TEST_REQUESTS') @@ -461,9 +460,6 @@ async function makeTestRequest (t, testReq, buildId) { res.on('data', chunk => { chunks.push(chunk) }) res.on('end', () => { const body = Buffer.concat(chunks) - // console.log('XXX res:', res.statusCode, res.headers, - // res.headers['content-type'] && ~res.headers['content-type'].indexOf('text') && body.toString(), - // '\n--') if (testReq.expectedRes.statusCode) { t.equal(res.statusCode, testReq.expectedRes.statusCode, `res.statusCode === ${testReq.expectedRes.statusCode}`) } @@ -535,7 +531,6 @@ function checkExpectedApmEvents (t, apmEvents) { .sort((a, b) => { return getEventField(a, 'timestamp') < getEventField(b, 'timestamp') ? -1 : 1 }) - console.log('XXX filtered and sorted apmEvents:', apmEvents) TEST_REQUESTS.forEach(testReq => { t.comment(`check APM events for "${testReq.testName}"`) // Collect all events for this transaction's trace_id, and pass that to @@ -552,25 +547,22 @@ function checkExpectedApmEvents (t, apmEvents) { // ---- tests -const SKIP_NPM_CI_FOR_DEV = false // process.env.USER === 'trentm' // XXX -if (!SKIP_NPM_CI_FOR_DEV) { - tape.test(`setup: npm ci (in ${testAppDir})`, t => { - const startTime = Date.now() - exec( - 'npm ci', - { - cwd: testAppDir - }, - function (err, stdout, stderr) { - t.error(err, `"npm ci" succeeded (took ${(Date.now() - startTime) / 1000}s)`) - if (err) { - t.comment(`$ npm ci\n-- stdout --\n${stdout}\n-- stderr --\n${stderr}\n--`) - } - t.end() +tape.test(`setup: npm ci (in ${testAppDir})`, t => { + const startTime = Date.now() + exec( + 'npm ci', + { + cwd: testAppDir + }, + function (err, stdout, stderr) { + t.error(err, `"npm ci" succeeded (took ${(Date.now() - startTime) / 1000}s)`) + if (err) { + t.comment(`$ npm ci\n-- stdout --\n${stdout}\n-- stderr --\n${stderr}\n--`) } - ) - }) -} + t.end() + } + ) +}) tape.test('setup: mock APM server', t => { nextJsVersion = require(path.join(testAppDir, 'node_modules/next/package.json')).version @@ -584,7 +576,7 @@ tape.test('setup: mock APM server', t => { }) // Test the Next "prod" server. I.e. `next build && next start`. -tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { +tape.test('-- prod server tests --', suite => { let nextServerProc suite.test('setup: npm run build', t => { @@ -605,9 +597,21 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { }) suite.test('setup: start Next.js prod server (next start)', t => { - // XXX warning using `npm run start` directly with Docker. + // Ideally we would simply spawn `npm run start` -- which handles setting + // NODE_OPTIONS. However, that results in a process tree: + // + // `- npm + // `- /tmp/.../tmp-$hash.sh + // `- node ./node_modules/.bin/next start + // that, in Docker, will reduce to: + // + // `- node ./node_modules/.bin/next start + // And our attempts to signal, `nextServerProc.kill()`, will fail to signal + // the actual server because the `npm` process is gone. nextServerProc = spawn( path.normalize('./node_modules/.bin/next'), + // Be explicit about "localhost" here, otherwise with node v18 we can + // get the server listening on IPv6 and the client connecting on IPv4. ['start', '-H', 'localhost'], { shell: os.platform() === 'win32', @@ -684,21 +688,18 @@ tape.test('-- prod server tests --', { skip: false /* XXX */ }, suite => { checkExpectedApmEvents(t, apmServer.events) t.end() }) - console.log('XXX before: pid %s is killed? %s', nextServerProc.pid, nextServerProc.killed) nextServerProc.kill('SIGTERM') - console.log('XXX sent SIGTERM to pid', nextServerProc.pid) - console.log('XXX sync after: pid %s is killed? %s', nextServerProc.pid, nextServerProc.killed) }) suite.end() }) // Test the Next "dev" server. I.e. `next dev`. -tape.test('-- dev server tests --', { skip: false /* XXX */ }, suite => { +tape.test('-- dev server tests --', suite => { let nextServerProc suite.test('setup: start Next.js dev server (next dev)', t => { - // XXX warning using `npm run dev` directly with Docker. + // See the warning notes for `spawn()` above. The same apply here. nextServerProc = spawn( path.normalize('./node_modules/.bin/next'), ['dev', '-H', 'localhost'], From dfecd43f315ee9c1b494c1de56fdb47166ea04bd Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 19 Oct 2022 15:08:44 -0700 Subject: [PATCH 48/66] clearning more XXXs --- .../modules/next/dist/server/dev/next-dev-server.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js index 758590840be..4d97bafcccb 100644 --- a/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js +++ b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js @@ -18,7 +18,6 @@ const kSetTransNameFn = Symbol.for('nextJsSetTransNameFn') const noopFn = () => {} module.exports = function (mod, agent, { version, enabled }) { - console.log('XXX hi in "next-dev-server" instr') if (!enabled) { return mod } @@ -30,8 +29,6 @@ module.exports = function (mod, agent, { version, enabled }) { const ins = agent._instrumentation const log = agent.logger - console.log('XXX mod: ', mod) - const DevServer = mod.default shimmer.wrap(DevServer.prototype, 'generateRoutes', wrapGenerateRoutes) shimmer.wrap(DevServer.prototype, 'ensureApiPage', wrapEnsureApiPage) @@ -143,7 +140,6 @@ module.exports = function (mod, agent, { version, enabled }) { const trans = ins.currTransaction() if (trans && !trans[kSetTransNameFn]) { trans[kSetTransNameFn] = (req, pathname) => { - console.log('XXX kSetTransNameFn called for wrapCatchAllRoute: pathname=%s', pathname) trans.setDefaultName(`${req.method} ${pathname}`) // Ensure only the first `findPageComponents` result sets the trans // name, otherwise a loaded `/_error` for page error handling could From a7ddf3baf2a875bfa0ddb1bd61e2ffbf8a3ba923 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 19 Oct 2022 16:10:31 -0700 Subject: [PATCH 49/66] working through XXXs --- .../next/dist/server/api-utils/node.js | 4 +- .../next/dist/server/dev/next-dev-server.js | 37 +++++------ .../modules/next/dist/server/next-server.js | 61 +++++++++---------- 3 files changed, 45 insertions(+), 57 deletions(-) diff --git a/lib/instrumentation/modules/next/dist/server/api-utils/node.js b/lib/instrumentation/modules/next/dist/server/api-utils/node.js index bd069d75cde..d1203a76deb 100644 --- a/lib/instrumentation/modules/next/dist/server/api-utils/node.js +++ b/lib/instrumentation/modules/next/dist/server/api-utils/node.js @@ -12,11 +12,9 @@ const semver = require('semver') const shimmer = require('../../../../../shimmer') -const kCapturedTransErr = Symbol.for('nextJsCapturedTransErr') +const kCapturedTransErr = Symbol.for('ElasticAPMNextJsCapturedTransErr') module.exports = function (mod, agent, { version, enabled }) { - console.log('XXX enabled: ', enabled) - console.log('XXX version: ', version) if (!enabled) { return mod } diff --git a/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js index 4d97bafcccb..becd78ff4d6 100644 --- a/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js +++ b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js @@ -12,8 +12,7 @@ const semver = require('semver') const shimmer = require('../../../../../shimmer') -const kInErrorHandling = Symbol.for('nextJsInErrorHandling') -const kSetTransNameFn = Symbol.for('nextJsSetTransNameFn') +const kSetTransNameFn = Symbol.for('ElasticAPMNextJsSetTransNameFn') const noopFn = () => {} @@ -33,11 +32,13 @@ module.exports = function (mod, agent, { version, enabled }) { shimmer.wrap(DevServer.prototype, 'generateRoutes', wrapGenerateRoutes) shimmer.wrap(DevServer.prototype, 'ensureApiPage', wrapEnsureApiPage) shimmer.wrap(DevServer.prototype, 'findPageComponents', wrapFindPageComponents) - // XXX splain the 'NextNodeServer.renderErrorToResponse' base is used. - // shimmer.wrap(DevServer.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse) + // Instrumenting the DevServer also uses the wrapping of + // 'NextNodeServer.renderErrorToResponse' in "next-server.js". return mod + // The `route` objects being wrapped here have this type: + // https://github.com/vercel/next.js/blob/v12.3.0/packages/next/server/router.ts#L26-L45 function wrapGenerateRoutes (orig) { return function wrappedGenerateRoutes () { if (this.constructor !== DevServer) { @@ -55,7 +56,6 @@ module.exports = function (mod, agent, { version, enabled }) { } } - // XXX Link to Next.js route type. function wrapRedirectRoute (route) { if (typeof route.fn !== 'function') { return @@ -86,7 +86,8 @@ module.exports = function (mod, agent, { version, enabled }) { } } - // XXX splain + // "FS" routes are those that go looking for matching paths on the filesystem + // to fulfill the request. function wrapFsRoute (route) { if (typeof route.fn !== 'function') { return @@ -94,12 +95,12 @@ module.exports = function (mod, agent, { version, enabled }) { const origRouteFn = route.fn // We explicitly handle only the `fsRoute`s that we know by name in the // Next.js code. We cannot set `trans.name` for all of them because of the - // true catch-all-routes that match any path and only sometimes handled them + // true catch-all-routes that match any path and only sometimes handle them // (e.g. 'public folder catchall'). - // XXX splain why we cannot do 'public folder catchall' and equiv. switch (route.name) { - // XXX splain case '_next/data catchall': + // This handles "/_next/data/..." paths that are used by Next.js + // client-side code to call `getServerSideProps()` for user pages. route.fn = function () { const trans = ins.currTransaction() if (trans) { @@ -107,6 +108,7 @@ module.exports = function (mod, agent, { version, enabled }) { if (!trans[kSetTransNameFn]) { trans[kSetTransNameFn] = (_req, pathname) => { trans.setDefaultName(`Next.js _next/data route ${pathname}`) + trans[kSetTransNameFn] = noopFn } } } @@ -122,7 +124,6 @@ module.exports = function (mod, agent, { version, enabled }) { route.fn = function () { const trans = ins.currTransaction() if (trans) { - // XXX 'splain result.finished trans.setDefaultName(`Next.js ${route.name}`) } return origRouteFn.apply(this, arguments) @@ -138,6 +139,8 @@ module.exports = function (mod, agent, { version, enabled }) { const origRouteFn = route.fn route.fn = function () { const trans = ins.currTransaction() + // This is a catchall route, so only set a kSetTransNameFn if a more + // specific route wrapper hasn't already done so. if (trans && !trans[kSetTransNameFn]) { trans[kSetTransNameFn] = (req, pathname) => { trans.setDefaultName(`${req.method} ${pathname}`) @@ -158,20 +161,16 @@ module.exports = function (mod, agent, { version, enabled }) { } const trans = ins.currTransaction() if (trans && trans.req) { - // XXX slight limitation on "handled": It could *possibly* be false if - // exception in `getPagePath`. Trade-off between getting that - // wrong and setting the trans.name early enough for captureError - // usage. log.trace({ pathname }, 'set transaction name from ensureApiPage') trans.setDefaultName(`${trans.req.method} ${pathname}`) - // In the DevServer an exception in an API handler will be followed up - // with a rendering of "/_error". trans[kSetTransNameFn] = noopFn } return orig.apply(this, arguments) } } + // `findPageComponents` is used to load any "./pages/..." files. It provides + // the resolved path appropriate for the transaction name. function wrapFindPageComponents (orig) { return function wrappedFindPageComponents ({ pathname }) { if (this.constructor !== DevServer) { @@ -181,11 +180,7 @@ module.exports = function (mod, agent, { version, enabled }) { promise.then(findComponentsResult => { if (findComponentsResult) { const trans = ins.currTransaction() - // If Next.js is doing error handling for this request, then it is - // loading an *error* page component (e.g. "_error.js"). We don't want - // to use that component's path for the transaction name. - console.log('XXX next-dev-server wrappedFindPageComponents: trans[kInErrorHandling]=%s', trans[kInErrorHandling], kInErrorHandling) - if (trans && !trans[kInErrorHandling] && trans.req && trans[kSetTransNameFn]) { + if (trans && trans.req && trans[kSetTransNameFn]) { log.trace({ pathname }, 'set transaction name from findPageComponents') trans[kSetTransNameFn](trans.req, pathname) } diff --git a/lib/instrumentation/modules/next/dist/server/next-server.js b/lib/instrumentation/modules/next/dist/server/next-server.js index 4db1ba7a7e1..56ad512c415 100644 --- a/lib/instrumentation/modules/next/dist/server/next-server.js +++ b/lib/instrumentation/modules/next/dist/server/next-server.js @@ -12,10 +12,8 @@ const semver = require('semver') const shimmer = require('../../../../shimmer') -// XXX either share these symbols, or prefix them with ElasticAPM -const kInErrorHandling = Symbol.for('nextJsInErrorHandling') -const kSetTransNameFn = Symbol.for('nextJsSetTransNameFn') -const kCapturedTransErr = Symbol.for('nextJsCapturedTransErr') +const kSetTransNameFn = Symbol.for('ElasticAPMNextJsSetTransNameFn') +const kCapturedTransErr = Symbol.for('ElasticAPMNextJsCapturedTransErr') const noopFn = () => {} @@ -45,6 +43,8 @@ module.exports = function (mod, agent, { version, enabled }) { return mod + // The `route` objects being wrapped here have this type: + // https://github.com/vercel/next.js/blob/v12.3.0/packages/next/server/router.ts#L26-L45 function wrapGenerateRoutes (orig) { return function wrappedGenerateRoutes () { if (this.constructor !== NextNodeServer) { @@ -62,7 +62,6 @@ module.exports = function (mod, agent, { version, enabled }) { } } - // XXX Link to Next.js route type. function wrapRedirectRoute (route) { if (typeof route.fn !== 'function') { return @@ -72,7 +71,6 @@ module.exports = function (mod, agent, { version, enabled }) { const trans = ins.currTransaction() if (trans) { trans.setDefaultName('Next.js ' + route.name) - // XXX 'splain trans[kSetTransNameFn] = noopFn } return origRouteFn.apply(this, arguments) @@ -94,7 +92,8 @@ module.exports = function (mod, agent, { version, enabled }) { } } - // XXX splain + // "FS" routes are those that go looking for matching paths on the filesystem + // to fulfill the request. function wrapFsRoute (route) { if (typeof route.fn !== 'function') { return @@ -104,10 +103,10 @@ module.exports = function (mod, agent, { version, enabled }) { // Next.js code. We cannot set `trans.name` for all of them because of the // true catch-all-routes that match any path and only sometimes handle them // (e.g. 'public folder catchall'). - // XXX splain why we cannot do 'public folder catchall' and equiv. switch (route.name) { - // XXX splain case '_next/data catchall': + // This handles "/_next/data/..." paths that are used by Next.js + // client-side code to call `getServerSideProps()` for user pages. route.fn = function () { const trans = ins.currTransaction() if (trans) { @@ -115,6 +114,7 @@ module.exports = function (mod, agent, { version, enabled }) { if (!trans[kSetTransNameFn]) { trans[kSetTransNameFn] = (_req, pathname) => { trans.setDefaultName(`Next.js _next/data route ${pathname}`) + trans[kSetTransNameFn] = noopFn } } } @@ -127,7 +127,6 @@ module.exports = function (mod, agent, { version, enabled }) { route.fn = function () { const trans = ins.currTransaction() if (trans) { - // XXX 'splain result.finished trans.setDefaultName(`Next.js ${route.name}`) } return origRouteFn.apply(this, arguments) @@ -143,6 +142,8 @@ module.exports = function (mod, agent, { version, enabled }) { const origRouteFn = route.fn route.fn = function () { const trans = ins.currTransaction() + // This is a catchall route, so only set a kSetTransNameFn if a more + // specific route wrapper hasn't already done so. if (trans && !trans[kSetTransNameFn]) { trans[kSetTransNameFn] = (req, pathname) => { trans.setDefaultName(`${req.method} ${pathname}`) @@ -163,10 +164,6 @@ module.exports = function (mod, agent, { version, enabled }) { } const trans = ins.currTransaction() if (trans && trans.req) { - // XXX slight limitation on "handled": It could *possibly* be false if - // exception in `getPagePath`. Trade-off between getting that - // wrong and setting the trans.name early enough for captureError - // usage. log.trace({ pathname }, 'set transaction name from ensureApiPage') trans.setDefaultName(`${trans.req.method} ${pathname}`) trans[kSetTransNameFn] = noopFn @@ -175,6 +172,8 @@ module.exports = function (mod, agent, { version, enabled }) { } } + // `findPageComponents` is used to load any "./pages/..." files. It provides + // the resolved path appropriate for the transaction name. function wrapFindPageComponents (orig) { return function wrappedFindPageComponents ({ pathname }) { if (this.constructor !== NextNodeServer) { @@ -184,10 +183,7 @@ module.exports = function (mod, agent, { version, enabled }) { promise.then(findComponentsResult => { if (findComponentsResult) { const trans = ins.currTransaction() - // If Next.js is doing error handling for this request, then it is - // loading an *error* page component (e.g. "_error.js"). We don't want - // to use that component's path for the transaction name. - if (trans && !trans[kInErrorHandling] && trans.req && trans[kSetTransNameFn]) { + if (trans && trans.req && trans[kSetTransNameFn]) { log.trace({ pathname }, 'set transaction name from findPageComponents') trans[kSetTransNameFn](trans.req, pathname) } @@ -199,25 +195,24 @@ module.exports = function (mod, agent, { version, enabled }) { function wrapRenderErrorToResponse (orig) { return function wrappedRenderErrorToResponse (ctx, err) { - // XXX 'splain - // if (this.constructor !== NextNodeServer) { - // console.log('XXX wrappedRenderErrorToResponse -> ctor != NextNodeServer abort') - // return orig.apply(this, arguments) - // } - console.log('XXX wrappedRenderErrorToResponse(ctx, err=%s)', err && err.message) + // The wrapped `NodeNextServer.renderErrorToResponse` is used for both + // this and the "next-dev-sever.js" instrumentation, so it doesn't have + // the `this.constructor !== ...` guard that the above wrappers do. + const trans = ins.currTransaction() if (trans) { - // Signal to subsequent instrumentation for this transaction that - // Next.js is now doing error handling for this request. - // XXX could try setting `kSetTransNameFn` to noop instead. Then only need the one symbol. - trans[kInErrorHandling] = true - console.log('XXX next-server.js: set kInErrorHandling', kInErrorHandling) + // Next.js is now doing error handling for this request, which typically + // means loading the "_error.js" page component. We don't want + // that `findPageComponents` call to set the transaction name. + trans[kSetTransNameFn] = noopFn } - // Next.js uses `err=null` to handle a 404. - // XXX 'splain duplicate for apiResolver() handling in dev mode vs prod mode. - console.log('XXX duplicate?', trans[kCapturedTransErr] === err) + + // - Next.js uses `err=null` to handle a 404. + // - To capture errors in API handlers we have shimmed `apiResolver` (see + // "api-utils/node.js"). In the dev server only, `renderErrorToResponse` + // is *also* called for the error. The `kCapturedTransErr` guard + // prevents a capturing the same error twice. if (err && (!trans || trans[kCapturedTransErr] !== err)) { - console.log('XXX capture error in wrappedRenderErrorToResponse') agent.captureError(err) } return orig.apply(this, arguments) From ca74a375dc05b6784de0b09638145924a34d32b3 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 19 Oct 2022 16:25:25 -0700 Subject: [PATCH 50/66] cache the wrapping of API endpoint handler modules (i.e. pages/api/*.js|tsx) --- .../next/dist/server/api-utils/node.js | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/instrumentation/modules/next/dist/server/api-utils/node.js b/lib/instrumentation/modules/next/dist/server/api-utils/node.js index d1203a76deb..c98b2698650 100644 --- a/lib/instrumentation/modules/next/dist/server/api-utils/node.js +++ b/lib/instrumentation/modules/next/dist/server/api-utils/node.js @@ -24,32 +24,34 @@ module.exports = function (mod, agent, { version, enabled }) { } const ins = agent._instrumentation + const wrappedModFromMod = new WeakMap() shimmer.wrap(mod, 'apiResolver', wrapApiResolver) return mod function wrapApiResolver (orig) { - return function wrappedApiResolver (req, res, query, resolverModule, apiContext, propagateError, dev, page) { - // XXX This is iteration and creating a wrapper object for every invocation. Yuck. - // Need to cache this. WeakRef on resolverModule? - // XXX I haven't yet tested the case where `resolverModule` doesn't have a - // `.default`, but *is* the handler function. What cases can that happen? - + return function wrappedApiResolver (_req, _res, _query, resolverModule, _apiContext, _propagateError, _dev, _page) { // The user module that we are wrapping is a webpack-wrapped build that // exposes fields only as getters. We therefore need to fully replace // the module to be able to wrap the single `.default` field. - // XXX can we use lib/propwrap.js?! - const wrappedMod = {} - const names = Object.getOwnPropertyNames(resolverModule) - for (let i = 0; i < names.length; i++) { - const name = names[i] - if (name !== 'default') { - // Proxy other module fields. - Object.defineProperty(wrappedMod, name, { enumerable: true, get: function () { return resolverModule[name] } }) + // + // Cache `wrappedMod` so we only need to create it once per API endpoint, + // rather than every time each API endpoint is called. + let wrappedMod = wrappedModFromMod.get(resolverModule) + if (!wrappedMod) { + wrappedMod = {} + const names = Object.getOwnPropertyNames(resolverModule) + for (let i = 0; i < names.length; i++) { + const name = names[i] + if (name !== 'default') { + // Proxy other module fields. + Object.defineProperty(wrappedMod, name, { enumerable: true, get: function () { return resolverModule[name] } }) + } } + wrappedMod.default = wrapApiHandler(resolverModule.default || resolverModule) + wrappedModFromMod.set(resolverModule, wrappedMod) } - wrappedMod.default = wrapApiHandler(resolverModule.default || resolverModule) arguments[3] = wrappedMod return orig.apply(this, arguments) @@ -71,7 +73,6 @@ module.exports = function (mod, agent, { version, enabled }) { try { promise = orig.apply(this, arguments) } catch (syncErr) { - console.log('XXX wrappedApiHandler: capture syncErr') agent.captureError(syncErr) const trans = ins.currTransaction() if (trans) { @@ -81,7 +82,6 @@ module.exports = function (mod, agent, { version, enabled }) { } if (promise) { promise.catch(rejectErr => { - console.log('XXX wrappedApiHandler: capture rejectErr') agent.captureError(rejectErr) const trans = ins.currTransaction() if (trans) { From dac6583012cda5b889dcca9160a701db792dd8d2 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 20 Oct 2022 10:01:20 -0700 Subject: [PATCH 51/66] changelog entry; some other doc/comment edits --- .eslintrc.json | 2 +- CHANGELOG.asciidoc | 34 +++++++++++++++++++ docs/supported-technologies.asciidoc | 2 +- examples/nextjs/README.md | 2 +- lib/instrumentation/modules/next/README.md | 4 ++- .../instrumentation/modules/next/next.test.js | 4 +-- 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index d7c877947d5..05895a69128 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -13,7 +13,7 @@ "ignorePatterns": [ "/.nyc_output", "/build", - "node_modules*", + "node_modules", "/examples/esbuild/dist", "/examples/typescript/dist", "/examples/nextjs", diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index a90e656294c..f44a914d66c 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -32,6 +32,40 @@ Notes: === Node.js Agent version 3.x +==== Unreleased + +[float] +===== Breaking changes + +[float] +===== Features + +* (Technical Preview) Next.js server-side instrumentation. See the <> document. ++ +This adds instrumentation of the Next.js dev server (`next dev`) and prod +server (`next start`). The APM transactions for incoming HTTP requests to the +server will be named appropriately based on Next.js's routing -- both for +user page routes (e.g. `GET /a-dynamic-page/[id]`) and for internal Next.js +routes (e.g. `Next.js _next/data route my-page`, +`Next.js Rewrite route /foo -> /bar`). As well, exceptions in server-side code +(e.g. `getServerSideProps`, server-side run page handlers, API handlers) will +be reported. ({pull}2959[#2959]) ++ +This is a technical preview to get feedback from Next.js users. The details on +how exactly the instrumentation works may change in future versions. + +[float] +===== Bug fixes + +* Source lines of context in stacktraces is *no longer reported* for "*.min.js" + files that do not have source-map information. These files are assumed to + be minimized files, for which source line context won't be useful. This + change is to guard against excessively large stacktrace data. + +[float] +===== Chores + + [[release-notes-3.39.0]] ==== 3.39.0 2022/10/17 diff --git a/docs/supported-technologies.asciidoc b/docs/supported-technologies.asciidoc index 9a29554f5dc..3758e14eb1a 100644 --- a/docs/supported-technologies.asciidoc +++ b/docs/supported-technologies.asciidoc @@ -93,7 +93,7 @@ These are the frameworks that we officially support: | <> | >=17.9.0 <21.0.0 | | <> | >=9.0.0 <19.0.0 | Deprecated. No longer tested. | <> via koa-router or @koa/router | >=5.2.0 <13.0.0 | Koa doesn't have a built in router, so we can't support Koa directly since we rely on router information for full support. We currently support the most popular Koa router called https://github.com/koajs/koa-router[koa-router]. -| <> | >=11.1.0 <13.0.0 | (Experimental) Names incoming HTTP transactions based on Next.js routing and reports errors in user pages. Supports the Next.js production server (`next start`) and development server (`next dev`). See the <>. +| <> | >=11.1.0 <13.0.0 | (Technical Preview) This instruments Next.js routing to name transactions for incoming HTTP transactions; and reports errors in user pages. It supports the Next.js production server (`next start`) and development server (`next dev`). See the <>. | <> | >=5.2.0 | |======================================================================= diff --git a/examples/nextjs/README.md b/examples/nextjs/README.md index 998b3ba2b8b..0c33d3329a3 100644 --- a/examples/nextjs/README.md +++ b/examples/nextjs/README.md @@ -20,7 +20,7 @@ This is a [Next.js](https://nextjs.org/) application npm run build && npm start # or the production server ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +Open [http://localhost:3000](http://localhost:3000) in your browser to see the result. An [API route](https://nextjs.org/docs/api-routes/introduction) can be accessed at . ## Learn More diff --git a/lib/instrumentation/modules/next/README.md b/lib/instrumentation/modules/next/README.md index e55a5404cbf..4dd52c432ae 100644 --- a/lib/instrumentation/modules/next/README.md +++ b/lib/instrumentation/modules/next/README.md @@ -9,7 +9,9 @@ or the result". There are a number of ways to deploy (https://nextjs.org/docs/deployment) a Next.js app. This instrumentation works with "Self-Hosting", and using Next.js's -built-in server. There is a Next.js "server" class hierarchy: +built-in server. + +Here is the Next.js "server" class hierarchy: class Server (in base-server.ts) class NextNodeServer (in next-server.ts, used for `next start`) diff --git a/test/instrumentation/modules/next/next.test.js b/test/instrumentation/modules/next/next.test.js index d89dc4f7373..0b8a2a15ea9 100644 --- a/test/instrumentation/modules/next/next.test.js +++ b/test/instrumentation/modules/next/next.test.js @@ -682,7 +682,7 @@ tape.test('-- prod server tests --', suite => { // To ensure we get all the trace data from the instrumented Next.js // server, we SIGTERM it and rely on the graceful-exit apm.flush() in - // "apmsetup.js" to flush it. + // "start-next.js" to flush it. nextServerProc.on('close', code => { t.equal(code, 0, 'Next.js server exit status was 0') checkExpectedApmEvents(t, apmServer.events) @@ -777,7 +777,7 @@ tape.test('-- dev server tests --', suite => { // To ensure we get all the trace data from the instrumented Next.js // server, we SIGTERM it and rely on the graceful-exit apm.flush() in - // "apmsetup.js" to flush it. + // "start-next.js" to flush it. nextServerProc.on('close', code => { t.equal(code, 0, 'Next.js server exit status was 0') checkExpectedApmEvents(t, apmServer.events) From 1729148e1d26055a2e869df98efa8333ff61d013 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 26 Oct 2022 12:22:26 -0700 Subject: [PATCH 52/66] Update docs/nextjs.asciidoc Co-authored-by: Brandon Morelli --- docs/nextjs.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/nextjs.asciidoc b/docs/nextjs.asciidoc index 4fbb05c572f..3c91d6b8bf2 100644 --- a/docs/nextjs.asciidoc +++ b/docs/nextjs.asciidoc @@ -32,7 +32,7 @@ in future versions of the agent. If you are a Next.js user, please help us prov You need an APM Server to send APM data to. Follow the {apm-guide-ref}/apm-quick-start.html[APM Quick start] if you have not set one up -yet. You will need your *APM server url* and an APM server *secret token* (or +yet. You will need your *APM server URL* and an APM server *secret token* (or *API key*) for configuring the APM agent below. You will also need a Next.js application to monitor. If you do not have an From e6210343b03b596935d4cceef25badb9fab6fce4 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 26 Oct 2022 12:22:40 -0700 Subject: [PATCH 53/66] Update docs/nextjs.asciidoc Co-authored-by: Brandon Morelli --- docs/nextjs.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/nextjs.asciidoc b/docs/nextjs.asciidoc index 3c91d6b8bf2..2066221fd78 100644 --- a/docs/nextjs.asciidoc +++ b/docs/nextjs.asciidoc @@ -102,7 +102,7 @@ https://nextjs.org/docs/basic-features/environment-variables[Next.js-supported "elastic-apm-node.js" file here. Create an "elastic-apm-node.js" file in the application root with the APM server -url and secret token values from the <> section above: +URL and secret token values from the <> section above: [source,javascript] ---- From 250b73f7664523c34c98bd153ae205ca96f963d5 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 26 Oct 2022 12:22:56 -0700 Subject: [PATCH 54/66] Update docs/nextjs.asciidoc Co-authored-by: Brandon Morelli --- docs/nextjs.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/nextjs.asciidoc b/docs/nextjs.asciidoc index 2066221fd78..0fccad468f6 100644 --- a/docs/nextjs.asciidoc +++ b/docs/nextjs.asciidoc @@ -146,7 +146,7 @@ while true; do sleep 1; curl -i http://localhost:3000/api/hello; done Visit your Kibana APM app and, after a few seconds, you should see a service entry for your Next.js app. (The service name will be pulled from the "name" field in "package.json". It can be overriden with -https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html#service-name[`serviceName`].) Here is an example: +<>. Here is an example: image::./images/nextjs-my-app-screenshot.png[Kibana APM app showing Next.js my-app] From 1ebd48eaab3c1602745090c64111e46ede42a447 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 26 Oct 2022 12:23:33 -0700 Subject: [PATCH 55/66] Update docs/nextjs.asciidoc Co-authored-by: Brandon Morelli --- docs/nextjs.asciidoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/nextjs.asciidoc b/docs/nextjs.asciidoc index 0fccad468f6..e96067b5a52 100644 --- a/docs/nextjs.asciidoc +++ b/docs/nextjs.asciidoc @@ -121,8 +121,7 @@ export ELASTIC_APM_SERVER_URL='https://...' export ELASTIC_APM_SECRET_TOKEN='...' ---- -See https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html[the -APM configuration guide] for full details on supported configuration variables. +See the <> for full details on supported configuration variables. [float] From 931e396df9021af25b7a46344a73eb13980a8e7d Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 26 Oct 2022 12:24:00 -0700 Subject: [PATCH 56/66] Update docs/nextjs.asciidoc Co-authored-by: Brandon Morelli --- docs/nextjs.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/nextjs.asciidoc b/docs/nextjs.asciidoc index e96067b5a52..5602cde14a5 100644 --- a/docs/nextjs.asciidoc +++ b/docs/nextjs.asciidoc @@ -19,7 +19,7 @@ on the server will be reported for viewing in the Kibana APM app. Note that the Node.js APM agent can only instrument _server-side_ code. To monitor the client-side parts of a Next.js application, see the -https://www.elastic.co/guide/en/apm/agent/rum-js/current/intro.html[Elastic RUM agent]. +{apm-rum-ref}/intro.html[Elastic RUM agent]. NOTE: This Next.js instrumentation is a _technical preview_ while we solicit feedback from Next.js users. That means that how the instrumentation works might change From b601b2233f935cee9df0aa34b1f6f5f4b7b9c56c Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 26 Oct 2022 12:24:16 -0700 Subject: [PATCH 57/66] Update docs/nextjs.asciidoc Co-authored-by: Brandon Morelli --- docs/nextjs.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/nextjs.asciidoc b/docs/nextjs.asciidoc index 5602cde14a5..de3e8788672 100644 --- a/docs/nextjs.asciidoc +++ b/docs/nextjs.asciidoc @@ -93,7 +93,7 @@ Edit the "dev" and "start" scripts in your "package.json" as follows: ==== Step 3: Configure the APM agent The APM agent can be -https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuring-the-agent.html[configured] +<> with environment variables or with an "elastic-apm-node.js" module in the current working directory. Note that because the APM agent is being loaded before the Next.js server, the From 64ca18bd6d25e6c14bc30527f2aba3751ff8334f Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 26 Oct 2022 12:24:27 -0700 Subject: [PATCH 58/66] Update examples/nextjs/README.md Co-authored-by: Brandon Morelli --- examples/nextjs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/nextjs/README.md b/examples/nextjs/README.md index 0c33d3329a3..2e987a7c65f 100644 --- a/examples/nextjs/README.md +++ b/examples/nextjs/README.md @@ -6,7 +6,7 @@ This is a [Next.js](https://nextjs.org/) application 1. `npm install` -2. Configure an APM server URL and token for the APM agent. See the [APM Quick start](https://www.elastic.co/guide/en/apm/guide/current/apm-quick-start.html) for setting up an Elastic Stack. +2. Configure an APM server URL and token for the APM agent. See the [APM Quick start](https://www.elastic.co/guide/en/apm/guide/current/apm-quick-start.html) for help setting up the Elastic Stack. ```bash cp elastic-apm-node.js.template elastic-apm-node.js From 7f7a54226350a4b1bfbd53297739e70e97b2d2d1 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 26 Oct 2022 12:24:53 -0700 Subject: [PATCH 59/66] Update examples/nextjs/README.md Co-authored-by: Brandon Morelli --- examples/nextjs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/nextjs/README.md b/examples/nextjs/README.md index 2e987a7c65f..b58c18bee64 100644 --- a/examples/nextjs/README.md +++ b/examples/nextjs/README.md @@ -25,6 +25,6 @@ An [API route](https://nextjs.org/docs/api-routes/introduction) can be accessed ## Learn More -- [Get started with Next.js and Elastic APM](https://www.elastic.co/guide/en/apm/agent/nodejs/master/nextjs.html) +- [Get started with Next.js and Elastic APM](https://www.elastic.co/guide/en/apm/agent/nodejs/master/nextjs.html) - official Elastic documentation. - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. From 3e2d15be92024eed7278e4fbe02ed6ab5e518dd6 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Wed, 26 Oct 2022 12:52:06 -0700 Subject: [PATCH 60/66] doc updates (mainly using preview:[]) from Brandon's review --- CHANGELOG.asciidoc | 2 +- docs/nextjs.asciidoc | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 78e1dc8aa4d..592aa618c90 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -42,7 +42,7 @@ Notes: * Enable support for redis v4 ({pull}2945[#2945]) -* (Technical Preview) Next.js server-side instrumentation. See the <> document. +* preview:[] Next.js server-side instrumentation. See the <> document. + This adds instrumentation of the Next.js dev server (`next dev`) and prod server (`next start`). The APM transactions for incoming HTTP requests to the diff --git a/docs/nextjs.asciidoc b/docs/nextjs.asciidoc index de3e8788672..ea8eef3a678 100644 --- a/docs/nextjs.asciidoc +++ b/docs/nextjs.asciidoc @@ -21,9 +21,10 @@ Note that the Node.js APM agent can only instrument _server-side_ code. To monitor the client-side parts of a Next.js application, see the {apm-rum-ref}/intro.html[Elastic RUM agent]. -NOTE: This Next.js instrumentation is a _technical preview_ while we solicit feedback -from Next.js users. That means that how the instrumentation works might change -in future versions of the agent. If you are a Next.js user, please help us provide a better Next.js observability experience with your feedback on our https://discuss.elastic.co/tags/c/apm/nodejs[Discuss forum]. +NOTE: preview:[] This Next.js instrumentation is a _technical preview_ while we +solicit feedback from Next.js users. If you are a Next.js user, please help us +provide a better Next.js observability experience with your feedback on our +https://discuss.elastic.co/tags/c/apm/nodejs[Discuss forum]. [float] @@ -143,7 +144,7 @@ while true; do sleep 1; curl -i http://localhost:3000/api/hello; done ---- Visit your Kibana APM app and, after a few seconds, you should see a service -entry for your Next.js app. (The service name will be pulled from the "name" +entry for your Next.js app. The service name will be pulled from the "name" field in "package.json". It can be overriden with <>. Here is an example: From 5ae3a2fe5137edd5a10457d0d3e3e126d7a2827b Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 27 Oct 2022 10:39:41 -0700 Subject: [PATCH 61/66] support Next.js 13 and fixes for versions of next before 12.3.1 There had been a issue in the TAV tests where the `npm ci` run as part of a test run would *override* the `npm install --no-save ...` changes that TAV was making. This resulted in older versions never actually being tested. Fixing that uncovered some issues/limitations in the tests and instrumentation for older versions back to the min 11.1.0 supported. - Improve de-dupe of captureError for earlier next versions that can call `renderErrorToResponse` twice. - Handle a change in `findPageComponents` signature in v12.2.6-canary.10 - Change how the test flushes and stops the test Next.js server to not rely on graceful shutdown... because NEXT_MANUAL_SIG_HANDLE support was only added in v12.1.7-canary.7. - Add instrumentation of the top-level "next/dist/server/next.js" to setFramework early enough to catch the first intake request. I didn't dig in all the way, but in next@13.0.0 the lazy load of "next-server.js" seemed to have changed such that it came after cloud metadata fetching uncorked the first intake request. - Adjust the test a-nextjs-app as required to work with Next.js 13 per some of the points in https://beta.nextjs.org/docs/upgrade-guide (no `` child of ``, no `next/future/image`). - Skip a couple of test cases on some older 12.0.x and 11.1.x versions of next because it isn't worth it right now. --- lib/instrumentation/index.js | 1 + lib/instrumentation/modules/next/README.md | 8 + .../next/dist/server/api-utils/node.js | 14 +- .../next/dist/server/dev/next-dev-server.js | 11 +- .../modules/next/dist/server/next-server.js | 27 +- .../modules/next/dist/server/next.js | 29 ++ start-next.js | 2 + test/config.test.js | 1 + .../modules/next/a-nextjs-app/.tav.yml | 12 +- .../next/a-nextjs-app/components/Header.js | 8 +- .../next/a-nextjs-app/package-lock.json | 289 +++++++++--------- .../modules/next/a-nextjs-app/package.json | 2 +- .../modules/next/a-nextjs-app/pages/a-page.js | 2 +- .../modules/next/a-nextjs-app/pages/index.js | 20 +- .../instrumentation/modules/next/next.test.js | 86 ++++-- 15 files changed, 312 insertions(+), 200 deletions(-) create mode 100644 lib/instrumentation/modules/next/dist/server/next.js diff --git a/lib/instrumentation/index.js b/lib/instrumentation/index.js index c485f9db634..18e2d92a6ea 100644 --- a/lib/instrumentation/index.js +++ b/lib/instrumentation/index.js @@ -65,6 +65,7 @@ var MODULES = [ 'mysql2', 'next/dist/server/api-utils/node', 'next/dist/server/dev/next-dev-server', + 'next/dist/server/next', 'next/dist/server/next-server', 'pg', 'pug', diff --git a/lib/instrumentation/modules/next/README.md b/lib/instrumentation/modules/next/README.md index 4dd52c432ae..165ba137022 100644 --- a/lib/instrumentation/modules/next/README.md +++ b/lib/instrumentation/modules/next/README.md @@ -18,6 +18,14 @@ Here is the Next.js "server" class hierarchy: class DevServer (in dev/next-dev-server.js, used for `next dev`) +## dist/server/next.js + +This is the first module imported for `require('next')`. It is used solely +to `agent.setFramework(...)`. Doing so in "next-server.js" can be too late +because it is lazily imported when creating the Next server -- by which point +metadata may have already been sent on the first APM agent intake request. + + ## dist/server/next-server.js This file in the "next" package implements the `NextNodeServer`, the Next.js diff --git a/lib/instrumentation/modules/next/dist/server/api-utils/node.js b/lib/instrumentation/modules/next/dist/server/api-utils/node.js index c98b2698650..0088fb82274 100644 --- a/lib/instrumentation/modules/next/dist/server/api-utils/node.js +++ b/lib/instrumentation/modules/next/dist/server/api-utils/node.js @@ -12,13 +12,13 @@ const semver = require('semver') const shimmer = require('../../../../../shimmer') -const kCapturedTransErr = Symbol.for('ElasticAPMNextJsCapturedTransErr') +const kErrIsCaptured = Symbol.for('ElasticAPMNextJsErrIsCaptured') module.exports = function (mod, agent, { version, enabled }) { if (!enabled) { return mod } - if (!semver.satisfies(version, '>=11.1.0 <13.0.0')) { + if (!semver.satisfies(version, '>=11.1.0 <14.0.0', { includePrerelease: true })) { agent.logger.debug('next/dist/server/api-utils/node.js version %s not supported, skipping', version) return mod } @@ -74,19 +74,13 @@ module.exports = function (mod, agent, { version, enabled }) { promise = orig.apply(this, arguments) } catch (syncErr) { agent.captureError(syncErr) - const trans = ins.currTransaction() - if (trans) { - trans[kCapturedTransErr] = syncErr - } + syncErr[kErrIsCaptured] = true throw syncErr } if (promise) { promise.catch(rejectErr => { agent.captureError(rejectErr) - const trans = ins.currTransaction() - if (trans) { - trans[kCapturedTransErr] = rejectErr - } + rejectErr[kErrIsCaptured] = true }) } return promise diff --git a/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js index becd78ff4d6..ab1e70523ca 100644 --- a/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js +++ b/lib/instrumentation/modules/next/dist/server/dev/next-dev-server.js @@ -20,7 +20,7 @@ module.exports = function (mod, agent, { version, enabled }) { if (!enabled) { return mod } - if (!semver.satisfies(version, '>=11.1.0 <13.0.0')) { + if (!semver.satisfies(version, '>=11.1.0 <14.0.0', { includePrerelease: true })) { agent.logger.debug('next version %s not supported, skipping', version) return mod } @@ -172,10 +172,17 @@ module.exports = function (mod, agent, { version, enabled }) { // `findPageComponents` is used to load any "./pages/..." files. It provides // the resolved path appropriate for the transaction name. function wrapFindPageComponents (orig) { - return function wrappedFindPageComponents ({ pathname }) { + return function wrappedFindPageComponents (pathnameOrArgs) { if (this.constructor !== DevServer) { return orig.apply(this, arguments) } + + // In next <=12.2.6-canary.10 the function signature is: + // async findPageComponents(pathname, query, params, isAppPath) + // after that version it is: + // async findPageComponents({ pathname, query, params, isAppPath }) + const pathname = typeof pathnameOrArgs === 'string' ? pathnameOrArgs : pathnameOrArgs.pathname + const promise = orig.apply(this, arguments) promise.then(findComponentsResult => { if (findComponentsResult) { diff --git a/lib/instrumentation/modules/next/dist/server/next-server.js b/lib/instrumentation/modules/next/dist/server/next-server.js index 56ad512c415..0431e260d07 100644 --- a/lib/instrumentation/modules/next/dist/server/next-server.js +++ b/lib/instrumentation/modules/next/dist/server/next-server.js @@ -13,7 +13,7 @@ const semver = require('semver') const shimmer = require('../../../../shimmer') const kSetTransNameFn = Symbol.for('ElasticAPMNextJsSetTransNameFn') -const kCapturedTransErr = Symbol.for('ElasticAPMNextJsCapturedTransErr') +const kErrIsCaptured = Symbol.for('ElasticAPMNextJsErrIsCaptured') const noopFn = () => {} @@ -21,7 +21,7 @@ module.exports = function (mod, agent, { version, enabled }) { if (!enabled) { return mod } - if (!semver.satisfies(version, '>=11.1.0 <13.0.0')) { + if (!semver.satisfies(version, '>=11.1.0 <14.0.0', { includePrerelease: true })) { agent.logger.debug('next version %s not supported, skipping', version) return mod } @@ -29,12 +29,6 @@ module.exports = function (mod, agent, { version, enabled }) { const ins = agent._instrumentation const log = agent.logger - // This isn't perfect. Which framework the agent will report with a - // custom Next.js server using another framework, e.g. - // https://github.com/vercel/next.js/blob/canary/examples/custom-server-fastify/server.js - // depends on which is *imported* first. - agent.setFramework({ name: 'Next.js', version, overwrite: false }) - const NextNodeServer = mod.default shimmer.wrap(NextNodeServer.prototype, 'generateRoutes', wrapGenerateRoutes) shimmer.wrap(NextNodeServer.prototype, 'ensureApiPage', wrapEnsureApiPage) @@ -175,10 +169,17 @@ module.exports = function (mod, agent, { version, enabled }) { // `findPageComponents` is used to load any "./pages/..." files. It provides // the resolved path appropriate for the transaction name. function wrapFindPageComponents (orig) { - return function wrappedFindPageComponents ({ pathname }) { + return function wrappedFindPageComponents (pathnameOrArgs) { if (this.constructor !== NextNodeServer) { return orig.apply(this, arguments) } + + // In next <=12.2.6-canary.10 the function signature is: + // async findPageComponents(pathname, query, params, isAppPath) + // after that version it is: + // async findPageComponents({ pathname, query, params, isAppPath }) + const pathname = typeof pathnameOrArgs === 'string' ? pathnameOrArgs : pathnameOrArgs.pathname + const promise = orig.apply(this, arguments) promise.then(findComponentsResult => { if (findComponentsResult) { @@ -210,10 +211,12 @@ module.exports = function (mod, agent, { version, enabled }) { // - Next.js uses `err=null` to handle a 404. // - To capture errors in API handlers we have shimmed `apiResolver` (see // "api-utils/node.js"). In the dev server only, `renderErrorToResponse` - // is *also* called for the error. The `kCapturedTransErr` guard - // prevents a capturing the same error twice. - if (err && (!trans || trans[kCapturedTransErr] !== err)) { + // is *also* called for the error -- and in v12.2.6 and below it is + // called *twice*. The `kErrIsCaptured` guard prevents capturing + // the same error twice. + if (err && !err[kErrIsCaptured]) { agent.captureError(err) + err[kErrIsCaptured] = true } return orig.apply(this, arguments) } diff --git a/lib/instrumentation/modules/next/dist/server/next.js b/lib/instrumentation/modules/next/dist/server/next.js new file mode 100644 index 00000000000..ff930a25547 --- /dev/null +++ b/lib/instrumentation/modules/next/dist/server/next.js @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict' + +// See "lib/instrumentation/modules/next/README.md". + +const semver = require('semver') + +module.exports = function (mod, agent, { version, enabled }) { + if (!enabled) { + return mod + } + if (!semver.satisfies(version, '>=11.1.0 <14.0.0', { includePrerelease: true })) { + agent.logger.debug('next version %s not supported, skipping', version) + return mod + } + + // This isn't perfect. Which framework the agent will report with a + // custom Next.js server using another framework, e.g. + // https://github.com/vercel/next.js/blob/canary/examples/custom-server-fastify/server.js + // depends on which is *imported* first. + agent.setFramework({ name: 'Next.js', version, overwrite: false }) + + return mod +} diff --git a/start-next.js b/start-next.js index dbf9a1dcf0d..a6bb702c6a7 100644 --- a/start-next.js +++ b/start-next.js @@ -13,6 +13,8 @@ const apm = require('./').start() // Flush APM data on server process termination. // https://nextjs.org/docs/deployment#manual-graceful-shutdowns +// Note: Support for NEXT_MANUAL_SIG_HANDLE was added in next@12.1.7-canary.7, +// so this `apm.flush()` will only happen in that and later versions. process.env.NEXT_MANUAL_SIG_HANDLE = 1 function flushApmAndExit () { apm.flush(() => { diff --git a/test/config.test.js b/test/config.test.js index 0164b8d3816..4b585d405a3 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -1032,6 +1032,7 @@ test('disableInstrumentations', function (t) { } modules.delete('next/dist/server/api-utils/node') modules.delete('next/dist/server/dev/next-dev-server') + modules.delete('next/dist/server/next') modules.delete('next/dist/server/next-server') if (semver.lt(process.version, '14.0.0')) { modules.delete('redis') // redis@4 supports node >=14 diff --git a/test/instrumentation/modules/next/a-nextjs-app/.tav.yml b/test/instrumentation/modules/next/a-nextjs-app/.tav.yml index 4ac794cfa8f..71ec36ef43e 100644 --- a/test/instrumentation/modules/next/a-nextjs-app/.tav.yml +++ b/test/instrumentation/modules/next/a-nextjs-app/.tav.yml @@ -1,6 +1,6 @@ # Test Next.js versions. # -# - We instrument Next.js ">=11.1.0 <13.0.0". I don't see much value in testing +# - We instrument Next.js ">=11.1.0 <14.0.0". I don't see much value in testing # every patch-level release. Instead we will test the latest patch release # for each minor. (It would be nice if tav supported this natively.) # - Next.js 11 supports back to node v12.22.0 and v14.5.0. However, when this @@ -14,7 +14,7 @@ next-11: peerDependencies: - "react@^17.0.2" - "react-dom@^17.0.2" -next: +next-12: name: next versions: '12.0.10 || 12.1.6 || 12.2.6 || 12.3.1 || >12.3.1 <13' node: '>=14.5.0' @@ -22,3 +22,11 @@ next: peerDependencies: - "react@^18.2.0" - "react-dom@^18.2.0" +next: + name: next + versions: '>=13.0.0 <14' + node: '>=14.6.0' + commands: node ../next.test.js + peerDependencies: + - "react@^18.2.0" + - "react-dom@^18.2.0" diff --git a/test/instrumentation/modules/next/a-nextjs-app/components/Header.js b/test/instrumentation/modules/next/a-nextjs-app/components/Header.js index 103bfe3a070..5236d499618 100644 --- a/test/instrumentation/modules/next/a-nextjs-app/components/Header.js +++ b/test/instrumentation/modules/next/a-nextjs-app/components/Header.js @@ -15,13 +15,13 @@ function Header () {

- Home + Home  |  - APage + APage  |  - AnSSRPage + AnSSRPage  |  - ADynamicPage/42 + ADynamicPage/42
diff --git a/test/instrumentation/modules/next/a-nextjs-app/package-lock.json b/test/instrumentation/modules/next/a-nextjs-app/package-lock.json index 6750bcb6bf7..2cfe676a7e7 100644 --- a/test/instrumentation/modules/next/a-nextjs-app/package-lock.json +++ b/test/instrumentation/modules/next/a-nextjs-app/package-lock.json @@ -9,20 +9,20 @@ "version": "1.0.0", "license": "BSD-2-Clause", "dependencies": { - "next": "^12.3.1", + "next": "^13.0.0", "react": "^18.2.0", "react-dom": "^18.2.0" } }, "node_modules/@next/env": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-12.3.1.tgz", - "integrity": "sha512-9P9THmRFVKGKt9DYqeC2aKIxm8rlvkK38V1P1sRE7qyoPBIs8l9oo79QoSdPtOWfzkbDAVUqvbQGgTMsb8BtJg==" + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.0.tgz", + "integrity": "sha512-65v9BVuah2Mplohm4+efsKEnoEuhmlGm8B2w6vD1geeEP2wXtlSJCvR/cCRJ3fD8wzCQBV41VcMBQeYET6MRkg==" }, "node_modules/@next/swc-android-arm-eabi": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.1.tgz", - "integrity": "sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.0.tgz", + "integrity": "sha512-+DUQkYF93gxFjWY+CYWE1QDX6gTgnUiWf+W4UqZjM1Jcef8U97fS6xYh+i+8rH4MM0AXHm7OSakvfOMzmjU6VA==", "cpu": [ "arm" ], @@ -35,9 +35,9 @@ } }, "node_modules/@next/swc-android-arm64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.3.1.tgz", - "integrity": "sha512-CmgU2ZNyBP0rkugOOqLnjl3+eRpXBzB/I2sjwcGZ7/Z6RcUJXK5Evz+N0ucOxqE4cZ3gkTeXtSzRrMK2mGYV8Q==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.0.tgz", + "integrity": "sha512-RW9Uy3bMSc0zVGCa11klFuwfP/jdcdkhdruqnrJ7v+7XHm6OFKkSRzX6ee7yGR1rdDZvTnP4GZSRSpzjLv/N0g==", "cpu": [ "arm64" ], @@ -50,9 +50,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.3.1.tgz", - "integrity": "sha512-hT/EBGNcu0ITiuWDYU9ur57Oa4LybD5DOQp4f22T6zLfpoBMfBibPtR8XktXmOyFHrL/6FC2p9ojdLZhWhvBHg==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.0.tgz", + "integrity": "sha512-APA26nps1j4qyhOIzkclW/OmgotVHj1jBxebSpMCPw2rXfiNvKNY9FA0TcuwPmUCNqaTnm703h6oW4dvp73A4Q==", "cpu": [ "arm64" ], @@ -65,9 +65,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.3.1.tgz", - "integrity": "sha512-9S6EVueCVCyGf2vuiLiGEHZCJcPAxglyckTZcEwLdJwozLqN0gtS0Eq0bQlGS3dH49Py/rQYpZ3KVWZ9BUf/WA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.0.tgz", + "integrity": "sha512-qsUhUdoFuRJiaJ7LnvTQ6GZv1QnMDcRXCIjxaN0FNVXwrjkq++U7KjBUaxXkRzLV4C7u0NHLNOp0iZwNNE7ypw==", "cpu": [ "x64" ], @@ -80,9 +80,9 @@ } }, "node_modules/@next/swc-freebsd-x64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.3.1.tgz", - "integrity": "sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.0.tgz", + "integrity": "sha512-sCdyCbboS7CwdnevKH9J6hkJI76LUw1jVWt4eV7kISuLiPba3JmehZSWm80oa4ADChRVAwzhLAo2zJaYRrInbg==", "cpu": [ "x64" ], @@ -95,9 +95,9 @@ } }, "node_modules/@next/swc-linux-arm-gnueabihf": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.3.1.tgz", - "integrity": "sha512-diL9MSYrEI5nY2wc/h/DBewEDUzr/DqBjIgHJ3RUNtETAOB3spMNHvJk2XKUDjnQuluLmFMloet9tpEqU2TT9w==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.0.tgz", + "integrity": "sha512-/X/VxfFA41C9jrEv+sUsPLQ5vbDPVIgG0CJrzKvrcc+b+4zIgPgtfsaWq9ockjHFQi3ycvlZK4TALOXO8ovQ6Q==", "cpu": [ "arm" ], @@ -110,9 +110,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.1.tgz", - "integrity": "sha512-o/xB2nztoaC7jnXU3Q36vGgOolJpsGG8ETNjxM1VAPxRwM7FyGCPHOMk1XavG88QZSQf+1r+POBW0tLxQOJ9DQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.0.tgz", + "integrity": "sha512-x6Oxr1GIi0ZtNiT6jbw+JVcbEi3UQgF7mMmkrgfL4mfchOwXtWSHKTSSPnwoJWJfXYa0Vy1n8NElWNTGAqoWFw==", "cpu": [ "arm64" ], @@ -125,9 +125,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.1.tgz", - "integrity": "sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.0.tgz", + "integrity": "sha512-SnMH9ngI+ipGh3kqQ8+mDtWunirwmhQnQeZkEq9e/9Xsgjf04OetqrqRHKM1HmJtG2qMUJbyXFJ0F81TPuT+3g==", "cpu": [ "arm64" ], @@ -140,9 +140,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.1.tgz", - "integrity": "sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.0.tgz", + "integrity": "sha512-VSQwTX9EmdbotArtA1J67X8964oQfe0xHb32x4tu+JqTR+wOHyG6wGzPMdXH2oKAp6rdd7BzqxUXXf0J+ypHlw==", "cpu": [ "x64" ], @@ -155,9 +155,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.1.tgz", - "integrity": "sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.0.tgz", + "integrity": "sha512-xBCP0nnpO0q4tsytXkvIwWFINtbFRyVY5gxa1zB0vlFtqYR9lNhrOwH3CBrks3kkeaePOXd611+8sjdUtrLnXA==", "cpu": [ "x64" ], @@ -170,9 +170,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.1.tgz", - "integrity": "sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.0.tgz", + "integrity": "sha512-NutwDafqhGxqPj/eiUixJq9ImS/0sgx6gqlD7jRndCvQ2Q8AvDdu1+xKcGWGNnhcDsNM/n1avf1e62OG1GaqJg==", "cpu": [ "arm64" ], @@ -185,9 +185,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.1.tgz", - "integrity": "sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.0.tgz", + "integrity": "sha512-zNaxaO+Kl/xNz02E9QlcVz0pT4MjkXGDLb25qxtAzyJL15aU0+VjjbIZAYWctG59dvggNIUNDWgoBeVTKB9xLg==", "cpu": [ "ia32" ], @@ -200,9 +200,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.1.tgz", - "integrity": "sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.0.tgz", + "integrity": "sha512-FFOGGWwTCRMu9W7MF496Urefxtuo2lttxF1vwS+1rIRsKvuLrWhVaVTj3T8sf2EBL6gtJbmh4TYlizS+obnGKA==", "cpu": [ "x64" ], @@ -223,9 +223,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001418", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz", - "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==", + "version": "1.0.30001426", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001426.tgz", + "integrity": "sha512-n7cosrHLl8AWt0wwZw/PJZgUg3lV0gk9LMI7ikGJwhyhgsd2Nb65vKvmSexCqq/J7rbH3mFG6yZZiPR5dLPW5A==", "funding": [ { "type": "opencollective", @@ -237,6 +237,11 @@ } ] }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -265,43 +270,43 @@ } }, "node_modules/next": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/next/-/next-12.3.1.tgz", - "integrity": "sha512-l7bvmSeIwX5lp07WtIiP9u2ytZMv7jIeB8iacR28PuUEFG5j0HGAPnMqyG5kbZNBG2H7tRsrQ4HCjuMOPnANZw==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/next/-/next-13.0.0.tgz", + "integrity": "sha512-puH1WGM6rGeFOoFdXXYfUxN9Sgi4LMytCV5HkQJvVUOhHfC1DoVqOfvzaEteyp6P04IW+gbtK2Q9pInVSrltPA==", "dependencies": { - "@next/env": "12.3.1", + "@next/env": "13.0.0", "@swc/helpers": "0.4.11", "caniuse-lite": "^1.0.30001406", "postcss": "8.4.14", - "styled-jsx": "5.0.7", + "styled-jsx": "5.1.0", "use-sync-external-store": "1.2.0" }, "bin": { "next": "dist/bin/next" }, "engines": { - "node": ">=12.22.0" + "node": ">=14.6.0" }, "optionalDependencies": { - "@next/swc-android-arm-eabi": "12.3.1", - "@next/swc-android-arm64": "12.3.1", - "@next/swc-darwin-arm64": "12.3.1", - "@next/swc-darwin-x64": "12.3.1", - "@next/swc-freebsd-x64": "12.3.1", - "@next/swc-linux-arm-gnueabihf": "12.3.1", - "@next/swc-linux-arm64-gnu": "12.3.1", - "@next/swc-linux-arm64-musl": "12.3.1", - "@next/swc-linux-x64-gnu": "12.3.1", - "@next/swc-linux-x64-musl": "12.3.1", - "@next/swc-win32-arm64-msvc": "12.3.1", - "@next/swc-win32-ia32-msvc": "12.3.1", - "@next/swc-win32-x64-msvc": "12.3.1" + "@next/swc-android-arm-eabi": "13.0.0", + "@next/swc-android-arm64": "13.0.0", + "@next/swc-darwin-arm64": "13.0.0", + "@next/swc-darwin-x64": "13.0.0", + "@next/swc-freebsd-x64": "13.0.0", + "@next/swc-linux-arm-gnueabihf": "13.0.0", + "@next/swc-linux-arm64-gnu": "13.0.0", + "@next/swc-linux-arm64-musl": "13.0.0", + "@next/swc-linux-x64-gnu": "13.0.0", + "@next/swc-linux-x64-musl": "13.0.0", + "@next/swc-win32-arm64-msvc": "13.0.0", + "@next/swc-win32-ia32-msvc": "13.0.0", + "@next/swc-win32-x64-msvc": "13.0.0" }, "peerDependencies": { "fibers": ">= 3.1.0", "node-sass": "^6.0.0 || ^7.0.0", - "react": "^17.0.2 || ^18.0.0-0", - "react-dom": "^17.0.2 || ^18.0.0-0", + "react": "^18.0.0-0", + "react-dom": "^18.0.0-0", "sass": "^1.3.0" }, "peerDependenciesMeta": { @@ -384,9 +389,12 @@ } }, "node_modules/styled-jsx": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.7.tgz", - "integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.0.tgz", + "integrity": "sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==", + "dependencies": { + "client-only": "0.0.1" + }, "engines": { "node": ">= 12.0.0" }, @@ -418,86 +426,86 @@ }, "dependencies": { "@next/env": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-12.3.1.tgz", - "integrity": "sha512-9P9THmRFVKGKt9DYqeC2aKIxm8rlvkK38V1P1sRE7qyoPBIs8l9oo79QoSdPtOWfzkbDAVUqvbQGgTMsb8BtJg==" + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.0.tgz", + "integrity": "sha512-65v9BVuah2Mplohm4+efsKEnoEuhmlGm8B2w6vD1geeEP2wXtlSJCvR/cCRJ3fD8wzCQBV41VcMBQeYET6MRkg==" }, "@next/swc-android-arm-eabi": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.1.tgz", - "integrity": "sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.0.tgz", + "integrity": "sha512-+DUQkYF93gxFjWY+CYWE1QDX6gTgnUiWf+W4UqZjM1Jcef8U97fS6xYh+i+8rH4MM0AXHm7OSakvfOMzmjU6VA==", "optional": true }, "@next/swc-android-arm64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.3.1.tgz", - "integrity": "sha512-CmgU2ZNyBP0rkugOOqLnjl3+eRpXBzB/I2sjwcGZ7/Z6RcUJXK5Evz+N0ucOxqE4cZ3gkTeXtSzRrMK2mGYV8Q==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.0.tgz", + "integrity": "sha512-RW9Uy3bMSc0zVGCa11klFuwfP/jdcdkhdruqnrJ7v+7XHm6OFKkSRzX6ee7yGR1rdDZvTnP4GZSRSpzjLv/N0g==", "optional": true }, "@next/swc-darwin-arm64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.3.1.tgz", - "integrity": "sha512-hT/EBGNcu0ITiuWDYU9ur57Oa4LybD5DOQp4f22T6zLfpoBMfBibPtR8XktXmOyFHrL/6FC2p9ojdLZhWhvBHg==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.0.tgz", + "integrity": "sha512-APA26nps1j4qyhOIzkclW/OmgotVHj1jBxebSpMCPw2rXfiNvKNY9FA0TcuwPmUCNqaTnm703h6oW4dvp73A4Q==", "optional": true }, "@next/swc-darwin-x64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.3.1.tgz", - "integrity": "sha512-9S6EVueCVCyGf2vuiLiGEHZCJcPAxglyckTZcEwLdJwozLqN0gtS0Eq0bQlGS3dH49Py/rQYpZ3KVWZ9BUf/WA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.0.tgz", + "integrity": "sha512-qsUhUdoFuRJiaJ7LnvTQ6GZv1QnMDcRXCIjxaN0FNVXwrjkq++U7KjBUaxXkRzLV4C7u0NHLNOp0iZwNNE7ypw==", "optional": true }, "@next/swc-freebsd-x64": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.3.1.tgz", - "integrity": "sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.0.tgz", + "integrity": "sha512-sCdyCbboS7CwdnevKH9J6hkJI76LUw1jVWt4eV7kISuLiPba3JmehZSWm80oa4ADChRVAwzhLAo2zJaYRrInbg==", "optional": true }, "@next/swc-linux-arm-gnueabihf": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.3.1.tgz", - "integrity": "sha512-diL9MSYrEI5nY2wc/h/DBewEDUzr/DqBjIgHJ3RUNtETAOB3spMNHvJk2XKUDjnQuluLmFMloet9tpEqU2TT9w==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.0.tgz", + "integrity": "sha512-/X/VxfFA41C9jrEv+sUsPLQ5vbDPVIgG0CJrzKvrcc+b+4zIgPgtfsaWq9ockjHFQi3ycvlZK4TALOXO8ovQ6Q==", "optional": true }, "@next/swc-linux-arm64-gnu": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.1.tgz", - "integrity": "sha512-o/xB2nztoaC7jnXU3Q36vGgOolJpsGG8ETNjxM1VAPxRwM7FyGCPHOMk1XavG88QZSQf+1r+POBW0tLxQOJ9DQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.0.tgz", + "integrity": "sha512-x6Oxr1GIi0ZtNiT6jbw+JVcbEi3UQgF7mMmkrgfL4mfchOwXtWSHKTSSPnwoJWJfXYa0Vy1n8NElWNTGAqoWFw==", "optional": true }, "@next/swc-linux-arm64-musl": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.1.tgz", - "integrity": "sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.0.tgz", + "integrity": "sha512-SnMH9ngI+ipGh3kqQ8+mDtWunirwmhQnQeZkEq9e/9Xsgjf04OetqrqRHKM1HmJtG2qMUJbyXFJ0F81TPuT+3g==", "optional": true }, "@next/swc-linux-x64-gnu": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.1.tgz", - "integrity": "sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.0.tgz", + "integrity": "sha512-VSQwTX9EmdbotArtA1J67X8964oQfe0xHb32x4tu+JqTR+wOHyG6wGzPMdXH2oKAp6rdd7BzqxUXXf0J+ypHlw==", "optional": true }, "@next/swc-linux-x64-musl": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.1.tgz", - "integrity": "sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.0.tgz", + "integrity": "sha512-xBCP0nnpO0q4tsytXkvIwWFINtbFRyVY5gxa1zB0vlFtqYR9lNhrOwH3CBrks3kkeaePOXd611+8sjdUtrLnXA==", "optional": true }, "@next/swc-win32-arm64-msvc": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.1.tgz", - "integrity": "sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.0.tgz", + "integrity": "sha512-NutwDafqhGxqPj/eiUixJq9ImS/0sgx6gqlD7jRndCvQ2Q8AvDdu1+xKcGWGNnhcDsNM/n1avf1e62OG1GaqJg==", "optional": true }, "@next/swc-win32-ia32-msvc": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.1.tgz", - "integrity": "sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.0.tgz", + "integrity": "sha512-zNaxaO+Kl/xNz02E9QlcVz0pT4MjkXGDLb25qxtAzyJL15aU0+VjjbIZAYWctG59dvggNIUNDWgoBeVTKB9xLg==", "optional": true }, "@next/swc-win32-x64-msvc": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.1.tgz", - "integrity": "sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.0.tgz", + "integrity": "sha512-FFOGGWwTCRMu9W7MF496Urefxtuo2lttxF1vwS+1rIRsKvuLrWhVaVTj3T8sf2EBL6gtJbmh4TYlizS+obnGKA==", "optional": true }, "@swc/helpers": { @@ -509,9 +517,14 @@ } }, "caniuse-lite": { - "version": "1.0.30001418", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz", - "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==" + "version": "1.0.30001426", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001426.tgz", + "integrity": "sha512-n7cosrHLl8AWt0wwZw/PJZgUg3lV0gk9LMI7ikGJwhyhgsd2Nb65vKvmSexCqq/J7rbH3mFG6yZZiPR5dLPW5A==" + }, + "client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, "js-tokens": { "version": "4.0.0", @@ -532,28 +545,28 @@ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" }, "next": { - "version": "12.3.1", - "resolved": "https://registry.npmjs.org/next/-/next-12.3.1.tgz", - "integrity": "sha512-l7bvmSeIwX5lp07WtIiP9u2ytZMv7jIeB8iacR28PuUEFG5j0HGAPnMqyG5kbZNBG2H7tRsrQ4HCjuMOPnANZw==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/next/-/next-13.0.0.tgz", + "integrity": "sha512-puH1WGM6rGeFOoFdXXYfUxN9Sgi4LMytCV5HkQJvVUOhHfC1DoVqOfvzaEteyp6P04IW+gbtK2Q9pInVSrltPA==", "requires": { - "@next/env": "12.3.1", - "@next/swc-android-arm-eabi": "12.3.1", - "@next/swc-android-arm64": "12.3.1", - "@next/swc-darwin-arm64": "12.3.1", - "@next/swc-darwin-x64": "12.3.1", - "@next/swc-freebsd-x64": "12.3.1", - "@next/swc-linux-arm-gnueabihf": "12.3.1", - "@next/swc-linux-arm64-gnu": "12.3.1", - "@next/swc-linux-arm64-musl": "12.3.1", - "@next/swc-linux-x64-gnu": "12.3.1", - "@next/swc-linux-x64-musl": "12.3.1", - "@next/swc-win32-arm64-msvc": "12.3.1", - "@next/swc-win32-ia32-msvc": "12.3.1", - "@next/swc-win32-x64-msvc": "12.3.1", + "@next/env": "13.0.0", + "@next/swc-android-arm-eabi": "13.0.0", + "@next/swc-android-arm64": "13.0.0", + "@next/swc-darwin-arm64": "13.0.0", + "@next/swc-darwin-x64": "13.0.0", + "@next/swc-freebsd-x64": "13.0.0", + "@next/swc-linux-arm-gnueabihf": "13.0.0", + "@next/swc-linux-arm64-gnu": "13.0.0", + "@next/swc-linux-arm64-musl": "13.0.0", + "@next/swc-linux-x64-gnu": "13.0.0", + "@next/swc-linux-x64-musl": "13.0.0", + "@next/swc-win32-arm64-msvc": "13.0.0", + "@next/swc-win32-ia32-msvc": "13.0.0", + "@next/swc-win32-x64-msvc": "13.0.0", "@swc/helpers": "0.4.11", "caniuse-lite": "^1.0.30001406", "postcss": "8.4.14", - "styled-jsx": "5.0.7", + "styled-jsx": "5.1.0", "use-sync-external-store": "1.2.0" } }, @@ -603,10 +616,12 @@ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" }, "styled-jsx": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.0.7.tgz", - "integrity": "sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==", - "requires": {} + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.0.tgz", + "integrity": "sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==", + "requires": { + "client-only": "0.0.1" + } }, "tslib": { "version": "2.4.0", diff --git a/test/instrumentation/modules/next/a-nextjs-app/package.json b/test/instrumentation/modules/next/a-nextjs-app/package.json index eb021b830e5..df3fe501e91 100644 --- a/test/instrumentation/modules/next/a-nextjs-app/package.json +++ b/test/instrumentation/modules/next/a-nextjs-app/package.json @@ -11,7 +11,7 @@ "keywords": [], "license": "BSD-2-Clause", "dependencies": { - "next": "^12.3.1", + "next": "^13.0.0", "react": "^18.2.0", "react-dom": "^18.2.0" } diff --git a/test/instrumentation/modules/next/a-nextjs-app/pages/a-page.js b/test/instrumentation/modules/next/a-nextjs-app/pages/a-page.js index 76c250e3936..b7999ac53ab 100644 --- a/test/instrumentation/modules/next/a-nextjs-app/pages/a-page.js +++ b/test/instrumentation/modules/next/a-nextjs-app/pages/a-page.js @@ -6,7 +6,7 @@ // A static page. -import Image from 'next/future/image' +import Image from 'next/image' import Header from '../components/Header' import img from '../public/elastic-logo.png' diff --git a/test/instrumentation/modules/next/a-nextjs-app/pages/index.js b/test/instrumentation/modules/next/a-nextjs-app/pages/index.js index b48ecc89490..3db50aaf52c 100644 --- a/test/instrumentation/modules/next/a-nextjs-app/pages/index.js +++ b/test/instrumentation/modules/next/a-nextjs-app/pages/index.js @@ -16,55 +16,55 @@ function IndexPage () { diff --git a/test/instrumentation/modules/next/next.test.js b/test/instrumentation/modules/next/next.test.js index 0b8a2a15ea9..3b9a222a5d3 100644 --- a/test/instrumentation/modules/next/next.test.js +++ b/test/instrumentation/modules/next/next.test.js @@ -10,7 +10,7 @@ // // This test roughly does the following: // - Start a MockAPMServer to capture intake requests. -// - `npm ci` to build the "a-nextjs-app" project. +// - `npm ci` to build the "a-nextjs-app" project, if necessary. // - Test instrumentation when using the Next.js production server. // - `next build && next start` configured to send to our MockAPMServer. // - Make every request in `TEST_REQUESTS` to the Next.js app. @@ -60,7 +60,7 @@ if (process.env.ELASTIC_APM_CONTEXT_MANAGER === 'patch') { const testAppDir = path.join(__dirname, 'a-nextjs-app') // Match ANSI escapes (from https://stackoverflow.com/a/29497680/14444044). -const ansiRe = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g /* eslint-disable-line no-control-regex */ +const ANSI_RE = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g /* eslint-disable-line no-control-regex */ let apmServer let nextJsVersion // Determined after `npm ci` is run. @@ -314,6 +314,12 @@ let TEST_REQUESTS = [ // Error capture cases { testName: 'an API endpoint that throws', + // Limitation: In Next.js commit 6bc7c4d9c (included in 12.0.11-canary.14), + // the `apiResolver()` method that we instrument was moved from + // next/dist/server/api-utils.js to next/dist/server/api-utils/node.js. + // We instrument the latter. To support error capture in API endpoint + // handlers in early versions we'd need to instrument the former path as well. + nextVersionRange: '>=12.0.11-canary.14', reqOpts: { method: 'GET', path: '/api/an-api-endpoint-that-throws' }, expectedRes: { statusCode: 500 @@ -354,6 +360,11 @@ let TEST_REQUESTS = [ }, { testName: 'a throw in getServerSideProps', + // Limitation: There was a bug in v11.1.0 where the error handling flow in + // the *dev* server was incomplete. This was fixed in + // https://github.com/vercel/next.js/pull/28520, included in version + // 11.1.1-canary.18. + nextVersionRange: '>=11.1.1-canary.18', reqOpts: { method: 'GET', path: '/a-throw-in-getServerSideProps' }, expectedRes: { statusCode: 500 @@ -383,6 +394,23 @@ if (DEV_TEST_FILTER) { // ---- utility functions +/** + * Format the given data for passing to `t.comment()`. + * + * - t.comment() wipes leading whitespace. Prefix lines with '|' to avoid + * that, and to visually group a multi-line write. + * - Drop ANSI escape characters, because those include control chars that + * are illegal in XML. When we convert TAP output to JUnit XML for + * Jenkins, then Jenkins complains about invalid XML. `FORCE_COLOR=0` + * can be used to disable ANSI escapes in `next dev`'s usage of chalk, + * but not in its coloured exception output. + */ +function formatForTComment (data) { + return data.toString('utf8') + .replace(ANSI_RE, '') + .trimRight().replace(/\n/g, '\n|') + '\n' +} + /** * Wait for the test a-nextjs-app server to be ready. * @@ -547,7 +575,8 @@ function checkExpectedApmEvents (t, apmEvents) { // ---- tests -tape.test(`setup: npm ci (in ${testAppDir})`, t => { +const haveNodeModules = fs.existsSync(path.join(testAppDir, 'node_modules')) +tape.test(`setup: npm ci (in ${testAppDir})`, { skip: haveNodeModules }, t => { const startTime = Date.now() exec( 'npm ci', @@ -564,9 +593,25 @@ tape.test(`setup: npm ci (in ${testAppDir})`, t => { ) }) -tape.test('setup: mock APM server', t => { +tape.test('setup: filter TEST_REQUESTS', t => { + // This version can only be fetched after the above `npm ci`. nextJsVersion = require(path.join(testAppDir, 'node_modules/next/package.json')).version + // Some entries in TEST_REQUESTS are only run for newer versions of Next.js. + TEST_REQUESTS = TEST_REQUESTS.filter(testReq => { + if (testReq.nextVersionRange && !semver.satisfies(nextJsVersion, testReq.nextVersionRange, { includePrerelease: true })) { + t.comment(`skip "${testReq.testName}" because next@${nextJsVersion} does not satisfy "${testReq.nextVersionRange}"`) + return false + } else { + return true + } + }) + + t.end() +}) + + +tape.test('setup: mock APM server', t => { apmServer = new MockAPMServer({ apmServerVersion: '7.15.0' }) apmServer.start(function (serverUrl_) { serverUrl = serverUrl_ @@ -618,7 +663,8 @@ tape.test('-- prod server tests --', suite => { cwd: testAppDir, env: Object.assign({}, process.env, { NODE_OPTIONS: '-r ../../../../../start-next.js', - ELASTIC_APM_SERVER_URL: serverUrl + ELASTIC_APM_SERVER_URL: serverUrl, + ELASTIC_APM_API_REQUEST_TIME: '2s' }) } ) @@ -626,10 +672,10 @@ tape.test('-- prod server tests --', suite => { t.error(err, 'no error from "next start"') }) nextServerProc.stdout.on('data', data => { - t.comment(`[Next.js server stdout] ${data}`) + t.comment(`[Next.js server stdout] ${formatForTComment(data)}`) }) nextServerProc.stderr.on('data', data => { - t.comment(`[Next.js server stderr] ${data}`) + t.comment(`[Next.js server stderr] ${formatForTComment(data)}`) }) // Allow some time for an early fail of `next start`, e.g. if there is @@ -681,14 +727,15 @@ tape.test('-- prod server tests --', suite => { } // To ensure we get all the trace data from the instrumented Next.js - // server, we SIGTERM it and rely on the graceful-exit apm.flush() in - // "start-next.js" to flush it. + // server, we wait 2x the `apiRequestTime` (set above) before stopping it. nextServerProc.on('close', code => { t.equal(code, 0, 'Next.js server exit status was 0') checkExpectedApmEvents(t, apmServer.events) t.end() }) - nextServerProc.kill('SIGTERM') + setTimeout(() => { + nextServerProc.kill('SIGTERM') + }, 4000) // 2x ELASTIC_APM_API_REQUEST_SIZE set above }) suite.end() @@ -708,7 +755,8 @@ tape.test('-- dev server tests --', suite => { cwd: testAppDir, env: Object.assign({}, process.env, { NODE_OPTIONS: '-r ../../../../../start-next.js', - ELASTIC_APM_SERVER_URL: serverUrl + ELASTIC_APM_SERVER_URL: serverUrl, + ELASTIC_APM_API_REQUEST_TIME: '2s' }) } ) @@ -716,15 +764,10 @@ tape.test('-- dev server tests --', suite => { t.error(err, 'no error from "next dev"') }) nextServerProc.stdout.on('data', data => { - // Drop ANSI escape characters, because those include control chars that - // are illegal in XML. When we convert TAP output to JUnit XML for - // Jenkins, then Jenkins complains about invalid XML. `FORCE_COLOR=0` - // can be used to disable ANSI escapes in `next dev`'s usage of chalk, - // but not in its coloured exception output. - t.comment(`[Next.js server stdout] ${data.toString().replace(ansiRe, '')}`) + t.comment(`[Next.js server stdout] ${formatForTComment(data)}`) }) nextServerProc.stderr.on('data', data => { - t.comment(`[Next.js server stderr] ${data.toString().replace(ansiRe, '')}`) + t.comment(`[Next.js server stderr] ${formatForTComment(data)}`) }) // Allow some time for an early fail of `next dev`, e.g. if there is @@ -776,14 +819,15 @@ tape.test('-- dev server tests --', suite => { } // To ensure we get all the trace data from the instrumented Next.js - // server, we SIGTERM it and rely on the graceful-exit apm.flush() in - // "start-next.js" to flush it. + // server, we wait 2x the `apiRequestTime` (set above) before stopping it. nextServerProc.on('close', code => { t.equal(code, 0, 'Next.js server exit status was 0') checkExpectedApmEvents(t, apmServer.events) t.end() }) - nextServerProc.kill('SIGTERM') + setTimeout(() => { + nextServerProc.kill('SIGTERM') + }, 4000) // 2x ELASTIC_APM_API_REQUEST_SIZE set above }) suite.end() From 33a6510716987660b54fc9750da60cc8d04946bf Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 27 Oct 2022 10:59:02 -0700 Subject: [PATCH 62/66] fix lint --- .../modules/next/dist/server/api-utils/node.js | 1 - test/instrumentation/modules/next/next.test.js | 8 +++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/instrumentation/modules/next/dist/server/api-utils/node.js b/lib/instrumentation/modules/next/dist/server/api-utils/node.js index 0088fb82274..59a4b007107 100644 --- a/lib/instrumentation/modules/next/dist/server/api-utils/node.js +++ b/lib/instrumentation/modules/next/dist/server/api-utils/node.js @@ -23,7 +23,6 @@ module.exports = function (mod, agent, { version, enabled }) { return mod } - const ins = agent._instrumentation const wrappedModFromMod = new WeakMap() shimmer.wrap(mod, 'apiResolver', wrapApiResolver) diff --git a/test/instrumentation/modules/next/next.test.js b/test/instrumentation/modules/next/next.test.js index 3b9a222a5d3..c77b019bd96 100644 --- a/test/instrumentation/modules/next/next.test.js +++ b/test/instrumentation/modules/next/next.test.js @@ -407,8 +407,8 @@ if (DEV_TEST_FILTER) { */ function formatForTComment (data) { return data.toString('utf8') - .replace(ANSI_RE, '') - .trimRight().replace(/\n/g, '\n|') + '\n' + .replace(ANSI_RE, '') + .trimRight().replace(/\n/g, '\n|') + '\n' } /** @@ -575,6 +575,9 @@ function checkExpectedApmEvents (t, apmEvents) { // ---- tests +// We need to `npm ci` for a first test run. However, *only* for a first test +// run, otherwise this will override any possible `npm install --no-save ...` +// changes made by a TAV runner. const haveNodeModules = fs.existsSync(path.join(testAppDir, 'node_modules')) tape.test(`setup: npm ci (in ${testAppDir})`, { skip: haveNodeModules }, t => { const startTime = Date.now() @@ -610,7 +613,6 @@ tape.test('setup: filter TEST_REQUESTS', t => { t.end() }) - tape.test('setup: mock APM server', t => { apmServer = new MockAPMServer({ apmServerVersion: '7.15.0' }) apmServer.start(function (serverUrl_) { From 94586a97369e59006eecb014c3afe191b4c51beb Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 27 Oct 2022 11:00:38 -0700 Subject: [PATCH 63/66] changelog tweak after merge --- CHANGELOG.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 87a3ccb619a..56c4b642e7a 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -55,6 +55,7 @@ be reported. ({pull}2959[#2959]) + This is a technical preview to get feedback from Next.js users. The details on how exactly the instrumentation works may change in future versions. + * Improve container-info gathering to support AWS ECS/Fargate environments. ({issues}2914[#2914]) @@ -74,6 +75,7 @@ how exactly the instrumentation works may change in future versions. central config. The re-fetch delay is clamped to `[5 seconds, 1 day]`. ({issues}2941[#2941]) + [[release-notes-3.39.0]] ==== 3.39.0 2022/10/17 From 15c2e9c0ef46c8a2a4d1ee9bac86b4df86df21ee Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Thu, 27 Oct 2022 11:47:16 -0700 Subject: [PATCH 64/66] test fix: only test next instr with node >=14.6.0 (the min support with next@13) to simplify the testing guards --- test/instrumentation/modules/next/next.test.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/test/instrumentation/modules/next/next.test.js b/test/instrumentation/modules/next/next.test.js index c77b019bd96..c693463a45b 100644 --- a/test/instrumentation/modules/next/next.test.js +++ b/test/instrumentation/modules/next/next.test.js @@ -41,15 +41,12 @@ if (os.platform() === 'win32') { console.log('# SKIP Next.js testing currently is not supported on windows') process.exit() } -if (semver.lt(process.version, '12.22.0')) { - console.log(`# SKIP next does not support node ${process.version}`) - process.exit() -} else if (semver.satisfies(process.version, '>=14.0.0 <14.5.0')) { - // The handling of SSR pages, e.g. `GET /an-ssr-page` in the test a-nextjs-app, - // in next@12.3.1 (I'm not sure of the full `next` version range) relies on - // https://github.com/nodejs/node/pull/33155 which landed in node v14.5.0 and - // v12.19.0. - console.log(`# SKIP next does not support fully node ${process.version}`) +if (semver.lt(process.version, '14.6.0')) { + // While some earlier supported versions of Next.js work with node v12, + // next@13 cannot even be imported with node v12 (newer JS syntax is used). + // To simplify, and because node v12 is EOL, we skip any testing with + // node <=14.6.0 (next@13's min supported node version). + console.log(`# SKIP test next with node <14.6.0 (node ${process.version})`) process.exit() } if (process.env.ELASTIC_APM_CONTEXT_MANAGER === 'patch') { From abed53d7a048dc750d09612b6d1607abaf931cd5 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Fri, 28 Oct 2022 13:04:45 -0700 Subject: [PATCH 65/66] clear out last XXXs that were there while this was still on a feature branch --- docs/nextjs.asciidoc | 4 ---- examples/nextjs/package.json | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/nextjs.asciidoc b/docs/nextjs.asciidoc index ea8eef3a678..3b8c83cdc65 100644 --- a/docs/nextjs.asciidoc +++ b/docs/nextjs.asciidoc @@ -48,8 +48,6 @@ cd my-app You can also take a look at and use this https://github.com/elastic/apm-agent-nodejs/tree/main/examples/nextjs/[Next.js + Elastic APM example app]. -NOTE: XXX Until this is merged, the example is at: - [float] [[nextjs-setup]] ==== Step 1: Add the APM agent dependency @@ -61,8 +59,6 @@ Add the `elastic-apm-node` module as a dependency to your application: npm install elastic-apm-node --save # or 'yarn add elastic-apm-node' ---- -NOTE: XXX Until this is in a release use `npm install "elastic-apm-node@elastic/apm-agent-nodejs#trentm/feat-nextjs"`. - [float] ==== Step 2: Start the APM agent diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index fa8fcbf7224..45168c859eb 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -8,9 +8,8 @@ "start": "NODE_OPTIONS=--require=elastic-apm-node/start-next.js next start", "lint": "next lint" }, - "XXX": "change to 'main' branch for merge, and issue to update this on release", "dependencies": { - "elastic-apm-node": "elastic/apm-agent-nodejs#trentm/feat-nextjs", + "elastic-apm-node": "elastic/apm-agent-nodejs#main", "next": "12.3.1", "react": "18.2.0", "react-dom": "18.2.0" From 58990ea5d4839baa651982c8a316bf4acdb67ad8 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Fri, 28 Oct 2022 13:11:10 -0700 Subject: [PATCH 66/66] a couple final doc/comment edits --- docs/supported-technologies.asciidoc | 2 +- start-next.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/supported-technologies.asciidoc b/docs/supported-technologies.asciidoc index 1fe19ace8e9..9dedaa8df6f 100644 --- a/docs/supported-technologies.asciidoc +++ b/docs/supported-technologies.asciidoc @@ -93,7 +93,7 @@ These are the frameworks that we officially support: | <> | >=17.9.0 <21.0.0 | | <> | >=9.0.0 <19.0.0 | Deprecated. No longer tested. | <> via koa-router or @koa/router | >=5.2.0 <13.0.0 | Koa doesn't have a built in router, so we can't support Koa directly since we rely on router information for full support. We currently support the most popular Koa router called https://github.com/koajs/koa-router[koa-router]. -| <> | >=11.1.0 <13.0.0 | (Technical Preview) This instruments Next.js routing to name transactions for incoming HTTP transactions; and reports errors in user pages. It supports the Next.js production server (`next start`) and development server (`next dev`). See the <>. +| <> | >=11.1.0 <14.0.0 | (Technical Preview) This instruments Next.js routing to name transactions for incoming HTTP transactions; and reports errors in user pages. It supports the Next.js production server (`next start`) and development server (`next dev`). See the <>. | <> | >=5.2.0 | |======================================================================= diff --git a/start-next.js b/start-next.js index a6bb702c6a7..89b6faa3d47 100644 --- a/start-next.js +++ b/start-next.js @@ -6,8 +6,8 @@ 'use strict' -// Use this module via `node --require=elastic-apm-node/start-next ...` -// monitor a Next.js app with Elastic APM. +// Use this module via `node --require=elastic-apm-node/start-next.js ...` +// to monitor a Next.js app with Elastic APM. const apm = require('./').start()