mirror of
				https://github.com/jakejarvis/jarv.is.git
				synced 2025-10-30 00:55:49 -04:00 
			
		
		
		
	convert APIs to edge functions (#1648)
This commit is contained in:
		| @@ -1,5 +1,6 @@ | ||||
| AIRTABLE_API_KEY= | ||||
| AIRTABLE_BASE= | ||||
| DATABASE_URL= | ||||
| GH_PUBLIC_TOKEN= | ||||
| HCAPTCHA_SECRET_KEY= | ||||
| NEXT_PUBLIC_HCAPTCHA_SITE_KEY= | ||||
| @@ -10,8 +11,6 @@ POSTGRES_PRISMA_URL= | ||||
| POSTGRES_URL= | ||||
| POSTGRES_URL_NON_POOLING= | ||||
| POSTGRES_USER= | ||||
| SENTRY_AUTH_TOKEN= | ||||
| SENTRY_DSN= | ||||
| SPOTIFY_CLIENT_ID= | ||||
| SPOTIFY_CLIENT_SECRET= | ||||
| SPOTIFY_REFRESH_TOKEN= | ||||
|   | ||||
| @@ -1,22 +1,17 @@ | ||||
| import { PrismaClient } from "@prisma/client"; | ||||
| import { IS_DEV_SERVER } from "../config/constants"; | ||||
| import { PrismaClient } from "@prisma/client/edge"; | ||||
|  | ||||
| // PrismaClient is attached to the `global` object in development to prevent | ||||
| // exhausting your database connection limit. | ||||
| // | ||||
| // Learn more: | ||||
| // creating PrismaClient here prevents next.js from starting too many concurrent prisma instances and exhausting the | ||||
| // number of connection pools available (especially when hot reloading from `next dev`). | ||||
| // https://pris.ly/d/help/next-js-best-practices | ||||
|  | ||||
| declare global { | ||||
|   // allow global `var` declarations | ||||
|   // eslint-disable-next-line no-var | ||||
|   var prisma: PrismaClient | undefined; | ||||
| } | ||||
| const globalForPrisma = globalThis as unknown as { | ||||
|   prisma: PrismaClient | undefined; | ||||
| }; | ||||
|  | ||||
| export const prisma = | ||||
|   global.prisma || | ||||
|   globalForPrisma.prisma ?? | ||||
|   new PrismaClient({ | ||||
|     log: ["query"], | ||||
|   }); | ||||
|  | ||||
| if (IS_DEV_SERVER) global.prisma = prisma; | ||||
| if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma; | ||||
|   | ||||
| @@ -1,38 +0,0 @@ | ||||
| import * as Sentry from "@sentry/node"; | ||||
| import "@sentry/tracing"; | ||||
| import { BUILD_ENV } from "../config/constants"; | ||||
|  | ||||
| const IsomorphicSentry = () => { | ||||
|   // https://docs.sentry.io/platforms/node/configuration/options/ | ||||
|   Sentry.init({ | ||||
|     dsn: process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN || "", | ||||
|     environment: BUILD_ENV, | ||||
|     tracesSampleRate: 1.0, | ||||
|   }); | ||||
|  | ||||
|   return Sentry; | ||||
| }; | ||||
|  | ||||
| export const logServerError = async (error: unknown) => { | ||||
|   try { | ||||
|     const sentryInstance = IsomorphicSentry(); | ||||
|  | ||||
|     // log error to sentry | ||||
|     sentryInstance.captureException(error); | ||||
|  | ||||
|     // give it 2 seconds to finish sending: | ||||
|     // https://docs.sentry.io/platforms/node/configuration/draining/ | ||||
|     await sentryInstance.flush(2000); | ||||
|   } catch (sentryError) { | ||||
|     // cue inception bong | ||||
|     console.error("Encountered an error logging an error... We are doomed.", sentryError); | ||||
|   } | ||||
|  | ||||
|   // also log the error normally to the Vercel console; will get picked up by log drain | ||||
|   console.error(error); | ||||
|  | ||||
|   // we really don't want to return *any* error from logging an error, so just keep it on the dl | ||||
|   return Promise.resolve(); | ||||
| }; | ||||
|  | ||||
| export default IsomorphicSentry; | ||||
							
								
								
									
										397
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										397
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -16,10 +16,9 @@ | ||||
|         "@primer/octicons": "^19.4.0", | ||||
|         "@prisma/client": "^4.16.1", | ||||
|         "@react-spring/web": "^9.7.2", | ||||
|         "@sentry/node": "^7.56.0", | ||||
|         "@sentry/tracing": "^7.56.0", | ||||
|         "@stitches/react": "^1.2.8", | ||||
|         "@vercel/analytics": "^1.0.1", | ||||
|         "@vercel/postgres": "^0.4.0", | ||||
|         "comma-number": "^2.1.0", | ||||
|         "copy-to-clipboard": "^3.3.3", | ||||
|         "dayjs": "^1.11.8", | ||||
| @@ -50,13 +49,13 @@ | ||||
|         "react-player": "~2.10.1", | ||||
|         "react-textarea-autosize": "^8.5.0", | ||||
|         "react-twitter-embed": "^4.0.4", | ||||
|         "rehype-prism-plus": "^1.5.1", | ||||
|         "rehype-prism-plus": "^1.6.1", | ||||
|         "rehype-slug": "^5.1.0", | ||||
|         "remark-gfm": "^3.0.1", | ||||
|         "remark-smartypants": "^2.0.0", | ||||
|         "remark-unwrap-images": "^3.0.1", | ||||
|         "remove-markdown": "^0.5.0", | ||||
|         "simple-icons": "^9.2.0", | ||||
|         "simple-icons": "^9.3.0", | ||||
|         "sitemap": "^7.1.1", | ||||
|         "stitches-normalize": "^2.0.0", | ||||
|         "swr": "^2.2.0" | ||||
| @@ -69,7 +68,7 @@ | ||||
|         "@types/node": "*", | ||||
|         "@types/novnc__novnc": "^1.3.0", | ||||
|         "@types/prop-types": "^15.7.5", | ||||
|         "@types/react": "^18.2.13", | ||||
|         "@types/react": "^18.2.14", | ||||
|         "@types/react-dom": "^18.2.6", | ||||
|         "@types/react-is": "^18.2.1", | ||||
|         "@types/remove-markdown": "^0.3.1", | ||||
| @@ -2281,6 +2280,14 @@ | ||||
|         "react": ">=16" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@neondatabase/serverless": { | ||||
|       "version": "0.4.19", | ||||
|       "resolved": "https://registry.npmjs.org/@neondatabase/serverless/-/serverless-0.4.19.tgz", | ||||
|       "integrity": "sha512-atzpP9uBLrWPokXZdVy2iNPmmxVx51HXhrEObDb4KXmsyGEkGhCcb6cs51LA/oyXwjJUeB4AreOoVgMcPAIv5g==", | ||||
|       "dependencies": { | ||||
|         "@types/pg": "8.6.6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@next/env": { | ||||
|       "version": "13.4.7", | ||||
|       "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.7.tgz", | ||||
| @@ -2576,9 +2583,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { | ||||
|       "version": "9.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", | ||||
|       "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", | ||||
|       "version": "9.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.2.tgz", | ||||
|       "integrity": "sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "brace-expansion": "^2.0.1" | ||||
| @@ -2705,12 +2712,6 @@ | ||||
|         "url": "https://opencollective.com/unts" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@pkgr/utils/node_modules/tslib": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | ||||
|       "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/@primer/octicons": { | ||||
|       "version": "19.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-19.4.0.tgz", | ||||
| @@ -2824,82 +2825,6 @@ | ||||
|       "integrity": "sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/@sentry-internal/tracing": { | ||||
|       "version": "7.56.0", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.56.0.tgz", | ||||
|       "integrity": "sha512-OKI4Pz/O13gng8hT9rNc+gRV3+P7nnk1HnHlV8fgaQydS6DsRxoDL1sHa42tZGbh7K9jqNAP3TC6VjBOsr2tXA==", | ||||
|       "dependencies": { | ||||
|         "@sentry/core": "7.56.0", | ||||
|         "@sentry/types": "7.56.0", | ||||
|         "@sentry/utils": "7.56.0", | ||||
|         "tslib": "^1.9.3" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@sentry/core": { | ||||
|       "version": "7.56.0", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.56.0.tgz", | ||||
|       "integrity": "sha512-Nuyyfh09Yz27kPo74fXHlrdmZeK6zrlJVtxQ6LkwuoaTBcNcesNXVaOtr6gjvUGUmsfriVPP3Jero5LXufV7GQ==", | ||||
|       "dependencies": { | ||||
|         "@sentry/types": "7.56.0", | ||||
|         "@sentry/utils": "7.56.0", | ||||
|         "tslib": "^1.9.3" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@sentry/node": { | ||||
|       "version": "7.56.0", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.56.0.tgz", | ||||
|       "integrity": "sha512-QXbWy/ypRxfFd8iP6zLvHInYZyjGKPrkVNYt43mhKAZHm764NxX/29vDfj1FztgG9Z6lVLIG2eyqTvLruYmsWw==", | ||||
|       "dependencies": { | ||||
|         "@sentry-internal/tracing": "7.56.0", | ||||
|         "@sentry/core": "7.56.0", | ||||
|         "@sentry/types": "7.56.0", | ||||
|         "@sentry/utils": "7.56.0", | ||||
|         "cookie": "^0.4.1", | ||||
|         "https-proxy-agent": "^5.0.0", | ||||
|         "lru_map": "^0.3.3", | ||||
|         "tslib": "^1.9.3" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@sentry/tracing": { | ||||
|       "version": "7.56.0", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.56.0.tgz", | ||||
|       "integrity": "sha512-Qy7lJdC2YBk9T8JFt4da7xHB3pTZH6yUiIwo5edmSBv2cY6MQ0QZgLzsjJurjf47+/WecVYYKdye9q4twsBlDA==", | ||||
|       "dependencies": { | ||||
|         "@sentry-internal/tracing": "7.56.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@sentry/types": { | ||||
|       "version": "7.56.0", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.56.0.tgz", | ||||
|       "integrity": "sha512-5WjhVOQm75ItOytOx2jTx+5yw8/qJ316+g1Di8dS9+kgIi1zniqdMcX00C2yYe3FMUgFB49PegCUYulm9Evapw==", | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@sentry/utils": { | ||||
|       "version": "7.56.0", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.56.0.tgz", | ||||
|       "integrity": "sha512-wgeX7bufxc//TjjSIE+gCMm8hVId7Jzvc+f441bYrWnNZBuzPIDW2BummCcPrKzSYe5GeYZDTZGV8YZGMLGBjw==", | ||||
|       "dependencies": { | ||||
|         "@sentry/types": "7.56.0", | ||||
|         "tslib": "^1.9.3" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@stitches/react": { | ||||
|       "version": "1.2.8", | ||||
|       "resolved": "https://registry.npmjs.org/@stitches/react/-/react-1.2.8.tgz", | ||||
| @@ -3173,11 +3098,6 @@ | ||||
|         "tslib": "^2.4.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@swc/helpers/node_modules/tslib": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | ||||
|       "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" | ||||
|     }, | ||||
|     "node_modules/@trysound/sax": { | ||||
|       "version": "0.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", | ||||
| @@ -3305,6 +3225,16 @@ | ||||
|       "integrity": "sha512-IiNumU7H5ezJhJlhFEUk8ouQ4z+DOX6Ldvlh2MwQ+fsN03FMLHJyAIBLVMaC+1pC43BMAqmTNMTK85sFlq0ADw==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/@types/pg": { | ||||
|       "version": "8.6.6", | ||||
|       "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.6.tgz", | ||||
|       "integrity": "sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==", | ||||
|       "dependencies": { | ||||
|         "@types/node": "*", | ||||
|         "pg-protocol": "*", | ||||
|         "pg-types": "^2.2.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/prismjs": { | ||||
|       "version": "1.26.0", | ||||
|       "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.0.tgz", | ||||
| @@ -3316,9 +3246,9 @@ | ||||
|       "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" | ||||
|     }, | ||||
|     "node_modules/@types/react": { | ||||
|       "version": "18.2.13", | ||||
|       "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.13.tgz", | ||||
|       "integrity": "sha512-vJ+zElvi/Zn9cVXB5slX2xL8PZodPCwPRDpittQdw43JR2AJ5k3vKdgJJyneV/cYgIbLQUwXa9JVDvUZXGba+Q==", | ||||
|       "version": "18.2.14", | ||||
|       "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.14.tgz", | ||||
|       "integrity": "sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==", | ||||
|       "dependencies": { | ||||
|         "@types/prop-types": "*", | ||||
|         "@types/scheduler": "*", | ||||
| @@ -3685,6 +3615,20 @@ | ||||
|       "resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.0.1.tgz", | ||||
|       "integrity": "sha512-Ux0c9qUfkcPqng3vrR0GTrlQdqNJ2JREn/2ydrVuKwM3RtMfF2mWX31Ijqo1opSjNAq6rK76PwtANw6kl6TAow==" | ||||
|     }, | ||||
|     "node_modules/@vercel/postgres": { | ||||
|       "version": "0.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/@vercel/postgres/-/postgres-0.4.0.tgz", | ||||
|       "integrity": "sha512-GemRRKvLi90BC511WQzt5tfVLPNOQ9eNbbbaPquSgasm5ogfWLtjJSbNUD4jYEbs9IxbNovKqr0H0CAjJzOAyw==", | ||||
|       "dependencies": { | ||||
|         "@neondatabase/serverless": "0.4.19", | ||||
|         "bufferutil": "4.0.7", | ||||
|         "utf-8-validate": "6.0.3", | ||||
|         "ws": "8.13.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=14.6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/abbrev": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", | ||||
| @@ -3713,17 +3657,6 @@ | ||||
|         "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/agent-base": { | ||||
|       "version": "6.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", | ||||
|       "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", | ||||
|       "dependencies": { | ||||
|         "debug": "4" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 6.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/aggregate-error": { | ||||
|       "version": "3.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", | ||||
| @@ -3800,9 +3733,9 @@ | ||||
|       "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" | ||||
|     }, | ||||
|     "node_modules/aria-query": { | ||||
|       "version": "5.2.1", | ||||
|       "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.2.1.tgz", | ||||
|       "integrity": "sha512-7uFg4b+lETFgdaJyETnILsXgnnzVnkHcgRbwbPwevm5x/LmUlt3MjczMRe1zg824iBgXZNRPTBftNYyRSKLp2g==", | ||||
|       "version": "5.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", | ||||
|       "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "dequal": "^2.0.3" | ||||
| @@ -4155,6 +4088,18 @@ | ||||
|       "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/bufferutil": { | ||||
|       "version": "4.0.7", | ||||
|       "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", | ||||
|       "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", | ||||
|       "hasInstallScript": true, | ||||
|       "dependencies": { | ||||
|         "node-gyp-build": "^4.3.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=6.14.2" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/bundle-name": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", | ||||
| @@ -4475,14 +4420,6 @@ | ||||
|       "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/cookie": { | ||||
|       "version": "0.4.2", | ||||
|       "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", | ||||
|       "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", | ||||
|       "engines": { | ||||
|         "node": ">= 0.6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/copy-to-clipboard": { | ||||
|       "version": "3.3.3", | ||||
|       "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", | ||||
| @@ -4909,12 +4846,6 @@ | ||||
|         "tslib": "^2.0.3" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/dot-case/node_modules/tslib": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | ||||
|       "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/eastasianwidth": { | ||||
|       "version": "0.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", | ||||
| @@ -4922,9 +4853,9 @@ | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/electron-to-chromium": { | ||||
|       "version": "1.4.439", | ||||
|       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.439.tgz", | ||||
|       "integrity": "sha512-BHpErPSNhb9FB25+OwQP6mCAf3ZXfGbmuvc4LzBNVJwpCcXQJm++LerimocYRG9FRxUVRKZqaB7d0+pImSTPSg==", | ||||
|       "version": "1.4.440", | ||||
|       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.440.tgz", | ||||
|       "integrity": "sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/emoji-regex": { | ||||
| @@ -5285,12 +5216,6 @@ | ||||
|         "eslint": ">=8.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/eslint-mdx/node_modules/tslib": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | ||||
|       "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/eslint-module-utils": { | ||||
|       "version": "2.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", | ||||
| @@ -5438,12 +5363,6 @@ | ||||
|         "eslint": ">=8.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/eslint-plugin-mdx/node_modules/tslib": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | ||||
|       "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/eslint-plugin-prettier": { | ||||
|       "version": "4.2.1", | ||||
|       "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", | ||||
| @@ -6133,11 +6052,6 @@ | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/formik/node_modules/tslib": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | ||||
|       "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" | ||||
|     }, | ||||
|     "node_modules/fs-constants": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", | ||||
| @@ -6398,11 +6312,6 @@ | ||||
|         "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/graphql-tag/node_modules/tslib": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | ||||
|       "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" | ||||
|     }, | ||||
|     "node_modules/gray-matter": { | ||||
|       "version": "4.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", | ||||
| @@ -6645,18 +6554,6 @@ | ||||
|       "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", | ||||
|       "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" | ||||
|     }, | ||||
|     "node_modules/https-proxy-agent": { | ||||
|       "version": "5.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", | ||||
|       "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", | ||||
|       "dependencies": { | ||||
|         "agent-base": "6", | ||||
|         "debug": "4" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">= 6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/human-signals": { | ||||
|       "version": "4.3.1", | ||||
|       "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", | ||||
| @@ -7764,17 +7661,6 @@ | ||||
|         "tslib": "^2.0.3" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/lower-case/node_modules/tslib": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | ||||
|       "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/lru_map": { | ||||
|       "version": "0.3.3", | ||||
|       "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", | ||||
|       "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==" | ||||
|     }, | ||||
|     "node_modules/lru-cache": { | ||||
|       "version": "5.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", | ||||
| @@ -9588,12 +9474,6 @@ | ||||
|         "tslib": "^2.0.3" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/no-case/node_modules/tslib": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | ||||
|       "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/node-abi": { | ||||
|       "version": "3.45.0", | ||||
|       "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.45.0.tgz", | ||||
| @@ -9645,6 +9525,16 @@ | ||||
|       "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", | ||||
|       "optional": true | ||||
|     }, | ||||
|     "node_modules/node-gyp-build": { | ||||
|       "version": "4.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", | ||||
|       "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", | ||||
|       "bin": { | ||||
|         "node-gyp-build": "bin.js", | ||||
|         "node-gyp-build-optional": "optional.js", | ||||
|         "node-gyp-build-test": "build-test.js" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/node-releases": { | ||||
|       "version": "2.0.12", | ||||
|       "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", | ||||
| @@ -10120,6 +10010,34 @@ | ||||
|         "is-reference": "^3.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/pg-int8": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", | ||||
|       "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", | ||||
|       "engines": { | ||||
|         "node": ">=4.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/pg-protocol": { | ||||
|       "version": "1.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", | ||||
|       "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" | ||||
|     }, | ||||
|     "node_modules/pg-types": { | ||||
|       "version": "2.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", | ||||
|       "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", | ||||
|       "dependencies": { | ||||
|         "pg-int8": "1.0.1", | ||||
|         "postgres-array": "~2.0.0", | ||||
|         "postgres-bytea": "~1.0.0", | ||||
|         "postgres-date": "~1.0.4", | ||||
|         "postgres-interval": "^1.1.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/picocolors": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", | ||||
| @@ -10182,6 +10100,41 @@ | ||||
|         "node": "^10 || ^12 || >=14" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/postgres-array": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", | ||||
|       "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", | ||||
|       "engines": { | ||||
|         "node": ">=4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/postgres-bytea": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", | ||||
|       "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", | ||||
|       "engines": { | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/postgres-date": { | ||||
|       "version": "1.0.7", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", | ||||
|       "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", | ||||
|       "engines": { | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/postgres-interval": { | ||||
|       "version": "1.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", | ||||
|       "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", | ||||
|       "dependencies": { | ||||
|         "xtend": "^4.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/prebuild-install": { | ||||
|       "version": "7.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", | ||||
| @@ -10723,13 +10676,13 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/rehype-prism-plus": { | ||||
|       "version": "1.5.1", | ||||
|       "resolved": "https://registry.npmjs.org/rehype-prism-plus/-/rehype-prism-plus-1.5.1.tgz", | ||||
|       "integrity": "sha512-mowYefSfrIkMMxkb0fwuEXlvc5nA9b1vQ6mzujM81Qx28RI0mo7jCHsBZ2tJ4eIJKXdFn+EdPkZZBGB10K02vg==", | ||||
|       "version": "1.6.1", | ||||
|       "resolved": "https://registry.npmjs.org/rehype-prism-plus/-/rehype-prism-plus-1.6.1.tgz", | ||||
|       "integrity": "sha512-HCLGvrbn9J1FSvsHo3/mJstToVJGe8czeUULpgPZhc3X21XJHCjYlZSBL4dH784WPUz/sW4PCATf8J1o7fgblA==", | ||||
|       "dependencies": { | ||||
|         "hast-util-to-string": "^2.0.0", | ||||
|         "parse-numeric-range": "^1.3.0", | ||||
|         "refractor": "^4.7.0", | ||||
|         "refractor": "^4.8.0", | ||||
|         "rehype-parse": "^8.0.2", | ||||
|         "unist-util-filter": "^4.0.0", | ||||
|         "unist-util-visit": "^4.0.0" | ||||
| @@ -11235,12 +11188,6 @@ | ||||
|         "tslib": "^2.1.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/rxjs/node_modules/tslib": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | ||||
|       "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/sade": { | ||||
|       "version": "1.8.1", | ||||
|       "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", | ||||
| @@ -11478,9 +11425,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/simple-icons": { | ||||
|       "version": "9.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-9.2.0.tgz", | ||||
|       "integrity": "sha512-BwLa76uMHBZKN1+L7Qmboj47mcFADiA/dfAePOd3arSPv6JMRNptJ2FNDBLuqkKliWG2W9XuBKhunuv+GzPpLA==", | ||||
|       "version": "9.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-9.3.0.tgz", | ||||
|       "integrity": "sha512-T6x3teIpPdMc8nibYvE56Va1eF+l8Puk//1YYtCxQP4wbGRzYxgwZTl18YCjoFLnu/0exehwyR2hMEFZV3lflg==", | ||||
|       "engines": { | ||||
|         "node": ">=0.12.18" | ||||
|       }, | ||||
| @@ -11583,12 +11530,6 @@ | ||||
|         "tslib": "^2.0.3" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/snake-case/node_modules/tslib": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | ||||
|       "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/source-map": { | ||||
|       "version": "0.6.1", | ||||
|       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", | ||||
| @@ -12016,12 +11957,6 @@ | ||||
|         "url": "https://opencollective.com/unts" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/synckit/node_modules/tslib": { | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | ||||
|       "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/tapable": { | ||||
|       "version": "2.2.1", | ||||
|       "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", | ||||
| @@ -12170,9 +12105,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/tslib": { | ||||
|       "version": "1.14.1", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", | ||||
|       "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" | ||||
|       "version": "2.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", | ||||
|       "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" | ||||
|     }, | ||||
|     "node_modules/tsutils": { | ||||
|       "version": "3.21.0", | ||||
| @@ -12189,6 +12124,12 @@ | ||||
|         "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/tsutils/node_modules/tslib": { | ||||
|       "version": "1.14.1", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", | ||||
|       "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/tunnel-agent": { | ||||
|       "version": "0.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", | ||||
| @@ -12703,6 +12644,18 @@ | ||||
|         "react": "^16.8.0 || ^17.0.0 || ^18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/utf-8-validate": { | ||||
|       "version": "6.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.3.tgz", | ||||
|       "integrity": "sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==", | ||||
|       "hasInstallScript": true, | ||||
|       "dependencies": { | ||||
|         "node-gyp-build": "^4.3.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=6.14.2" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/util-deprecate": { | ||||
|       "version": "1.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", | ||||
| @@ -13129,6 +13082,26 @@ | ||||
|       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", | ||||
|       "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" | ||||
|     }, | ||||
|     "node_modules/ws": { | ||||
|       "version": "8.13.0", | ||||
|       "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", | ||||
|       "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", | ||||
|       "engines": { | ||||
|         "node": ">=10.0.0" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "bufferutil": "^4.0.1", | ||||
|         "utf-8-validate": ">=5.0.2" | ||||
|       }, | ||||
|       "peerDependenciesMeta": { | ||||
|         "bufferutil": { | ||||
|           "optional": true | ||||
|         }, | ||||
|         "utf-8-validate": { | ||||
|           "optional": true | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/xml-js": { | ||||
|       "version": "1.6.11", | ||||
|       "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", | ||||
| @@ -13140,6 +13113,14 @@ | ||||
|         "xml-js": "bin/cli.js" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/xtend": { | ||||
|       "version": "4.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", | ||||
|       "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", | ||||
|       "engines": { | ||||
|         "node": ">=0.4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/yallist": { | ||||
|       "version": "3.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", | ||||
|   | ||||
							
								
								
									
										11
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								package.json
									
									
									
									
									
								
							| @@ -15,7 +15,7 @@ | ||||
|     "build": "next build", | ||||
|     "start": "next start", | ||||
|     "lint": "eslint . --ext js,jsx,ts,tsx,md,mdx", | ||||
|     "postinstall": "prisma generate" | ||||
|     "postinstall": "prisma generate --data-proxy" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@giscus/react": "^2.2.8", | ||||
| @@ -26,10 +26,9 @@ | ||||
|     "@primer/octicons": "^19.4.0", | ||||
|     "@prisma/client": "^4.16.1", | ||||
|     "@react-spring/web": "^9.7.2", | ||||
|     "@sentry/node": "^7.56.0", | ||||
|     "@sentry/tracing": "^7.56.0", | ||||
|     "@stitches/react": "^1.2.8", | ||||
|     "@vercel/analytics": "^1.0.1", | ||||
|     "@vercel/postgres": "^0.4.0", | ||||
|     "comma-number": "^2.1.0", | ||||
|     "copy-to-clipboard": "^3.3.3", | ||||
|     "dayjs": "^1.11.8", | ||||
| @@ -60,13 +59,13 @@ | ||||
|     "react-player": "~2.10.1", | ||||
|     "react-textarea-autosize": "^8.5.0", | ||||
|     "react-twitter-embed": "^4.0.4", | ||||
|     "rehype-prism-plus": "^1.5.1", | ||||
|     "rehype-prism-plus": "^1.6.1", | ||||
|     "rehype-slug": "^5.1.0", | ||||
|     "remark-gfm": "^3.0.1", | ||||
|     "remark-smartypants": "^2.0.0", | ||||
|     "remark-unwrap-images": "^3.0.1", | ||||
|     "remove-markdown": "^0.5.0", | ||||
|     "simple-icons": "^9.2.0", | ||||
|     "simple-icons": "^9.3.0", | ||||
|     "sitemap": "^7.1.1", | ||||
|     "stitches-normalize": "^2.0.0", | ||||
|     "swr": "^2.2.0" | ||||
| @@ -79,7 +78,7 @@ | ||||
|     "@types/node": "*", | ||||
|     "@types/novnc__novnc": "^1.3.0", | ||||
|     "@types/prop-types": "^15.7.5", | ||||
|     "@types/react": "^18.2.13", | ||||
|     "@types/react": "^18.2.14", | ||||
|     "@types/react-dom": "^18.2.6", | ||||
|     "@types/react-is": "^18.2.1", | ||||
|     "@types/remove-markdown": "^0.3.1", | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import { NextResponse } from "next/server"; | ||||
| import queryString from "query-string"; | ||||
| import { logServerError } from "../../lib/helpers/sentry"; | ||||
| import type { NextApiRequest, NextApiResponse } from "next"; | ||||
| import { baseUrl } from "../../lib/config"; | ||||
| import type { NextRequest } from "next/server"; | ||||
|  | ||||
| // fallback to dummy secret for testing: https://docs.hcaptcha.com/#integration-testing-test-keys | ||||
| const HCAPTCHA_SITE_KEY = | ||||
| @@ -11,56 +12,55 @@ const HCAPTCHA_API_ENDPOINT = "https://hcaptcha.com/siteverify"; | ||||
| const { AIRTABLE_API_KEY, AIRTABLE_BASE } = process.env; | ||||
| const AIRTABLE_API_ENDPOINT = "https://api.airtable.com/v0/"; | ||||
|  | ||||
| const handler = async (req: NextApiRequest, res: NextApiResponse) => { | ||||
|   try { | ||||
|     // disable caching on both ends | ||||
|     res.setHeader("Cache-Control", "private, no-cache, no-store, must-revalidate"); | ||||
|     res.setHeader("Pragma", "no-cache"); | ||||
| export const config = { | ||||
|   runtime: "edge", | ||||
| }; | ||||
|  | ||||
|     // redirect GET requests to this endpoint to the contact form itself | ||||
|     if (req.method === "GET") { | ||||
|       return res.redirect(302, "/contact/"); | ||||
|     } | ||||
|  | ||||
|     const { body } = req; | ||||
|  | ||||
|     // these are both backups to client-side validations just in case someone squeezes through without them. the codes | ||||
|     // are identical so they're caught in the same fashion. | ||||
|     if (!body.name || !body.email || !body.message) { | ||||
|       // all fields are required | ||||
|       throw new Error("USER_MISSING_DATA"); | ||||
|     } | ||||
|     if (!body["h-captcha-response"] || !(await validateCaptcha(body["h-captcha-response"]))) { | ||||
|       // either the captcha is wrong or completely missing | ||||
|       throw new Error("USER_INVALID_CAPTCHA"); | ||||
|     } | ||||
|  | ||||
|     // sent directly to airtable | ||||
|     const airtableResult = await sendToAirtable({ | ||||
|       Name: body.name, | ||||
|       Email: body.email, | ||||
|       Message: body.message, | ||||
|     }); | ||||
|  | ||||
|     // throw an internal error, not user's fault | ||||
|     if (airtableResult !== true) { | ||||
|       throw new Error("AIRTABLE_API_ERROR"); | ||||
|     } | ||||
|  | ||||
|     // success! let the client know | ||||
|     return res.status(200).json({ success: true }); | ||||
|   } catch (error) { | ||||
|     // extract just the error message to send back to client | ||||
|     const message = error instanceof Error ? error.message : "UNKNOWN_EXCEPTION"; | ||||
|  | ||||
|     // log errors (except PEBCAK) to console and sentry | ||||
|     if (!message.startsWith("USER_")) { | ||||
|       await logServerError(error); | ||||
|     } | ||||
|  | ||||
|     // 500 Internal Server Error | ||||
|     return res.status(500).json({ success: false, message }); | ||||
| // eslint-disable-next-line import/no-anonymous-default-export | ||||
| export default async (req: NextRequest) => { | ||||
|   // redirect GET requests to this endpoint to the contact form itself | ||||
|   if (req.method === "GET") { | ||||
|     return NextResponse.redirect(`${baseUrl}/contact/`); | ||||
|   } | ||||
|  | ||||
|   // possible weirdness? https://github.com/orgs/vercel/discussions/78#discussioncomment-5089059 | ||||
|   const data = await req.json(); | ||||
|  | ||||
|   // these are both backups to client-side validations just in case someone squeezes through without them. the codes | ||||
|   // are identical so they're caught in the same fashion. | ||||
|   if (!data.name || !data.email || !data.message) { | ||||
|     // all fields are required | ||||
|     throw new Error("MISSING_DATA"); | ||||
|   } | ||||
|   if (!data["h-captcha-response"] || !(await validateCaptcha(data["h-captcha-response"]))) { | ||||
|     // either the captcha is wrong or completely missing | ||||
|     throw new Error("INVALID_CAPTCHA"); | ||||
|   } | ||||
|  | ||||
|   // sent directly to airtable | ||||
|   const airtableResult = await sendToAirtable({ | ||||
|     Name: data.name, | ||||
|     Email: data.email, | ||||
|     Message: data.message, | ||||
|   }); | ||||
|  | ||||
|   // throw an internal error, not user's fault | ||||
|   if (airtableResult !== true) { | ||||
|     throw new Error("AIRTABLE_API_ERROR"); | ||||
|   } | ||||
|  | ||||
|   // success! let the client know | ||||
|   return NextResponse.json( | ||||
|     { success: true }, | ||||
|     { | ||||
|       status: 201, | ||||
|       headers: { | ||||
|         // disable caching on both ends. see: | ||||
|         // https://vercel.com/docs/concepts/functions/edge-functions/edge-caching | ||||
|         "Cache-Control": "private, no-cache, no-store, must-revalidate", | ||||
|       }, | ||||
|     } | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| const validateCaptcha = async (formResponse: unknown): Promise<unknown> => { | ||||
| @@ -95,5 +95,3 @@ const sendToAirtable = async (data: unknown): Promise<boolean> => { | ||||
|  | ||||
|   return response.ok; | ||||
| }; | ||||
|  | ||||
| export default handler; | ||||
|   | ||||
| @@ -1,32 +1,30 @@ | ||||
| import { NextResponse } from "next/server"; | ||||
| import { prisma } from "../../lib/helpers/prisma"; | ||||
| import { logServerError } from "../../lib/helpers/sentry"; | ||||
| import type { NextApiRequest, NextApiResponse } from "next"; | ||||
| import type { NextRequest } from "next/server"; | ||||
| import type { PageStats } from "../../types"; | ||||
|  | ||||
| const handler = async (req: NextApiRequest, res: NextApiResponse) => { | ||||
|   try { | ||||
|     if (!req.query?.slug) { | ||||
|       return res.status(400).json({ message: "Missing `slug` parameter." }); | ||||
|     } | ||||
| export const config = { | ||||
|   runtime: "edge", | ||||
|   regions: ["iad1"], // the vercel postgres database lives in DC | ||||
| }; | ||||
|  | ||||
|     // add one to this page's count and return the new number | ||||
|     const result = await incrementPageHits(req.query.slug as string); | ||||
| // eslint-disable-next-line import/no-anonymous-default-export | ||||
| export default async (req: NextRequest) => { | ||||
|   const slug = req.nextUrl.searchParams.get("slug"); | ||||
|  | ||||
|     // disable caching on both ends | ||||
|     res.setHeader("Cache-Control", "private, no-cache, no-store, must-revalidate"); | ||||
|  | ||||
|     // send result as JSON | ||||
|     return res.status(200).json(result); | ||||
|   } catch (error) { | ||||
|     // extract just the error message to send back to client | ||||
|     const message = error instanceof Error ? error.message : error; | ||||
|  | ||||
|     // log full error to console and sentry | ||||
|     await logServerError(error); | ||||
|  | ||||
|     // 500 Internal Server Error | ||||
|     return res.status(500).json({ message }); | ||||
|   if (!slug) { | ||||
|     return NextResponse.json({ message: "Missing `slug` parameter." }, { status: 400 }); | ||||
|   } | ||||
|  | ||||
|   // add one to this page's count and return the new number | ||||
|   return NextResponse.json(await incrementPageHits(slug), { | ||||
|     status: 200, | ||||
|     headers: { | ||||
|       // disable caching on both ends. see: | ||||
|       // https://vercel.com/docs/concepts/functions/edge-functions/edge-caching | ||||
|       "Cache-Control": "private, no-cache, no-store, must-revalidate", | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| const incrementPageHits = async (slug: string): Promise<PageStats> => { | ||||
| @@ -43,5 +41,3 @@ const incrementPageHits = async (slug: string): Promise<PageStats> => { | ||||
|   // send client the *new* hit count | ||||
|   return { hits }; | ||||
| }; | ||||
|  | ||||
| export default handler; | ||||
|   | ||||
| @@ -1,71 +1,29 @@ | ||||
| import { prisma } from "../../lib/helpers/prisma"; | ||||
| import { getAllNotes } from "../../lib/helpers/parse-notes"; | ||||
| import { logServerError } from "../../lib/helpers/sentry"; | ||||
| import { NOTES_DIR } from "../../lib/config/constants"; | ||||
| import type { NextApiRequest, NextApiResponse } from "next"; | ||||
| import type { DetailedPageStats, SiteStats } from "../../types"; | ||||
| import { NextResponse } from "next/server"; | ||||
|  | ||||
| const handler = async (req: NextApiRequest, res: NextApiResponse) => { | ||||
|   try { | ||||
|     // return overall site stats | ||||
|     const result = await getSiteStats(); | ||||
|  | ||||
|     // let Vercel edge cache results for 15 mins | ||||
|     res.setHeader("Cache-Control", "public, max-age=0, s-maxage=900, stale-while-revalidate"); | ||||
|  | ||||
|     // send result as JSON | ||||
|     return res.status(200).json(result); | ||||
|   } catch (error) { | ||||
|     // extract just the error message to send back to client | ||||
|     const message = error instanceof Error ? error.message : error; | ||||
|  | ||||
|     // log full error to console and sentry | ||||
|     await logServerError(error); | ||||
|  | ||||
|     // 500 Internal Server Error | ||||
|     return res.status(500).json({ message }); | ||||
|   } | ||||
| export const config = { | ||||
|   runtime: "edge", | ||||
|   regions: ["iad1"], // the vercel postgres database lives in DC | ||||
| }; | ||||
|  | ||||
| const getSiteStats = async (): Promise<SiteStats> => { | ||||
| // eslint-disable-next-line import/no-anonymous-default-export | ||||
| export default async () => { | ||||
|   // simultaneously fetch the entire hits db and notes from the filesystem | ||||
|   const [hits, notes] = await Promise.all([ | ||||
|     prisma.hits.findMany({ | ||||
|       orderBy: [ | ||||
|         { | ||||
|           hits: "desc", | ||||
|         }, | ||||
|       ], | ||||
|     }), | ||||
|     getAllNotes(), | ||||
|   ]); | ||||
|  | ||||
|   const pages: DetailedPageStats[] = []; | ||||
|   const total = { hits: 0 }; | ||||
|  | ||||
|   hits.forEach((record) => { | ||||
|     // match slugs from getAllNotes() with db results to populate some metadata | ||||
|     // TODO: add support for pages other than notes. | ||||
|     const match = notes.find((note) => `${NOTES_DIR}/${note.slug}` === record.slug); | ||||
|  | ||||
|     // don't reveal via API if the db entry doesn't belong to a valid page | ||||
|     if (!match) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     // merge record with its matching front matter data | ||||
|     pages.push({ | ||||
|       ...record, | ||||
|       title: match.title, | ||||
|       url: match.permalink, | ||||
|       date: match.date, | ||||
|     }); | ||||
|  | ||||
|     // add these hits to running tally | ||||
|     total.hits += record.hits; | ||||
|   const pages = await prisma.hits.findMany({ | ||||
|     orderBy: [ | ||||
|       { | ||||
|         hits: "desc", | ||||
|       }, | ||||
|     ], | ||||
|   }); | ||||
|  | ||||
|   return { total, pages }; | ||||
| }; | ||||
|   const total = { hits: 0 }; | ||||
|  | ||||
| export default handler; | ||||
|   // calculate total hits | ||||
|   pages.forEach((page) => { | ||||
|     // add these hits to running tally | ||||
|     total.hits += page.hits; | ||||
|   }); | ||||
|  | ||||
|   return NextResponse.json({ total, pages }, { status: 200 }); | ||||
| }; | ||||
|   | ||||
| @@ -2,8 +2,7 @@ | ||||
| // Heavily inspired by @leerob: https://leerob.io/snippets/spotify | ||||
|  | ||||
| import queryString from "query-string"; | ||||
| import { logServerError } from "../../lib/helpers/sentry"; | ||||
| import type { NextApiRequest, NextApiResponse } from "next"; | ||||
| import { NextResponse } from "next/server"; | ||||
| import type { Track } from "../../types"; | ||||
|  | ||||
| const { SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, SPOTIFY_REFRESH_TOKEN } = process.env; | ||||
| @@ -32,25 +31,16 @@ type SpotifyTrackSchema = { | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| const handler = async (req: NextApiRequest, res: NextApiResponse) => { | ||||
|   try { | ||||
|     // let Vercel edge cache results for 5 mins | ||||
|     res.setHeader("Cache-Control", "public, max-age=0, s-maxage=300, stale-while-revalidate"); | ||||
| export const config = { | ||||
|   runtime: "edge", | ||||
| }; | ||||
|  | ||||
|     const token = await getAccessToken(); | ||||
|     const playing = await getNowPlaying(token); | ||||
|     const top = await getTopTracks(token); | ||||
| // eslint-disable-next-line import/no-anonymous-default-export | ||||
| export default async () => { | ||||
|   const token = await getAccessToken(); | ||||
|   const [playing, top] = await Promise.all([getNowPlaying(token), getTopTracks(token)]); | ||||
|  | ||||
|     return res.status(200).json({ playing, top }); | ||||
|   } catch (error) { | ||||
|     const message = error instanceof Error ? error.message : "Unknown error."; | ||||
|  | ||||
|     // log full error to console and sentry | ||||
|     await logServerError(error); | ||||
|  | ||||
|     // 500 Internal Server Error | ||||
|     return res.status(500).json({ message }); | ||||
|   } | ||||
|   return NextResponse.json({ playing, top }, { status: 200 }); | ||||
| }; | ||||
|  | ||||
| const getAccessToken = async () => { | ||||
| @@ -125,5 +115,3 @@ const getTopTracks = async (token: string): Promise<Track[]> => { | ||||
|  | ||||
|   return tracks; | ||||
| }; | ||||
|  | ||||
| export default handler; | ||||
|   | ||||
| @@ -5,7 +5,7 @@ generator client { | ||||
|  | ||||
| datasource db { | ||||
|   provider          = "postgresql" | ||||
|   url               = env("POSTGRES_PRISMA_URL") | ||||
|   url               = env("DATABASE_URL") | ||||
|   directUrl         = env("POSTGRES_URL_NON_POOLING") | ||||
|   shadowDatabaseUrl = env("POSTGRES_URL_NON_POOLING") | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 GitHub
						GitHub