diff --git a/examples/nextjs-13-pages-wrap/.env.local.example b/examples/nextjs-13-pages-wrap/.env.local.example new file mode 100644 index 000000000..0c183c7ac --- /dev/null +++ b/examples/nextjs-13-pages-wrap/.env.local.example @@ -0,0 +1 @@ +ARCJET_KEY= \ No newline at end of file diff --git a/examples/nextjs-13-pages-wrap/README.md b/examples/nextjs-13-pages-wrap/README.md index bd9321f62..b44be7ff8 100644 --- a/examples/nextjs-13-pages-wrap/README.md +++ b/examples/nextjs-13-pages-wrap/README.md @@ -25,11 +25,7 @@ Route](https://nextjs.org/docs/pages/building-your-application/routing/api-route npm ci ``` -3. Add your Arcjet key to `.env.local` - - ```env - ARCJET_KEY= - ``` +3. Rename `.env.local.example` to `.env.local` and add your Arcjet key. 4. Start the dev server. diff --git a/examples/nextjs-13-pages-wrap/environment.d.ts b/examples/nextjs-13-pages-wrap/environment.d.ts new file mode 100644 index 000000000..9ae2b0722 --- /dev/null +++ b/examples/nextjs-13-pages-wrap/environment.d.ts @@ -0,0 +1,5 @@ +declare namespace NodeJS { + export interface ProcessEnv { + readonly ARCJET_KEY: string; + } +} \ No newline at end of file diff --git a/examples/nextjs-13-pages-wrap/next.config.js b/examples/nextjs-13-pages-wrap/next.config.js index a843cbee0..ce65fdeef 100644 --- a/examples/nextjs-13-pages-wrap/next.config.js +++ b/examples/nextjs-13-pages-wrap/next.config.js @@ -1,6 +1,9 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, + // Disabled due to errors when building. See + // https://nextjs.org/docs/messages/failed-loading-swc + swcMinify: false, } -module.exports = nextConfig +module.exports = nextConfig \ No newline at end of file diff --git a/examples/nextjs-13-pages-wrap/package-lock.json b/examples/nextjs-13-pages-wrap/package-lock.json index 5db61f1ef..76a544fea 100644 --- a/examples/nextjs-13-pages-wrap/package-lock.json +++ b/examples/nextjs-13-pages-wrap/package-lock.json @@ -27,18 +27,18 @@ }, "../../arcjet-next": { "name": "@arcjet/next", - "version": "1.0.0-alpha.7", + "version": "1.0.0-alpha.8", "license": "Apache-2.0", "dependencies": { - "@arcjet/ip": "1.0.0-alpha.7", + "@arcjet/ip": "1.0.0-alpha.8", "@connectrpc/connect-web": "1.3.0", - "arcjet": "1.0.0-alpha.7", + "arcjet": "1.0.0-alpha.8", "next": "14.1.0" }, "devDependencies": { - "@arcjet/eslint-config": "1.0.0-alpha.7", - "@arcjet/rollup-config": "1.0.0-alpha.7", - "@arcjet/tsconfig": "1.0.0-alpha.7", + "@arcjet/eslint-config": "1.0.0-alpha.8", + "@arcjet/rollup-config": "1.0.0-alpha.8", + "@arcjet/tsconfig": "1.0.0-alpha.8", "@jest/globals": "29.7.0", "@rollup/wasm-node": "4.9.6", "@types/node": "18.18.0", diff --git a/examples/nextjs-13-pages-wrap/pages/api/arcjet-edge.ts b/examples/nextjs-13-pages-wrap/pages/api/arcjet-edge.ts index bafb9abb6..971061b7d 100644 --- a/examples/nextjs-13-pages-wrap/pages/api/arcjet-edge.ts +++ b/examples/nextjs-13-pages-wrap/pages/api/arcjet-edge.ts @@ -10,14 +10,16 @@ const aj = arcjet({ // Get your site key from https://app.arcjet.com // and set it as an environment variable rather than hard coding. // See: https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables - key: process.env.ARCJET_KEY!, + key: process.env.ARCJET_KEY, rules: [ + // Fixed window rate limit. Arcjet also supports sliding window and token + // bucket. fixedWindow({ - mode: "LIVE", + mode: "LIVE", // will block requests. Use "DRY_RUN" to log only // Limiting by ip.src is the default if not specified //characteristics: ["ip.src"], - window: "1m", - max: 1, + window: "1m", // 1 min fixed window + max: 1, // allow a single request (for demo purposes) }), ], }); diff --git a/examples/nextjs-13-pages-wrap/pages/api/arcjet.ts b/examples/nextjs-13-pages-wrap/pages/api/arcjet.ts index fe667c1b2..30a69647a 100644 --- a/examples/nextjs-13-pages-wrap/pages/api/arcjet.ts +++ b/examples/nextjs-13-pages-wrap/pages/api/arcjet.ts @@ -6,14 +6,16 @@ const aj = arcjet({ // Get your site key from https://app.arcjet.com // and set it as an environment variable rather than hard coding. // See: https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables - key: process.env.ARCJET_KEY!, + key: process.env.ARCJET_KEY, rules: [ + // Fixed window rate limit. Arcjet also supports sliding window and token + // bucket. fixedWindow({ - mode: "LIVE", + mode: "LIVE", // will block requests. Use "DRY_RUN" to log only // Limiting by ip.src is the default if not specified //characteristics: ["ip.src"], - window: "1m", - max: 1, + window: "1m", // 1 min fixed window + max: 1, // allow a single request (for demo purposes) }), ], }); diff --git a/examples/nextjs-14-app-dir-rl/.env.local.example b/examples/nextjs-14-app-dir-rl/.env.local.example new file mode 100644 index 000000000..0c183c7ac --- /dev/null +++ b/examples/nextjs-14-app-dir-rl/.env.local.example @@ -0,0 +1 @@ +ARCJET_KEY= \ No newline at end of file diff --git a/examples/nextjs-14-app-dir-rl/README.md b/examples/nextjs-14-app-dir-rl/README.md index 50da1ba5b..2b9612b6f 100644 --- a/examples/nextjs-14-app-dir-rl/README.md +++ b/examples/nextjs-14-app-dir-rl/README.md @@ -25,11 +25,7 @@ handler](https://nextjs.org/docs/app/building-your-application/routing/route-han npm ci ``` -3. Add your Arcjet key to `.env.local` - - ```env - ARCJET_KEY= - ``` +3. Rename `.env.local.example` to `.env.local` and add your Arcjet key. 4. Start the dev server. diff --git a/examples/nextjs-14-app-dir-rl/app/api/arcjet/route.ts b/examples/nextjs-14-app-dir-rl/app/api/arcjet/route.ts index 94dfe92aa..31cc33ad0 100644 --- a/examples/nextjs-14-app-dir-rl/app/api/arcjet/route.ts +++ b/examples/nextjs-14-app-dir-rl/app/api/arcjet/route.ts @@ -7,11 +7,14 @@ const aj = arcjet({ // See: https://nextjs.org/docs/app/building-your-application/configuring/environment-variables key: process.env.ARCJET_KEY!, rules: [ + // Fixed window rate limit. Arcjet also supports sliding window and token + // bucket. fixedWindow({ - mode: "LIVE", - characteristics: ["ip.src"], - window: "1h", - max: 1, + mode: "LIVE", // will block requests. Use "DRY_RUN" to log only + // Limiting by ip.src is the default if not specified + //characteristics: ["ip.src"], + window: "1m", // 1 min fixed window + max: 1, // allow a single request (for demo purposes) }), ], }); diff --git a/examples/nextjs-14-app-dir-rl/app/api/custom_timeout/route.ts b/examples/nextjs-14-app-dir-rl/app/api/custom_timeout/route.ts index 21ac3bf26..494e43890 100644 --- a/examples/nextjs-14-app-dir-rl/app/api/custom_timeout/route.ts +++ b/examples/nextjs-14-app-dir-rl/app/api/custom_timeout/route.ts @@ -17,8 +17,8 @@ const aj = arcjet({ key: process.env.ARCJET_KEY!, rules: [ validateEmail({ - mode: "LIVE", - block: ["NO_MX_RECORDS"], + mode: "LIVE", // will block requests. Use "DRY_RUN" to log only + block: ["NO_MX_RECORDS"], // block email addresses with no MX records }), ], client, diff --git a/examples/nextjs-14-app-dir-rl/environment.d.ts b/examples/nextjs-14-app-dir-rl/environment.d.ts new file mode 100644 index 000000000..9ae2b0722 --- /dev/null +++ b/examples/nextjs-14-app-dir-rl/environment.d.ts @@ -0,0 +1,5 @@ +declare namespace NodeJS { + export interface ProcessEnv { + readonly ARCJET_KEY: string; + } +} \ No newline at end of file diff --git a/examples/nextjs-14-app-dir-rl/package-lock.json b/examples/nextjs-14-app-dir-rl/package-lock.json index b53c3d69c..136a2711e 100644 --- a/examples/nextjs-14-app-dir-rl/package-lock.json +++ b/examples/nextjs-14-app-dir-rl/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "@arcjet/next": "file:../../arcjet-next", - "next": "14.0.4", + "next": "14.1.0", "react": "^18", "react-dom": "^18" }, @@ -27,18 +27,18 @@ }, "../../arcjet-next": { "name": "@arcjet/next", - "version": "1.0.0-alpha.7", + "version": "1.0.0-alpha.8", "license": "Apache-2.0", "dependencies": { - "@arcjet/ip": "1.0.0-alpha.7", + "@arcjet/ip": "1.0.0-alpha.8", "@connectrpc/connect-web": "1.3.0", - "arcjet": "1.0.0-alpha.7", + "arcjet": "1.0.0-alpha.8", "next": "14.1.0" }, "devDependencies": { - "@arcjet/eslint-config": "1.0.0-alpha.7", - "@arcjet/rollup-config": "1.0.0-alpha.7", - "@arcjet/tsconfig": "1.0.0-alpha.7", + "@arcjet/eslint-config": "1.0.0-alpha.8", + "@arcjet/rollup-config": "1.0.0-alpha.8", + "@arcjet/tsconfig": "1.0.0-alpha.8", "@jest/globals": "29.7.0", "@rollup/wasm-node": "4.9.6", "@types/node": "18.18.0", @@ -268,8 +268,9 @@ } }, "node_modules/@next/env": { - "version": "14.0.4", - "license": "MIT" + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz", + "integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==" }, "node_modules/@next/eslint-plugin-next": { "version": "14.1.0", @@ -327,9 +328,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.4.tgz", - "integrity": "sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz", + "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==", "cpu": [ "arm64" ], @@ -342,9 +343,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.4.tgz", - "integrity": "sha512-IZQ3C7Bx0k2rYtrZZxKKiusMTM9WWcK5ajyhOZkYYTCc8xytmwSzR1skU7qLgVT/EY9xtXDG0WhY6fyujnI3rw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz", + "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==", "cpu": [ "x64" ], @@ -357,9 +358,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.4.tgz", - "integrity": "sha512-VwwZKrBQo/MGb1VOrxJ6LrKvbpo7UbROuyMRvQKTFKhNaXjUmKTu7wxVkIuCARAfiI8JpaWAnKR+D6tzpCcM4w==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz", + "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==", "cpu": [ "arm64" ], @@ -372,9 +373,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.4.tgz", - "integrity": "sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz", + "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==", "cpu": [ "arm64" ], @@ -387,9 +388,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.4.tgz", - "integrity": "sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz", + "integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==", "cpu": [ "x64" ], @@ -402,9 +403,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.4.tgz", - "integrity": "sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz", + "integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==", "cpu": [ "x64" ], @@ -417,9 +418,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.4.tgz", - "integrity": "sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz", + "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==", "cpu": [ "arm64" ], @@ -432,9 +433,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.4.tgz", - "integrity": "sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", + "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", "cpu": [ "ia32" ], @@ -447,9 +448,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.4.tgz", - "integrity": "sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz", + "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==", "cpu": [ "x64" ], @@ -2209,11 +2210,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -3073,17 +3069,17 @@ "dev": true }, "node_modules/next": { - "version": "14.0.4", - "license": "MIT", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", + "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==", "dependencies": { - "@next/env": "14.0.4", + "@next/env": "14.1.0", "@swc/helpers": "0.5.2", "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001406", + "caniuse-lite": "^1.0.30001579", "graceful-fs": "^4.2.11", "postcss": "8.4.31", - "styled-jsx": "5.1.1", - "watchpack": "2.4.0" + "styled-jsx": "5.1.1" }, "bin": { "next": "dist/bin/next" @@ -3092,15 +3088,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.0.4", - "@next/swc-darwin-x64": "14.0.4", - "@next/swc-linux-arm64-gnu": "14.0.4", - "@next/swc-linux-arm64-musl": "14.0.4", - "@next/swc-linux-x64-gnu": "14.0.4", - "@next/swc-linux-x64-musl": "14.0.4", - "@next/swc-win32-arm64-msvc": "14.0.4", - "@next/swc-win32-ia32-msvc": "14.0.4", - "@next/swc-win32-x64-msvc": "14.0.4" + "@next/swc-darwin-arm64": "14.1.0", + "@next/swc-darwin-x64": "14.1.0", + "@next/swc-linux-arm64-gnu": "14.1.0", + "@next/swc-linux-arm64-musl": "14.1.0", + "@next/swc-linux-x64-gnu": "14.1.0", + "@next/swc-linux-x64-musl": "14.1.0", + "@next/swc-win32-arm64-msvc": "14.1.0", + "@next/swc-win32-ia32-msvc": "14.1.0", + "@next/swc-win32-x64-msvc": "14.1.0" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -4544,18 +4540,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/examples/nextjs-14-app-dir-rl/package.json b/examples/nextjs-14-app-dir-rl/package.json index 67f557880..412b8412e 100644 --- a/examples/nextjs-14-app-dir-rl/package.json +++ b/examples/nextjs-14-app-dir-rl/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@arcjet/next": "file:../../arcjet-next", - "next": "14.0.4", + "next": "14.1.0", "react": "^18", "react-dom": "^18" }, @@ -25,4 +25,4 @@ "tailwindcss": "^3.4.1", "typescript": "^5" } -} +} \ No newline at end of file diff --git a/examples/nextjs-14-app-dir-validate-email/.env.local.example b/examples/nextjs-14-app-dir-validate-email/.env.local.example new file mode 100644 index 000000000..0c183c7ac --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/.env.local.example @@ -0,0 +1 @@ +ARCJET_KEY= \ No newline at end of file diff --git a/examples/nextjs-14-app-dir-validate-email/README.md b/examples/nextjs-14-app-dir-validate-email/README.md index 827c9c3b7..330f84f6b 100644 --- a/examples/nextjs-14-app-dir-validate-email/README.md +++ b/examples/nextjs-14-app-dir-validate-email/README.md @@ -25,11 +25,7 @@ handler](https://nextjs.org/docs/app/building-your-application/routing/route-han npm ci ``` -3. Add your Arcjet key to `.env.local` - - ```env - ARCJET_KEY= - ``` +3. Rename `.env.local.example` to `.env.local` and add your Arcjet key. 4. Start the dev server. diff --git a/examples/nextjs-14-app-dir-validate-email/app/api/arcjet/route.ts b/examples/nextjs-14-app-dir-validate-email/app/api/arcjet/route.ts index 1e4b27c2f..56a586fec 100644 --- a/examples/nextjs-14-app-dir-validate-email/app/api/arcjet/route.ts +++ b/examples/nextjs-14-app-dir-validate-email/app/api/arcjet/route.ts @@ -5,11 +5,11 @@ const aj = arcjet({ // Get your site key from https://app.arcjet.com // and set it as an environment variable rather than hard coding. // See: https://nextjs.org/docs/app/building-your-application/configuring/environment-variables - key: process.env.ARCJET_KEY!, + key: process.env.ARCJET_KEY, rules: [ validateEmail({ - mode: "LIVE", - block: ["NO_MX_RECORDS"], + mode: "LIVE", // will block requests. Use "DRY_RUN" to log only + block: ["NO_MX_RECORDS"], // block email addresses with no MX records }), ], }); diff --git a/examples/nextjs-14-app-dir-validate-email/environment.d.ts b/examples/nextjs-14-app-dir-validate-email/environment.d.ts new file mode 100644 index 000000000..9ae2b0722 --- /dev/null +++ b/examples/nextjs-14-app-dir-validate-email/environment.d.ts @@ -0,0 +1,5 @@ +declare namespace NodeJS { + export interface ProcessEnv { + readonly ARCJET_KEY: string; + } +} \ No newline at end of file diff --git a/examples/nextjs-14-app-dir-validate-email/package-lock.json b/examples/nextjs-14-app-dir-validate-email/package-lock.json index 1d0417037..f3fd1ae53 100644 --- a/examples/nextjs-14-app-dir-validate-email/package-lock.json +++ b/examples/nextjs-14-app-dir-validate-email/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "@arcjet/next": "file:../../arcjet-next", - "next": "14.0.4", + "next": "14.1.0", "react": "^18", "react-dom": "^18" }, @@ -27,18 +27,18 @@ }, "../../arcjet-next": { "name": "@arcjet/next", - "version": "1.0.0-alpha.7", + "version": "1.0.0-alpha.8", "license": "Apache-2.0", "dependencies": { - "@arcjet/ip": "1.0.0-alpha.7", + "@arcjet/ip": "1.0.0-alpha.8", "@connectrpc/connect-web": "1.3.0", - "arcjet": "1.0.0-alpha.7", + "arcjet": "1.0.0-alpha.8", "next": "14.1.0" }, "devDependencies": { - "@arcjet/eslint-config": "1.0.0-alpha.7", - "@arcjet/rollup-config": "1.0.0-alpha.7", - "@arcjet/tsconfig": "1.0.0-alpha.7", + "@arcjet/eslint-config": "1.0.0-alpha.8", + "@arcjet/rollup-config": "1.0.0-alpha.8", + "@arcjet/tsconfig": "1.0.0-alpha.8", "@jest/globals": "29.7.0", "@rollup/wasm-node": "4.9.6", "@types/node": "18.18.0", @@ -268,9 +268,9 @@ } }, "node_modules/@next/env": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz", - "integrity": "sha512-irQnbMLbUNQpP1wcE5NstJtbuA/69kRfzBrpAD7Gsn8zm/CY6YQYc3HQBz8QPxwISG26tIm5afvvVbu508oBeQ==" + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz", + "integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==" }, "node_modules/@next/eslint-plugin-next": { "version": "14.1.0", @@ -328,9 +328,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.4.tgz", - "integrity": "sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz", + "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==", "cpu": [ "arm64" ], @@ -343,9 +343,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.4.tgz", - "integrity": "sha512-IZQ3C7Bx0k2rYtrZZxKKiusMTM9WWcK5ajyhOZkYYTCc8xytmwSzR1skU7qLgVT/EY9xtXDG0WhY6fyujnI3rw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz", + "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==", "cpu": [ "x64" ], @@ -358,9 +358,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.4.tgz", - "integrity": "sha512-VwwZKrBQo/MGb1VOrxJ6LrKvbpo7UbROuyMRvQKTFKhNaXjUmKTu7wxVkIuCARAfiI8JpaWAnKR+D6tzpCcM4w==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz", + "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==", "cpu": [ "arm64" ], @@ -373,9 +373,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.4.tgz", - "integrity": "sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz", + "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==", "cpu": [ "arm64" ], @@ -388,9 +388,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.4.tgz", - "integrity": "sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz", + "integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==", "cpu": [ "x64" ], @@ -403,9 +403,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.4.tgz", - "integrity": "sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz", + "integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==", "cpu": [ "x64" ], @@ -418,9 +418,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.4.tgz", - "integrity": "sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz", + "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==", "cpu": [ "arm64" ], @@ -433,9 +433,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.4.tgz", - "integrity": "sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", + "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", "cpu": [ "ia32" ], @@ -448,9 +448,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.4.tgz", - "integrity": "sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz", + "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==", "cpu": [ "x64" ], @@ -2210,11 +2210,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -3074,18 +3069,17 @@ "dev": true }, "node_modules/next": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/next/-/next-14.0.4.tgz", - "integrity": "sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", + "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==", "dependencies": { - "@next/env": "14.0.4", + "@next/env": "14.1.0", "@swc/helpers": "0.5.2", "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001406", + "caniuse-lite": "^1.0.30001579", "graceful-fs": "^4.2.11", "postcss": "8.4.31", - "styled-jsx": "5.1.1", - "watchpack": "2.4.0" + "styled-jsx": "5.1.1" }, "bin": { "next": "dist/bin/next" @@ -3094,15 +3088,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.0.4", - "@next/swc-darwin-x64": "14.0.4", - "@next/swc-linux-arm64-gnu": "14.0.4", - "@next/swc-linux-arm64-musl": "14.0.4", - "@next/swc-linux-x64-gnu": "14.0.4", - "@next/swc-linux-x64-musl": "14.0.4", - "@next/swc-win32-arm64-msvc": "14.0.4", - "@next/swc-win32-ia32-msvc": "14.0.4", - "@next/swc-win32-x64-msvc": "14.0.4" + "@next/swc-darwin-arm64": "14.1.0", + "@next/swc-darwin-x64": "14.1.0", + "@next/swc-linux-arm64-gnu": "14.1.0", + "@next/swc-linux-arm64-musl": "14.1.0", + "@next/swc-linux-x64-gnu": "14.1.0", + "@next/swc-linux-x64-musl": "14.1.0", + "@next/swc-win32-arm64-msvc": "14.1.0", + "@next/swc-win32-ia32-msvc": "14.1.0", + "@next/swc-win32-x64-msvc": "14.1.0" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -4546,18 +4540,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/examples/nextjs-14-app-dir-validate-email/package.json b/examples/nextjs-14-app-dir-validate-email/package.json index d151ea583..9b17aa208 100644 --- a/examples/nextjs-14-app-dir-validate-email/package.json +++ b/examples/nextjs-14-app-dir-validate-email/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@arcjet/next": "file:../../arcjet-next", - "next": "14.0.4", + "next": "14.1.0", "react": "^18", "react-dom": "^18" }, @@ -25,4 +25,4 @@ "tailwindcss": "^3.4.1", "typescript": "^5" } -} +} \ No newline at end of file diff --git a/examples/nextjs-14-openai/.env.local.example b/examples/nextjs-14-openai/.env.local.example new file mode 100644 index 000000000..ec23f58cd --- /dev/null +++ b/examples/nextjs-14-openai/.env.local.example @@ -0,0 +1,2 @@ +ARCJET_KEY= +OPENAI_API_KEY= \ No newline at end of file diff --git a/examples/nextjs-14-openai/README.md b/examples/nextjs-14-openai/README.md index e57611dca..5f935a989 100644 --- a/examples/nextjs-14-openai/README.md +++ b/examples/nextjs-14-openai/README.md @@ -8,7 +8,19 @@ # Arcjet rate limit OpenAI chat route with Next.js 14 This example shows how to implement a rate limit on a Next.js 14 API route which -uses the OpenAI chat API. +uses the OpenAI chat API. It uses the [openai-chat-tokens +library](https://github.com/hmarr/openai-chat-tokens) to track the number of +tokens used by a `gpt-3.5-turbo` AI chatbot. + +There are 2 example routes: + +1. `/api/chat` is the default route that tracks the user by IP address. It +applies a limit of 2,000 tokens per hour with a maximum of 5,000 tokens in the +bucket. This allows for a reasonable conversation length without consuming too +many tokens. + +2. `/api/chat_userid` is a route that tracks the user by a unique identifier. + You could use this to track a quota per authenticated user. ## How to use @@ -25,12 +37,8 @@ uses the OpenAI chat API. npm ci ``` -3. Add your Arcjet & OpenAI keys to `.env.local` - - ```env - ARCJET_KEY= - OPENAI_API_KEY= - ``` +3. Rename `.env.local.example` to `.env.local` and add your Arcjet & OpenAI + keys. 4. Start the dev server. diff --git a/examples/nextjs-14-openai/app/api/chat/route.ts b/examples/nextjs-14-openai/app/api/chat/route.ts index 77c649542..823d009f5 100644 --- a/examples/nextjs-14-openai/app/api/chat/route.ts +++ b/examples/nextjs-14-openai/app/api/chat/route.ts @@ -1,43 +1,60 @@ -// This example is adapted from https://sdk.vercel.ai/docs/guides/frameworks/nextjs-app +/* + If you are building an AI application you may be more interested in the number + of AI tokens rather than the number of HTTP requests. The token bucket + algorithm is a good fit for this use case because you can vary the number of + tokens withdrawn from the bucket with every request. + + This example is adapted from + https://sdk.vercel.ai/docs/guides/frameworks/nextjs-app and calculates as + estimate of the number of tokens required to process the request. It then uses + a token bucket rate limit algorithm to limit the number of tokens consumed, + keeping costs under control. + + The IP address is used to track tokens used, which could be used to provide a + free service to users with a limited number of tokens. Arcjet also supports + adding custom characteristics so you could use a user ID to track authenticated + users instead. See the `chat_userid` example for an example of this. +*/ import arcjet, { tokenBucket } from "@arcjet/next"; import { OpenAIStream, StreamingTextResponse } from "ai"; import OpenAI from "openai"; import { promptTokensEstimate } from "openai-chat-tokens"; -// Arcjet rate limit rule const aj = arcjet({ // Get your site key from https://app.arcjet.com // and set it as an environment variable rather than hard coding. // See: https://nextjs.org/docs/app/building-your-application/configuring/environment-variables - key: process.env.ARCJET_KEY!, + key: process.env.ARCJET_KEY, rules: [ tokenBucket({ - mode: "LIVE", - characteristics: ["ip.src"], - refillRate: 40_000, - interval: "1d", - capacity: 40_000, + mode: "LIVE", // will block requests. Use "DRY_RUN" to log only + characteristics: ["ip.src"], // track requests by IP address + refillRate: 2_000, // fill the bucket up by 2,000 tokens + interval: "1h", // every hour + capacity: 5_000, // up to 5,000 tokens }), ], }); // OpenAI client const openai = new OpenAI({ - apiKey: process.env["OPENAI_API_KEY"] ?? "OPENAI_KEY_MISSING", + apiKey: process.env.OPENAI_API_KEY ?? "OPENAI_KEY_MISSING", }); +// Edge runtime allows for streaming responses export const runtime = "edge"; export async function POST(req: Request) { const { messages } = await req.json(); + // Estimate the number of tokens required to process the request const estimate = promptTokensEstimate({ messages, }); console.log("Token estimate", estimate); - // Protect the route with Arcjet + // Withdraw tokens from the token bucket const decision = await aj.protect(req, { requested: estimate }); console.log("Arcjet decision", decision.conclusion); diff --git a/examples/nextjs-14-openai/app/api/chat_userid/route.ts b/examples/nextjs-14-openai/app/api/chat_userid/route.ts new file mode 100644 index 000000000..72bcf924d --- /dev/null +++ b/examples/nextjs-14-openai/app/api/chat_userid/route.ts @@ -0,0 +1,95 @@ +/* + If you are building an AI application you may be more interested in the number + of AI tokens rather than the number of HTTP requests. The token bucket + algorithm is a good fit for this use case because you can vary the number of + tokens withdrawn from the bucket with every request. + + This example is adapted from + https://sdk.vercel.ai/docs/guides/frameworks/nextjs-app and calculates as + estimate of the number of tokens required to process the request. It then uses + a token bucket rate limit algorithm to limit the number of tokens consumed, + keeping costs under control. + + A custom user identifier is used to track tokens used by a user regardless of + which device or IP they are using. This can be used to apply a quota for each + user. Custom characteristics are defined with a string key when configuring the + rate limit rule. The value is then passed as a string, number or boolean when + calling the protect method. You can use any string value for the key. +*/ +import arcjet, { tokenBucket } from "@arcjet/next"; +import { OpenAIStream, StreamingTextResponse } from "ai"; +import OpenAI from "openai"; +import { promptTokensEstimate } from "openai-chat-tokens"; + +const aj = arcjet({ + // Get your site key from https://app.arcjet.com + // and set it as an environment variable rather than hard coding. + // See: https://nextjs.org/docs/app/building-your-application/configuring/environment-variables + key: process.env.ARCJET_KEY, + rules: [ + tokenBucket({ + mode: "LIVE", // will block requests. Use "DRY_RUN" to log only + characteristics: ["userId"], // track requests by user ID + refillRate: 2_000, // fill the bucket up by 2,000 tokens + interval: "1h", // every hour + capacity: 5_000, // up to 5,000 tokens + }), + ], +}); + +// OpenAI client +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY ?? "OPENAI_KEY_MISSING", +}); + +// Edge runtime allows for streaming responses +export const runtime = "edge"; + +export async function POST(req: Request) { + // This userId is hard coded for the example, but this is where you would do a + // session lookup and get the user ID. + const userId = "totoro" + + const { messages } = await req.json(); + + // Estimate the number of tokens required to process the request + const estimate = promptTokensEstimate({ + messages, + }); + + console.log("Token estimate", estimate); + + // Withdraw tokens from the token bucket + const decision = await aj.protect(req, { requested: estimate, userId }); + console.log("Arcjet decision", decision.conclusion); + + if (decision.reason.isRateLimit()) { + console.log("Requests remaining", decision.reason.remaining); + } + + // If the request is denied, return a 429 + if (decision.isDenied()) { + if (decision.reason.isRateLimit()) { + return new Response("Too Many Requests", { + status: 429, + }); + } else { + return new Response("Forbidden", { + status: 403, + }); + } + } + + // If the request is allowed, continue to use OpenAI + // Ask OpenAI for a streaming chat completion given the prompt + const response = await openai.chat.completions.create({ + model: "gpt-3.5-turbo", + stream: true, + messages, + }); + + // Convert the response into a friendly text-stream + const stream = OpenAIStream(response); + // Respond with the stream + return new StreamingTextResponse(stream); +} \ No newline at end of file diff --git a/examples/nextjs-14-openai/environment.d.ts b/examples/nextjs-14-openai/environment.d.ts new file mode 100644 index 000000000..ee5ec3330 --- /dev/null +++ b/examples/nextjs-14-openai/environment.d.ts @@ -0,0 +1,6 @@ +declare namespace NodeJS { + export interface ProcessEnv { + readonly ARCJET_KEY: string; + readonly OPENAI_API_KEY: string; + } +} \ No newline at end of file diff --git a/examples/nextjs-14-openai/package-lock.json b/examples/nextjs-14-openai/package-lock.json index 1034e2276..ff787f802 100644 --- a/examples/nextjs-14-openai/package-lock.json +++ b/examples/nextjs-14-openai/package-lock.json @@ -1,16 +1,16 @@ { - "name": "my-ai-app", + "name": "nextjs-14-openai", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "my-ai-app", + "name": "nextjs-14-openai", "version": "0.1.0", "dependencies": { "@arcjet/next": "file:../../arcjet-next", - "ai": "^2.2.33", - "next": "14.0.4", + "ai": "^2.2.34", + "next": "14.1.0", "openai": "^4.27.0", "openai-chat-tokens": "^0.2.8", "react": "^18", @@ -30,18 +30,18 @@ }, "../../arcjet-next": { "name": "@arcjet/next", - "version": "1.0.0-alpha.7", + "version": "1.0.0-alpha.8", "license": "Apache-2.0", "dependencies": { - "@arcjet/ip": "1.0.0-alpha.7", + "@arcjet/ip": "1.0.0-alpha.8", "@connectrpc/connect-web": "1.3.0", - "arcjet": "1.0.0-alpha.7", + "arcjet": "1.0.0-alpha.8", "next": "14.1.0" }, "devDependencies": { - "@arcjet/eslint-config": "1.0.0-alpha.7", - "@arcjet/rollup-config": "1.0.0-alpha.7", - "@arcjet/tsconfig": "1.0.0-alpha.7", + "@arcjet/eslint-config": "1.0.0-alpha.8", + "@arcjet/rollup-config": "1.0.0-alpha.8", + "@arcjet/tsconfig": "1.0.0-alpha.8", "@jest/globals": "29.7.0", "@rollup/wasm-node": "4.9.6", "@types/node": "18.18.0", @@ -291,9 +291,9 @@ } }, "node_modules/@next/env": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz", - "integrity": "sha512-irQnbMLbUNQpP1wcE5NstJtbuA/69kRfzBrpAD7Gsn8zm/CY6YQYc3HQBz8QPxwISG26tIm5afvvVbu508oBeQ==" + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz", + "integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==" }, "node_modules/@next/eslint-plugin-next": { "version": "14.1.0", @@ -351,9 +351,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.4.tgz", - "integrity": "sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz", + "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==", "cpu": [ "arm64" ], @@ -366,9 +366,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.4.tgz", - "integrity": "sha512-IZQ3C7Bx0k2rYtrZZxKKiusMTM9WWcK5ajyhOZkYYTCc8xytmwSzR1skU7qLgVT/EY9xtXDG0WhY6fyujnI3rw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz", + "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==", "cpu": [ "x64" ], @@ -381,9 +381,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.4.tgz", - "integrity": "sha512-VwwZKrBQo/MGb1VOrxJ6LrKvbpo7UbROuyMRvQKTFKhNaXjUmKTu7wxVkIuCARAfiI8JpaWAnKR+D6tzpCcM4w==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz", + "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==", "cpu": [ "arm64" ], @@ -396,9 +396,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.4.tgz", - "integrity": "sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz", + "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==", "cpu": [ "arm64" ], @@ -411,9 +411,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.4.tgz", - "integrity": "sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz", + "integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==", "cpu": [ "x64" ], @@ -426,9 +426,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.4.tgz", - "integrity": "sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz", + "integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==", "cpu": [ "x64" ], @@ -441,9 +441,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.4.tgz", - "integrity": "sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz", + "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==", "cpu": [ "arm64" ], @@ -456,9 +456,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.4.tgz", - "integrity": "sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", + "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", "cpu": [ "ia32" ], @@ -471,9 +471,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.4.tgz", - "integrity": "sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz", + "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==", "cpu": [ "x64" ], @@ -886,9 +886,9 @@ } }, "node_modules/ai": { - "version": "2.2.33", - "resolved": "https://registry.npmjs.org/ai/-/ai-2.2.33.tgz", - "integrity": "sha512-y9iMgt/RjFZCrjx5NuC+tdZqvunM9Bo1ufuC1BpgyjPmmE2RYduM+3Whjez0fu808KkwTQvvhUhhC5NkAy8/9g==", + "version": "2.2.34", + "resolved": "https://registry.npmjs.org/ai/-/ai-2.2.34.tgz", + "integrity": "sha512-5U/Q9Kkc24vfgq+NpwgbXnS5wgk++ljx1sAyfOFdG2byOR57gkX8wK1gVwiz0CaoZzVj62IqdmnQBoSCAIsbCA==", "dependencies": { "eventsource-parser": "1.0.0", "nanoid": "3.3.6", @@ -2594,11 +2594,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -3540,18 +3535,17 @@ "dev": true }, "node_modules/next": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/next/-/next-14.0.4.tgz", - "integrity": "sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", + "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==", "dependencies": { - "@next/env": "14.0.4", + "@next/env": "14.1.0", "@swc/helpers": "0.5.2", "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001406", + "caniuse-lite": "^1.0.30001579", "graceful-fs": "^4.2.11", "postcss": "8.4.31", - "styled-jsx": "5.1.1", - "watchpack": "2.4.0" + "styled-jsx": "5.1.1" }, "bin": { "next": "dist/bin/next" @@ -3560,15 +3554,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.0.4", - "@next/swc-darwin-x64": "14.0.4", - "@next/swc-linux-arm64-gnu": "14.0.4", - "@next/swc-linux-arm64-musl": "14.0.4", - "@next/swc-linux-x64-gnu": "14.0.4", - "@next/swc-linux-x64-musl": "14.0.4", - "@next/swc-win32-arm64-msvc": "14.0.4", - "@next/swc-win32-ia32-msvc": "14.0.4", - "@next/swc-win32-x64-msvc": "14.0.4" + "@next/swc-darwin-arm64": "14.1.0", + "@next/swc-darwin-x64": "14.1.0", + "@next/swc-linux-arm64-gnu": "14.1.0", + "@next/swc-linux-arm64-musl": "14.1.0", + "@next/swc-linux-x64-gnu": "14.1.0", + "@next/swc-linux-x64-musl": "14.1.0", + "@next/swc-win32-arm64-msvc": "14.1.0", + "@next/swc-win32-ia32-msvc": "14.1.0", + "@next/swc-win32-x64-msvc": "14.1.0" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -5271,18 +5265,6 @@ } } }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/web-streams-polyfill": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", diff --git a/examples/nextjs-14-openai/package.json b/examples/nextjs-14-openai/package.json index 1a3915494..1d1c22243 100644 --- a/examples/nextjs-14-openai/package.json +++ b/examples/nextjs-14-openai/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@arcjet/next": "file:../../arcjet-next", - "ai": "^2.2.33", - "next": "14.0.4", + "ai": "^2.2.34", + "next": "14.1.0", "openai": "^4.27.0", "openai-chat-tokens": "^0.2.8", "react": "^18", @@ -28,4 +28,4 @@ "tailwindcss": "^3.4.1", "typescript": "^5" } -} +} \ No newline at end of file diff --git a/examples/nextjs-14-pages-wrap/.env.local.example b/examples/nextjs-14-pages-wrap/.env.local.example new file mode 100644 index 000000000..0c183c7ac --- /dev/null +++ b/examples/nextjs-14-pages-wrap/.env.local.example @@ -0,0 +1 @@ +ARCJET_KEY= \ No newline at end of file diff --git a/examples/nextjs-14-pages-wrap/README.md b/examples/nextjs-14-pages-wrap/README.md index b55f13dcc..cf375ddea 100644 --- a/examples/nextjs-14-pages-wrap/README.md +++ b/examples/nextjs-14-pages-wrap/README.md @@ -25,11 +25,7 @@ Route](https://nextjs.org/docs/pages/building-your-application/routing/api-route npm ci ``` -3. Add your Arcjet key to `.env.local` - - ```env - ARCJET_KEY= - ``` +3. Rename `.env.local.example` to `.env.local` and add your Arcjet key. 4. Start the dev server. diff --git a/examples/nextjs-14-pages-wrap/environment.d.ts b/examples/nextjs-14-pages-wrap/environment.d.ts new file mode 100644 index 000000000..9ae2b0722 --- /dev/null +++ b/examples/nextjs-14-pages-wrap/environment.d.ts @@ -0,0 +1,5 @@ +declare namespace NodeJS { + export interface ProcessEnv { + readonly ARCJET_KEY: string; + } +} \ No newline at end of file diff --git a/examples/nextjs-14-pages-wrap/package-lock.json b/examples/nextjs-14-pages-wrap/package-lock.json index 7ebf9157b..2ab1a8bf0 100644 --- a/examples/nextjs-14-pages-wrap/package-lock.json +++ b/examples/nextjs-14-pages-wrap/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "@arcjet/next": "file:../../arcjet-next", - "next": "14.0.4", + "next": "14.1.0", "react": "^18", "react-dom": "^18" }, @@ -27,18 +27,18 @@ }, "../../arcjet-next": { "name": "@arcjet/next", - "version": "1.0.0-alpha.7", + "version": "1.0.0-alpha.8", "license": "Apache-2.0", "dependencies": { - "@arcjet/ip": "1.0.0-alpha.7", + "@arcjet/ip": "1.0.0-alpha.8", "@connectrpc/connect-web": "1.3.0", - "arcjet": "1.0.0-alpha.7", + "arcjet": "1.0.0-alpha.8", "next": "14.1.0" }, "devDependencies": { - "@arcjet/eslint-config": "1.0.0-alpha.7", - "@arcjet/rollup-config": "1.0.0-alpha.7", - "@arcjet/tsconfig": "1.0.0-alpha.7", + "@arcjet/eslint-config": "1.0.0-alpha.8", + "@arcjet/rollup-config": "1.0.0-alpha.8", + "@arcjet/tsconfig": "1.0.0-alpha.8", "@jest/globals": "29.7.0", "@rollup/wasm-node": "4.9.6", "@types/node": "18.18.0", @@ -268,9 +268,9 @@ } }, "node_modules/@next/env": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz", - "integrity": "sha512-irQnbMLbUNQpP1wcE5NstJtbuA/69kRfzBrpAD7Gsn8zm/CY6YQYc3HQBz8QPxwISG26tIm5afvvVbu508oBeQ==" + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz", + "integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==" }, "node_modules/@next/eslint-plugin-next": { "version": "14.1.0", @@ -328,9 +328,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.4.tgz", - "integrity": "sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz", + "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==", "cpu": [ "arm64" ], @@ -343,9 +343,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.4.tgz", - "integrity": "sha512-IZQ3C7Bx0k2rYtrZZxKKiusMTM9WWcK5ajyhOZkYYTCc8xytmwSzR1skU7qLgVT/EY9xtXDG0WhY6fyujnI3rw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz", + "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==", "cpu": [ "x64" ], @@ -358,9 +358,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.4.tgz", - "integrity": "sha512-VwwZKrBQo/MGb1VOrxJ6LrKvbpo7UbROuyMRvQKTFKhNaXjUmKTu7wxVkIuCARAfiI8JpaWAnKR+D6tzpCcM4w==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz", + "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==", "cpu": [ "arm64" ], @@ -373,9 +373,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.4.tgz", - "integrity": "sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz", + "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==", "cpu": [ "arm64" ], @@ -388,9 +388,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.4.tgz", - "integrity": "sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz", + "integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==", "cpu": [ "x64" ], @@ -403,9 +403,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.4.tgz", - "integrity": "sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz", + "integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==", "cpu": [ "x64" ], @@ -418,9 +418,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.4.tgz", - "integrity": "sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz", + "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==", "cpu": [ "arm64" ], @@ -433,9 +433,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.4.tgz", - "integrity": "sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", + "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", "cpu": [ "ia32" ], @@ -448,9 +448,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.4.tgz", - "integrity": "sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz", + "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==", "cpu": [ "x64" ], @@ -2210,11 +2210,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -3074,18 +3069,17 @@ "dev": true }, "node_modules/next": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/next/-/next-14.0.4.tgz", - "integrity": "sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", + "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==", "dependencies": { - "@next/env": "14.0.4", + "@next/env": "14.1.0", "@swc/helpers": "0.5.2", "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001406", + "caniuse-lite": "^1.0.30001579", "graceful-fs": "^4.2.11", "postcss": "8.4.31", - "styled-jsx": "5.1.1", - "watchpack": "2.4.0" + "styled-jsx": "5.1.1" }, "bin": { "next": "dist/bin/next" @@ -3094,15 +3088,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.0.4", - "@next/swc-darwin-x64": "14.0.4", - "@next/swc-linux-arm64-gnu": "14.0.4", - "@next/swc-linux-arm64-musl": "14.0.4", - "@next/swc-linux-x64-gnu": "14.0.4", - "@next/swc-linux-x64-musl": "14.0.4", - "@next/swc-win32-arm64-msvc": "14.0.4", - "@next/swc-win32-ia32-msvc": "14.0.4", - "@next/swc-win32-x64-msvc": "14.0.4" + "@next/swc-darwin-arm64": "14.1.0", + "@next/swc-darwin-x64": "14.1.0", + "@next/swc-linux-arm64-gnu": "14.1.0", + "@next/swc-linux-arm64-musl": "14.1.0", + "@next/swc-linux-x64-gnu": "14.1.0", + "@next/swc-linux-x64-musl": "14.1.0", + "@next/swc-win32-arm64-msvc": "14.1.0", + "@next/swc-win32-ia32-msvc": "14.1.0", + "@next/swc-win32-x64-msvc": "14.1.0" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -4546,18 +4540,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/examples/nextjs-14-pages-wrap/package.json b/examples/nextjs-14-pages-wrap/package.json index 1dca4effc..e2af2c990 100644 --- a/examples/nextjs-14-pages-wrap/package.json +++ b/examples/nextjs-14-pages-wrap/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@arcjet/next": "file:../../arcjet-next", - "next": "14.0.4", + "next": "14.1.0", "react": "^18", "react-dom": "^18" }, @@ -25,4 +25,4 @@ "tailwindcss": "^3.4.1", "typescript": "^5" } -} +} \ No newline at end of file diff --git a/examples/nextjs-14-pages-wrap/pages/api/arcjet-edge.ts b/examples/nextjs-14-pages-wrap/pages/api/arcjet-edge.ts index bafb9abb6..121e953ba 100644 --- a/examples/nextjs-14-pages-wrap/pages/api/arcjet-edge.ts +++ b/examples/nextjs-14-pages-wrap/pages/api/arcjet-edge.ts @@ -12,12 +12,14 @@ const aj = arcjet({ // See: https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables key: process.env.ARCJET_KEY!, rules: [ + // Fixed window rate limit. Arcjet also supports sliding window and token + // bucket. fixedWindow({ - mode: "LIVE", + mode: "LIVE", // will block requests. Use "DRY_RUN" to log only // Limiting by ip.src is the default if not specified //characteristics: ["ip.src"], - window: "1m", - max: 1, + window: "1m", // 1 min fixed window + max: 1, // allow a single request (for demo purposes) }), ], }); diff --git a/examples/nextjs-14-pages-wrap/pages/api/arcjet.ts b/examples/nextjs-14-pages-wrap/pages/api/arcjet.ts index fe667c1b2..36f06826a 100644 --- a/examples/nextjs-14-pages-wrap/pages/api/arcjet.ts +++ b/examples/nextjs-14-pages-wrap/pages/api/arcjet.ts @@ -8,12 +8,14 @@ const aj = arcjet({ // See: https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables key: process.env.ARCJET_KEY!, rules: [ + // Fixed window rate limit. Arcjet also supports sliding window and token + // bucket. fixedWindow({ - mode: "LIVE", + mode: "LIVE", // will block requests. Use "DRY_RUN" to log only // Limiting by ip.src is the default if not specified //characteristics: ["ip.src"], - window: "1m", - max: 1, + window: "1m", // 1 min fixed window + max: 1, // allow a single request (for demo purposes) }), ], }); diff --git a/examples/nextjs-example/.env.local.example b/examples/nextjs-example/.env.local.example new file mode 100644 index 000000000..0c183c7ac --- /dev/null +++ b/examples/nextjs-example/.env.local.example @@ -0,0 +1 @@ +ARCJET_KEY= \ No newline at end of file diff --git a/examples/nextjs-example/.gitignore b/examples/nextjs-example/.gitignore new file mode 100644 index 000000000..f36d623f3 --- /dev/null +++ b/examples/nextjs-example/.gitignore @@ -0,0 +1,37 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz +package-lock.json + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts \ No newline at end of file diff --git a/examples/nextjs-example/README.md b/examples/nextjs-example/README.md new file mode 100644 index 000000000..ac4f82589 --- /dev/null +++ b/examples/nextjs-example/README.md @@ -0,0 +1,46 @@ +# Arcjet Rate Limiting Example + +‼️ This example is [for the Next.js +repo](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md). + +This example uses the [Arcjet](https://arcjet.com/) SDK to implement a token +bucket rate limit for API routes. It does not require any additional +infrastructure e.g. Redis. + +The Arcjet SDK allows you to Implement rate limiting, bot protection, email +verification & defend against common attacks. See [the +docs](https://docs.arcjet.com/) for details. + +## Deploy your own + +Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/next.js/tree/canary/examples/arcjet-rate-limit&project-name=arcjet-rate-limit&repository-name=arcjet-rate-limit) + +## How to use + +Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), [pnpm](https://pnpm.io), or [Bun](https://bun.sh/docs/cli/bunx) to bootstrap the example: + +```bash +npx create-next-app --example arcjet-rate-limit arcjet-rate-limit-app +``` + +```bash +yarn create next-app --example arcjet-rate-limit arcjet-rate-limit-app +``` + +```bash +pnpm create next-app --example arcjet-rate-limit arcjet-rate-limit-app +``` + +```bash +bunx create-next-app --example arcjet-rate-limit arcjet-rate-limit-app +``` + +To run the example you need to: + +1. [Create a free Arcjet account](https://app.arcjet.com/) then follow the + instructions to add a site and get a key. +2. Rename `.env.local.example` to `.env.local` and add your Arcjet key. + +Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). diff --git a/examples/nextjs-example/app/api/arcjet/route.ts b/examples/nextjs-example/app/api/arcjet/route.ts new file mode 100644 index 000000000..d2a345519 --- /dev/null +++ b/examples/nextjs-example/app/api/arcjet/route.ts @@ -0,0 +1,48 @@ +import arcjet, { tokenBucket } from "@arcjet/next"; +import { NextResponse } from "next/server"; + +const aj = arcjet({ + // Get your key from https://app.arcjet.com and set it as an environment + // variable. + key: process.env.ARCJET_KEY, + // Arcjet supports multiple rules including bot protection and email + // validation. See https://docs.arcjet.com + rules: [ + // Create a token bucket rate limit. Fixed and sliding window rate limits + // are also supported. See https://docs.arcjet.com/rate-limiting/algorithms + tokenBucket({ + mode: "LIVE", // will block requests. Use "DRY_RUN" to log only + // Use one of the built in characteristics e.g. ip.src, or create a custom + // one. Any string is valid - here we use userId. See + // https://docs.arcjet.com/rate-limiting/configuration#characteristics + characteristics: ["userId"], // track requests by user ID + refillRate: 100, // refill 100 tokens per interval + interval: "1m", // 1 min interval + capacity: 200, // bucket maximum capacity of 200 tokens + }), + ], +}); + +export async function GET(req: Request) { + // The rate limit will be applied by userId and 50 tokens will be withdrawn + // per request. Based on the rules above, 2 requests will consume all 100 + // tokens in the bucket. The bucket will then refill by 100 tokens every + // minute to a maximum of 200. Set the user ID and requested tokens + // dynamically e.g. based on the authenticated user and the "cost" of a + // request e.g. AI tokens consumed. + const decision = await aj.protect(req, { userId: "user1", requested: 50 }); + + if (decision.isDenied()) { + return NextResponse.json( + { + error: "Too Many Requests", + reason: decision.reason, + }, + { + status: 429, + } + ); + } + + return NextResponse.json({ message: "Hello World" }); +} \ No newline at end of file diff --git a/examples/nextjs-example/app/layout.tsx b/examples/nextjs-example/app/layout.tsx new file mode 100644 index 000000000..a14e64fcd --- /dev/null +++ b/examples/nextjs-example/app/layout.tsx @@ -0,0 +1,16 @@ +export const metadata = { + title: 'Next.js', + description: 'Generated by Next.js', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/examples/nextjs-example/app/page.tsx b/examples/nextjs-example/app/page.tsx new file mode 100644 index 000000000..57a3a822c --- /dev/null +++ b/examples/nextjs-example/app/page.tsx @@ -0,0 +1,8 @@ +export default function Index() { + return ( +

+ To test the rate limit, browse to /api/arcjet{" "} + and refresh the page a few times. +

+ ); +} \ No newline at end of file diff --git a/examples/nextjs-example/environment.d.ts b/examples/nextjs-example/environment.d.ts new file mode 100644 index 000000000..9ae2b0722 --- /dev/null +++ b/examples/nextjs-example/environment.d.ts @@ -0,0 +1,5 @@ +declare namespace NodeJS { + export interface ProcessEnv { + readonly ARCJET_KEY: string; + } +} \ No newline at end of file diff --git a/examples/nextjs-example/next.config.mjs b/examples/nextjs-example/next.config.mjs new file mode 100644 index 000000000..4678774e6 --- /dev/null +++ b/examples/nextjs-example/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/examples/nextjs-example/package.json b/examples/nextjs-example/package.json new file mode 100644 index 000000000..6b12fc7d0 --- /dev/null +++ b/examples/nextjs-example/package.json @@ -0,0 +1,23 @@ +{ + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@arcjet/next": "^1.0.0-alpha.8", + "next": "latest", + "react": "^18", + "react-dom": "^18" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "eslint": "^8", + "eslint-config-next": "14.1.0", + "typescript": "^5" + } +} \ No newline at end of file diff --git a/examples/nextjs-example/tsconfig.json b/examples/nextjs-example/tsconfig.json new file mode 100644 index 000000000..e7ff90fd2 --- /dev/null +++ b/examples/nextjs-example/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +}