From 6b066ac8ee9fbf05393c74889418f0d49578ec01 Mon Sep 17 00:00:00 2001 From: Henry Barrow Date: Fri, 2 Jun 2023 14:40:24 +0100 Subject: [PATCH 01/13] Update Vercel example --- packages/sdk/vercel/example-vercel/.gitignore | 147 ++++++++++++++---- packages/sdk/vercel/example-vercel/README.md | 44 ++++-- .../sdk/vercel/example-vercel/next-env.d.ts | 5 + .../sdk/vercel/example-vercel/package.json | 12 +- .../example-vercel/src/app/api/hello/route.ts | 52 ++++--- .../vercel/example-vercel/src/app/page.tsx | 19 ++- 6 files changed, 203 insertions(+), 76 deletions(-) create mode 100644 packages/sdk/vercel/example-vercel/next-env.d.ts diff --git a/packages/sdk/vercel/example-vercel/.gitignore b/packages/sdk/vercel/example-vercel/.gitignore index 983df7c7d3..caf544da4e 100644 --- a/packages/sdk/vercel/example-vercel/.gitignore +++ b/packages/sdk/vercel/example-vercel/.gitignore @@ -1,36 +1,131 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* -# dependencies -/node_modules -/.pnp -.pnp.js +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json -# testing -/coverage +# Runtime data +pids +*.pid +*.seed +*.pid.lock -# next.js -/.next/ -/out/ +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov -# production -/build +# Coverage directory used by tools like istanbul +coverage +*.lcov -# misc -.DS_Store -*.pem +# nyc test coverage +.nyc_output -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt -# local env files -.env*.local +# Bower dependency directory (https://bower.io/) +bower_components -# vercel -.vercel +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release -# typescript +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache *.tsbuildinfo -next-env.d.ts -.vscode + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* +.vercel diff --git a/packages/sdk/vercel/example-vercel/README.md b/packages/sdk/vercel/example-vercel/README.md index 5a60fc48eb..52bf2f6a32 100644 --- a/packages/sdk/vercel/example-vercel/README.md +++ b/packages/sdk/vercel/example-vercel/README.md @@ -1,30 +1,54 @@ # Example test app for Vercel LaunchDarkly SDK This is an example test app to showcase the usage of the Vercel LaunchDarkly -SDK. This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). +SDK to evaluate a feature flag in a [Route Handler](https://nextjs.org/docs/app/building-your-application/routing/router-handlers) using [Vercel's edge runtime](https://nextjs.org/docs/app/building-your-application/routing/router-handlers#edge-and-nodejs-runtimes). This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +Most of the LaunchDarkly-related code can be found in [src/app/api/hello/route.ts](src/app/api/hello/route.ts). ## Prerequisites A node environment of version 16 and yarn are required to develop in this repository. -You will also need the vercel cli installed and a Vercel account to setup -the test data required by this example. See the [Vercel docs](https://vercel.com/docs/storage/edge-config/get-started) on how to setup your edge config store. +You will also need the [Vercel CLI](https://vercel.com/docs/cli) installed and a Vercel account to setup +the test data required by this example. See the [Vercel docs](https://vercel.com/docs/storage/edge-config/get-started) on how to setup your Edge Config store. + +## Setting up your LaunchDarkly environment + +For simplicity, we recommend [creating a new LaunchDarkly project](https://docs.launchdarkly.com/home/organize/projects/?q=create+proj) for this example app. After creating a new project, create the following feature flags with Client-side SDK availability: -## Usage +- `test-flag` - (Boolean) - This flag is evaluated in [src/app/api/hello/route.ts](src/app/api/hello/route.ts). -1. Follow the [Vercel docs](https://vercel.com/docs/storage/edge-config/get-started) to insert [testData.json](https://github.com/launchdarkly/js-core/blob/main/packages/sdk/vercel/testData.json) to your edge config store. +After creating your project, You will need to have the LaunchDarkly Vercel integration configured to push feature flag data to your Vercel Edge Config. Read [Vercel](https://docs.launchdarkly.com/integrations/vercel/) to set up the integration. Be sure to connect the **Test** environment for project you created above. -2. After completing the guide above, you should have linked this example app to your Vercel project and created an `.env.development.local`. +## Setting up your development environment + +1. Copy this directory in a new repository. +2. Create a new Vercel project based on the new repository. +3. [Add a new environment variable to your project](https://vercel.com/docs/concepts/projects/environment-variables) named `LD_CLIENT_SIDE_ID` and set it to the LaunchDarkly client-side ID for the **Test** environment in the project you created above. +4. Follow [Vercel's documentation](https://vercel.com/docs/storage/edge-config/get-started) to connect an Edge Config to your new project. +5. Run the following command to link your local codebase to your Vercel project: + +```shell +vercel link +``` + +6. Run the following command to sync your projects environment variables in your development environment: + +```shell +vercel env pull .env.development.local +``` -3. At the root of the js-core repo: +7. After completing the guide above, you should have linked this example app to your Vercel project and created an `.env.development.local`. +8. Verify the contents of `.env.development.local` have values for the `LD_CLIENT_SIDE_ID` and `EDGE_CONFIG`. +9. Run the following command to install all dependencies: ```shell -yarn && yarn build +yarn ``` -4. Then back in this example folder: +10. Run the following command to start your development environment: ```shell yarn dev ``` -5. Open [http://localhost:3000/api/hello](http://localhost:3000/api/hello). +11. Open [http://localhost:3000/api/hello](http://localhost:3000/api/hello). diff --git a/packages/sdk/vercel/example-vercel/next-env.d.ts b/packages/sdk/vercel/example-vercel/next-env.d.ts new file mode 100644 index 0000000000..4f11a03dc6 --- /dev/null +++ b/packages/sdk/vercel/example-vercel/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/packages/sdk/vercel/example-vercel/package.json b/packages/sdk/vercel/example-vercel/package.json index 627f6bd73a..e88d520997 100644 --- a/packages/sdk/vercel/example-vercel/package.json +++ b/packages/sdk/vercel/example-vercel/package.json @@ -4,21 +4,21 @@ "packageManager": "yarn@3.4.1", "scripts": { "dev": "next dev", - "build": "tsc", + "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "@launchdarkly/vercel-server-sdk": "0.3.0", - "@vercel/edge-config": "^0.1.8", - "next": "13.3.1", - "react": "18.2.0", + "@vercel/edge-config": "^0.2.1", + "next": "^13.4.4", + "react": "^18.2.0", "react-dom": "18.2.0" }, "devDependencies": { "@types/node": "18.16.2", - "@types/react": "18.2.0", - "@types/react-dom": "18.2.1", + "@types/react": "^18.2.7", + "@types/react-dom": "^18.2.4", "autoprefixer": "10.4.14", "postcss": "8.4.23", "tailwindcss": "3.3.2", diff --git a/packages/sdk/vercel/example-vercel/src/app/api/hello/route.ts b/packages/sdk/vercel/example-vercel/src/app/api/hello/route.ts index 391d75db28..ebd718b498 100644 --- a/packages/sdk/vercel/example-vercel/src/app/api/hello/route.ts +++ b/packages/sdk/vercel/example-vercel/src/app/api/hello/route.ts @@ -1,37 +1,41 @@ -import { NextResponse } from 'next/server'; -import { createClient } from '@vercel/edge-config'; -import { init as initLD } from '@launchdarkly/vercel-server-sdk'; +import { NextResponse } from "next/server"; +import { createClient } from "@vercel/edge-config"; +import { init as initLD } from "@launchdarkly/vercel-server-sdk"; -export const config = { - runtime: 'edge', -}; +export const runtime = "edge"; export async function GET() { - const sdkKey = 'test-sdk-key'; - const flagKey = 'testFlag1'; + const clientSideID = process.env.LD_CLIENT_SIDE_ID; + if (!clientSideID) { + return NextResponse.json( + { + error: "LD_CLIENT_SIDE_ID environment variable is missing.", + }, + { status: 500 } + ); + } + const flagKey = "test-flag"; - // default fallthrough evaluates to true - const contextFallthrough = { kind: 'user', key: 'test-user-key-1' }; - - // matches email targeting rule evaluates to false - const contextEmailRule = { kind: 'user', key: 'test-user-key-1', email: 'test@gmail.com' }; + // This is just a simple example context. You can also include request information such as headers and path or user ID + // if the request is authenticated. + const flagContext = { kind: "user", key: "test-user-key-1" }; const vercelClient = createClient(process.env.EDGE_CONFIG); - // start using ld - const client = initLD(sdkKey, vercelClient); + const client = initLD(clientSideID, vercelClient); await client.waitForInitialization(); - const fallthrough = await client.variation(flagKey, contextFallthrough, false); - const emailRule = await client.variation(flagKey, contextEmailRule, false); - const flagDetail = await client.variationDetail(flagKey, contextEmailRule, false); - const allFlags = await client.allFlagsState(contextEmailRule); + + // Use .variation() to evaluate a single feature flag for a given context. + const testFlagVariation = await client.variation(flagKey, flagContext, false); + + // allFlagsState() returns an object containing the variations served for all feature flags for a given context. + // This is useful for bootstrapping flags for use in the LaunchDarkly React SDK or JS client-side SDK. + const allFlags = await client.allFlagsState(flagContext); return NextResponse.json({ flagKey: `${flagKey}`, - contextKey: `${contextFallthrough.key}`, - fallthrough, - emailRule, - emailRuleDetail: `${JSON.stringify(flagDetail)}`, - allFlags: `${JSON.stringify(allFlags)}`, + context: flagContext, + variationServed: testFlagVariation, + allFlags: allFlags.toJSON(), }); } diff --git a/packages/sdk/vercel/example-vercel/src/app/page.tsx b/packages/sdk/vercel/example-vercel/src/app/page.tsx index b83bb1bb26..e46c38cb02 100644 --- a/packages/sdk/vercel/example-vercel/src/app/page.tsx +++ b/packages/sdk/vercel/example-vercel/src/app/page.tsx @@ -1,15 +1,14 @@ -import Image from 'next/image'; -import { Inter } from 'next/font/google'; +import Image from "next/image"; +import { Inter } from "next/font/google"; -const inter = Inter({ subsets: ['latin'] }); +const inter = Inter({ subsets: ["latin"] }); export default function Home() { return (

- Get started by editing  - src/app/page.tsx + Get started by visiting /api/hello

- By{' '} + By{" "} Vercel Logo

- Docs{' '} + Docs{" "} -> @@ -67,7 +66,7 @@ export default function Home() { rel="noopener noreferrer" >

- Learn{' '} + Learn{" "} -> @@ -84,7 +83,7 @@ export default function Home() { rel="noopener noreferrer" >

- Templates{' '} + Templates{" "} -> @@ -101,7 +100,7 @@ export default function Home() { rel="noopener noreferrer" >

- Deploy{' '} + Deploy{" "} -> From 7546240e31dd8bf706748a5fa0373e95711d407a Mon Sep 17 00:00:00 2001 From: Henry Barrow Date: Fri, 2 Jun 2023 14:45:28 +0100 Subject: [PATCH 02/13] run prettier --- .../example-vercel/src/app/api/hello/route.ts | 14 +++++++------- .../sdk/vercel/example-vercel/src/app/page.tsx | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/sdk/vercel/example-vercel/src/app/api/hello/route.ts b/packages/sdk/vercel/example-vercel/src/app/api/hello/route.ts index ebd718b498..9008ea7a82 100644 --- a/packages/sdk/vercel/example-vercel/src/app/api/hello/route.ts +++ b/packages/sdk/vercel/example-vercel/src/app/api/hello/route.ts @@ -1,24 +1,24 @@ -import { NextResponse } from "next/server"; -import { createClient } from "@vercel/edge-config"; -import { init as initLD } from "@launchdarkly/vercel-server-sdk"; +import { NextResponse } from 'next/server'; +import { createClient } from '@vercel/edge-config'; +import { init as initLD } from '@launchdarkly/vercel-server-sdk'; -export const runtime = "edge"; +export const runtime = 'edge'; export async function GET() { const clientSideID = process.env.LD_CLIENT_SIDE_ID; if (!clientSideID) { return NextResponse.json( { - error: "LD_CLIENT_SIDE_ID environment variable is missing.", + error: 'LD_CLIENT_SIDE_ID environment variable is missing.', }, { status: 500 } ); } - const flagKey = "test-flag"; + const flagKey = 'test-flag'; // This is just a simple example context. You can also include request information such as headers and path or user ID // if the request is authenticated. - const flagContext = { kind: "user", key: "test-user-key-1" }; + const flagContext = { kind: 'user', key: 'test-user-key-1' }; const vercelClient = createClient(process.env.EDGE_CONFIG); diff --git a/packages/sdk/vercel/example-vercel/src/app/page.tsx b/packages/sdk/vercel/example-vercel/src/app/page.tsx index e46c38cb02..29f446819e 100644 --- a/packages/sdk/vercel/example-vercel/src/app/page.tsx +++ b/packages/sdk/vercel/example-vercel/src/app/page.tsx @@ -1,7 +1,7 @@ -import Image from "next/image"; -import { Inter } from "next/font/google"; +import Image from 'next/image'; +import { Inter } from 'next/font/google'; -const inter = Inter({ subsets: ["latin"] }); +const inter = Inter({ subsets: ['latin'] }); export default function Home() { return ( @@ -17,7 +17,7 @@ export default function Home() { target="_blank" rel="noopener noreferrer" > - By{" "} + By{' '} Vercel Logo

- Docs{" "} + Docs{' '} -> @@ -66,7 +66,7 @@ export default function Home() { rel="noopener noreferrer" >

- Learn{" "} + Learn{' '} -> @@ -83,7 +83,7 @@ export default function Home() { rel="noopener noreferrer" >

- Templates{" "} + Templates{' '} -> @@ -100,7 +100,7 @@ export default function Home() { rel="noopener noreferrer" >

- Deploy{" "} + Deploy{' '} -> From 2b65ecc17658a728f03c823476785454d6131906 Mon Sep 17 00:00:00 2001 From: Henry Barrow Date: Fri, 2 Jun 2023 14:52:51 +0100 Subject: [PATCH 03/13] clenaup: move vercel into examples dir --- package.json | 2 +- .../route-handler}/.gitignore | 0 .../route-handler}/README.md | 0 .../route-handler}/next-env.d.ts | 0 .../route-handler}/next.config.js | 0 .../route-handler}/package.json | 0 .../route-handler}/postcss.config.js | 0 .../route-handler}/public/next.svg | 0 .../route-handler}/public/vercel.svg | 0 .../route-handler}/src/app/api/hello/route.ts | 0 .../route-handler}/src/app/favicon.ico | Bin .../route-handler}/src/app/globals.css | 0 .../route-handler}/src/app/layout.tsx | 0 .../route-handler}/src/app/page.tsx | 0 .../route-handler}/tailwind.config.js | 0 .../route-handler}/testData.json | 0 .../route-handler}/tsconfig.eslint.json | 0 .../route-handler}/tsconfig.json | 0 packages/sdk/vercel/tsconfig.json | 19 ++++++++++++++---- 19 files changed, 16 insertions(+), 5 deletions(-) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/.gitignore (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/README.md (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/next-env.d.ts (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/next.config.js (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/package.json (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/postcss.config.js (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/public/next.svg (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/public/vercel.svg (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/src/app/api/hello/route.ts (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/src/app/favicon.ico (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/src/app/globals.css (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/src/app/layout.tsx (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/src/app/page.tsx (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/tailwind.config.js (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/testData.json (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/tsconfig.eslint.json (100%) rename packages/sdk/vercel/{example-vercel => examples/route-handler}/tsconfig.json (100%) diff --git a/package.json b/package.json index 4403974827..26d892d310 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "packages/sdk/cloudflare", "packages/sdk/cloudflare/example", "packages/sdk/vercel", - "packages/sdk/vercel/example-vercel", + "packages/sdk/vercel/examples/route-handler", "packages/sdk/akamai" ], "private": true, diff --git a/packages/sdk/vercel/example-vercel/.gitignore b/packages/sdk/vercel/examples/route-handler/.gitignore similarity index 100% rename from packages/sdk/vercel/example-vercel/.gitignore rename to packages/sdk/vercel/examples/route-handler/.gitignore diff --git a/packages/sdk/vercel/example-vercel/README.md b/packages/sdk/vercel/examples/route-handler/README.md similarity index 100% rename from packages/sdk/vercel/example-vercel/README.md rename to packages/sdk/vercel/examples/route-handler/README.md diff --git a/packages/sdk/vercel/example-vercel/next-env.d.ts b/packages/sdk/vercel/examples/route-handler/next-env.d.ts similarity index 100% rename from packages/sdk/vercel/example-vercel/next-env.d.ts rename to packages/sdk/vercel/examples/route-handler/next-env.d.ts diff --git a/packages/sdk/vercel/example-vercel/next.config.js b/packages/sdk/vercel/examples/route-handler/next.config.js similarity index 100% rename from packages/sdk/vercel/example-vercel/next.config.js rename to packages/sdk/vercel/examples/route-handler/next.config.js diff --git a/packages/sdk/vercel/example-vercel/package.json b/packages/sdk/vercel/examples/route-handler/package.json similarity index 100% rename from packages/sdk/vercel/example-vercel/package.json rename to packages/sdk/vercel/examples/route-handler/package.json diff --git a/packages/sdk/vercel/example-vercel/postcss.config.js b/packages/sdk/vercel/examples/route-handler/postcss.config.js similarity index 100% rename from packages/sdk/vercel/example-vercel/postcss.config.js rename to packages/sdk/vercel/examples/route-handler/postcss.config.js diff --git a/packages/sdk/vercel/example-vercel/public/next.svg b/packages/sdk/vercel/examples/route-handler/public/next.svg similarity index 100% rename from packages/sdk/vercel/example-vercel/public/next.svg rename to packages/sdk/vercel/examples/route-handler/public/next.svg diff --git a/packages/sdk/vercel/example-vercel/public/vercel.svg b/packages/sdk/vercel/examples/route-handler/public/vercel.svg similarity index 100% rename from packages/sdk/vercel/example-vercel/public/vercel.svg rename to packages/sdk/vercel/examples/route-handler/public/vercel.svg diff --git a/packages/sdk/vercel/example-vercel/src/app/api/hello/route.ts b/packages/sdk/vercel/examples/route-handler/src/app/api/hello/route.ts similarity index 100% rename from packages/sdk/vercel/example-vercel/src/app/api/hello/route.ts rename to packages/sdk/vercel/examples/route-handler/src/app/api/hello/route.ts diff --git a/packages/sdk/vercel/example-vercel/src/app/favicon.ico b/packages/sdk/vercel/examples/route-handler/src/app/favicon.ico similarity index 100% rename from packages/sdk/vercel/example-vercel/src/app/favicon.ico rename to packages/sdk/vercel/examples/route-handler/src/app/favicon.ico diff --git a/packages/sdk/vercel/example-vercel/src/app/globals.css b/packages/sdk/vercel/examples/route-handler/src/app/globals.css similarity index 100% rename from packages/sdk/vercel/example-vercel/src/app/globals.css rename to packages/sdk/vercel/examples/route-handler/src/app/globals.css diff --git a/packages/sdk/vercel/example-vercel/src/app/layout.tsx b/packages/sdk/vercel/examples/route-handler/src/app/layout.tsx similarity index 100% rename from packages/sdk/vercel/example-vercel/src/app/layout.tsx rename to packages/sdk/vercel/examples/route-handler/src/app/layout.tsx diff --git a/packages/sdk/vercel/example-vercel/src/app/page.tsx b/packages/sdk/vercel/examples/route-handler/src/app/page.tsx similarity index 100% rename from packages/sdk/vercel/example-vercel/src/app/page.tsx rename to packages/sdk/vercel/examples/route-handler/src/app/page.tsx diff --git a/packages/sdk/vercel/example-vercel/tailwind.config.js b/packages/sdk/vercel/examples/route-handler/tailwind.config.js similarity index 100% rename from packages/sdk/vercel/example-vercel/tailwind.config.js rename to packages/sdk/vercel/examples/route-handler/tailwind.config.js diff --git a/packages/sdk/vercel/example-vercel/testData.json b/packages/sdk/vercel/examples/route-handler/testData.json similarity index 100% rename from packages/sdk/vercel/example-vercel/testData.json rename to packages/sdk/vercel/examples/route-handler/testData.json diff --git a/packages/sdk/vercel/example-vercel/tsconfig.eslint.json b/packages/sdk/vercel/examples/route-handler/tsconfig.eslint.json similarity index 100% rename from packages/sdk/vercel/example-vercel/tsconfig.eslint.json rename to packages/sdk/vercel/examples/route-handler/tsconfig.eslint.json diff --git a/packages/sdk/vercel/example-vercel/tsconfig.json b/packages/sdk/vercel/examples/route-handler/tsconfig.json similarity index 100% rename from packages/sdk/vercel/example-vercel/tsconfig.json rename to packages/sdk/vercel/examples/route-handler/tsconfig.json diff --git a/packages/sdk/vercel/tsconfig.json b/packages/sdk/vercel/tsconfig.json index b1cf0c57b2..603d370bc8 100644 --- a/packages/sdk/vercel/tsconfig.json +++ b/packages/sdk/vercel/tsconfig.json @@ -4,7 +4,9 @@ "rootDir": ".", "outDir": "dist", "target": "es6", - "lib": ["es6"], + "lib": [ + "es6" + ], "module": "commonjs", "strict": true, "noImplicitOverride": true, @@ -15,8 +17,17 @@ "resolveJsonModule": true, "stripInternal": true, "moduleResolution": "node", - "types": ["jest", "node"], + "types": [ + "jest", + "node" + ], "skipLibCheck": true }, - "exclude": ["**/*.test.ts", "dist", "node_modules", "__tests__", "example-vercel"] -} + "exclude": [ + "**/*.test.ts", + "dist", + "node_modules", + "__tests__", + "examples" + ] +} \ No newline at end of file From ac22b4a758c0edba4d3ba2fc393769bfb52d33a6 Mon Sep 17 00:00:00 2001 From: Henry Barrow Date: Fri, 2 Jun 2023 15:00:30 +0100 Subject: [PATCH 04/13] Add complete example --- package.json | 1 + .../sdk/vercel/examples/complete/.env.example | 2 + .../vercel/examples/complete/.eslintrc.json | 4 + .../sdk/vercel/examples/complete/.gitignore | 43 ++++++ packages/sdk/vercel/examples/complete/.npmrc | 1 + .../sdk/vercel/examples/complete/README.md | 52 ++++++++ .../examples/complete/app/closed/page.tsx | 23 ++++ .../vercel/examples/complete/app/layout.tsx | 48 +++++++ .../complete/app/missing-edge-config/page.tsx | 68 ++++++++++ .../sdk/vercel/examples/complete/app/page.tsx | 126 ++++++++++++++++++ .../components/launchdarklyProvider.tsx | 40 ++++++ .../examples/complete/components/nav.tsx | 31 +++++ .../examples/complete/lib/ldEdgeClient.ts | 8 ++ .../vercel/examples/complete/middleware.ts | 67 ++++++++++ .../vercel/examples/complete/next-env.d.ts | 5 + .../vercel/examples/complete/next.config.js | 5 + .../sdk/vercel/examples/complete/package.json | 32 +++++ .../examples/complete/postcss.config.js | 8 ++ .../vercel/examples/complete/public/cat.png | Bin 0 -> 30817 bytes .../examples/complete/public/favicon.ico | Bin 0 -> 15086 bytes .../examples/complete/public/hot-dog.ico | Bin 0 -> 189647 bytes .../examples/complete/public/vercel.svg | 4 + .../examples/complete/tailwind.config.js | 12 ++ .../vercel/examples/complete/tsconfig.json | 26 ++++ .../sdk/vercel/examples/complete/vercel.json | 3 + 25 files changed, 609 insertions(+) create mode 100644 packages/sdk/vercel/examples/complete/.env.example create mode 100644 packages/sdk/vercel/examples/complete/.eslintrc.json create mode 100644 packages/sdk/vercel/examples/complete/.gitignore create mode 100644 packages/sdk/vercel/examples/complete/.npmrc create mode 100644 packages/sdk/vercel/examples/complete/README.md create mode 100644 packages/sdk/vercel/examples/complete/app/closed/page.tsx create mode 100644 packages/sdk/vercel/examples/complete/app/layout.tsx create mode 100644 packages/sdk/vercel/examples/complete/app/missing-edge-config/page.tsx create mode 100644 packages/sdk/vercel/examples/complete/app/page.tsx create mode 100644 packages/sdk/vercel/examples/complete/components/launchdarklyProvider.tsx create mode 100644 packages/sdk/vercel/examples/complete/components/nav.tsx create mode 100644 packages/sdk/vercel/examples/complete/lib/ldEdgeClient.ts create mode 100644 packages/sdk/vercel/examples/complete/middleware.ts create mode 100644 packages/sdk/vercel/examples/complete/next-env.d.ts create mode 100644 packages/sdk/vercel/examples/complete/next.config.js create mode 100644 packages/sdk/vercel/examples/complete/package.json create mode 100644 packages/sdk/vercel/examples/complete/postcss.config.js create mode 100644 packages/sdk/vercel/examples/complete/public/cat.png create mode 100644 packages/sdk/vercel/examples/complete/public/favicon.ico create mode 100644 packages/sdk/vercel/examples/complete/public/hot-dog.ico create mode 100644 packages/sdk/vercel/examples/complete/public/vercel.svg create mode 100644 packages/sdk/vercel/examples/complete/tailwind.config.js create mode 100644 packages/sdk/vercel/examples/complete/tsconfig.json create mode 100644 packages/sdk/vercel/examples/complete/vercel.json diff --git a/package.json b/package.json index 26d892d310..419294818c 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "packages/sdk/cloudflare", "packages/sdk/cloudflare/example", "packages/sdk/vercel", + "packages/sdk/vercel/examples/complete", "packages/sdk/vercel/examples/route-handler", "packages/sdk/akamai" ], diff --git a/packages/sdk/vercel/examples/complete/.env.example b/packages/sdk/vercel/examples/complete/.env.example new file mode 100644 index 0000000000..c510dcf81e --- /dev/null +++ b/packages/sdk/vercel/examples/complete/.env.example @@ -0,0 +1,2 @@ +EDGE_CONFIG= +LD_CLIENT_SIDE_ID= \ No newline at end of file diff --git a/packages/sdk/vercel/examples/complete/.eslintrc.json b/packages/sdk/vercel/examples/complete/.eslintrc.json new file mode 100644 index 0000000000..a2569c2c7c --- /dev/null +++ b/packages/sdk/vercel/examples/complete/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "root": true, + "extends": "next/core-web-vitals" +} diff --git a/packages/sdk/vercel/examples/complete/.gitignore b/packages/sdk/vercel/examples/complete/.gitignore new file mode 100644 index 0000000000..34d63f0733 --- /dev/null +++ b/packages/sdk/vercel/examples/complete/.gitignore @@ -0,0 +1,43 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# Dependencies +/node_modules +/.pnp +.pnp.js + +# Testing +/coverage + +# Next.js +/.next/ +/out/ + +# VS code +/.vscode + +# Production +/build + +# Misc +.DS_Store +*.pem + +# Debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Local ENV files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Vercel +.vercel + +# Turborepo +.turbo + +# typescript +*.tsbuildinfo \ No newline at end of file diff --git a/packages/sdk/vercel/examples/complete/.npmrc b/packages/sdk/vercel/examples/complete/.npmrc new file mode 100644 index 0000000000..e9ee3cb4d0 --- /dev/null +++ b/packages/sdk/vercel/examples/complete/.npmrc @@ -0,0 +1 @@ +legacy-peer-deps=true \ No newline at end of file diff --git a/packages/sdk/vercel/examples/complete/README.md b/packages/sdk/vercel/examples/complete/README.md new file mode 100644 index 0000000000..b1e96f7c94 --- /dev/null +++ b/packages/sdk/vercel/examples/complete/README.md @@ -0,0 +1,52 @@ +# LaunchDarkly Feature Flag Apple Store + +This example shows how to evaluate feature flags in Vercel's edge runtime using the [LaunchDarkly Vercel SDK](https://github.com/launchdarkly/js-core/tree/main/packages/sdk/vercel). Two primary use cases are highlighted: + +1. Bootstrapping feature flags from the edge runtime and consuming them in the [LaunchDarkly Client-side SDK for React](https://github.com/launchdarkly/react-client-sdk). This is leveraging feature flags in edge-rendered pages while still maintaining the events and ergonomics provided by the React SDK. You can see details in [`app/layout.tsx`](./app/layout.tsx) and [`components/launchdarklyProvider.tsx`](./components/launchdarklyProvider.tsx). +2. Evaluating feature flags in the [Edge Middleware](https://vercel.com/docs/concepts/functions/edge-middleware). This can be seen in [`middleware.ts`](./middleware.ts). + +## Demo + +https://hello-vercel-edge.vercel.app/ + +## How to Use + +#### Create a new LaunchDarkly project and flags + +For simplicity, we recommend [creating a new LaunchDarkly project](https://docs.launchdarkly.com/home/organize/projects/?q=create+proj) for this example app. After creating a new project, create the following feature flags with Client-side SDK availability: + +- `bootstrap-flags` - (Boolean) - This flag will determine whether or not the LaunchDarkly React SDK will bootstrap feature flags from the edge. +- `show-debugging-info` - (Boolean) - This flag is used to expose the current flag values. +- `hero-text` - (String) - This flag is used to dynamically change the hero text. You can make the variations anything you want, e.g. "The best way to buy the products you love." +- `enable-hot-dog-favicon` - (Boolean) - This flag is used in middleware.ts to dynamically load a different favicon. +- `store-closed` - (Boolean) - This flag is evaluated in `middleware.ts` and can be used to load a different home page when the store is closed. + +#### Set up the LaunchDarkly Vercel integration + +You will need to have the LaunchDarkly Vercel integration configured to push feature flag data to your Vercel Edge Config. Read [Vercel](https://docs.launchdarkly.com/integrations/vercel/) to set up the integration. Be sure to connect the project you created above. + +#### Set up environment variables + +Copy the `.env.example` file in this directory to `.env.local` (which will be ignored by Git): + +```bash +cp .env.example .env.local +``` + +Set `LD_CLIENT_SIDE_ID` to the LaunchDarkly client-side ID from the same environment you used when configuring the LaunchDarkly + Vercel integration. + +This example requires you to set up an Edge Config and store its connection string in the `EDGE_CONFIG` environment variable. You can specify the Edge Config connection string in `.env.local`, but it's probably easier to run the following command after linking the Edge Config to a project in vercel: + +```bash +vercel env pull .env.development.local +``` + +#### Run the app locally + +Next, run Next.js in development mode: + +```bash +npm run dev +``` + +Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=edge-middleware-eap) ([Documentation](https://nextjs.org/docs/deployment)). diff --git a/packages/sdk/vercel/examples/complete/app/closed/page.tsx b/packages/sdk/vercel/examples/complete/app/closed/page.tsx new file mode 100644 index 0000000000..d8f6e833a5 --- /dev/null +++ b/packages/sdk/vercel/examples/complete/app/closed/page.tsx @@ -0,0 +1,23 @@ +export default function Closed() { + return ( +
+ + + +

+ We'll be back. +

+

+ We're busy updating the Apple Store for you and will be back soon. +

+
+ ); +} diff --git a/packages/sdk/vercel/examples/complete/app/layout.tsx b/packages/sdk/vercel/examples/complete/app/layout.tsx new file mode 100644 index 0000000000..613257dbfd --- /dev/null +++ b/packages/sdk/vercel/examples/complete/app/layout.tsx @@ -0,0 +1,48 @@ +import 'tailwindcss/tailwind.css'; +import Nav from 'components/nav'; +import { ReactElement } from 'react'; +import { headers } from 'next/headers'; +import { LDMultiKindContext } from '@launchdarkly/vercel-server-sdk'; +import LaunchDarklyProvider from 'components/launchdarklyProvider'; +import { ldEdgeClient } from 'lib/ldEdgeClient'; + +// Specify the `edge` runtime to use the LaunchDarkly Edge SDK in layouts +export const runtime = 'edge'; + +export default async function RootLayout({ children }: { children: ReactElement }) { + const headersList = headers(); + await ldEdgeClient.waitForInitialization(); + + // Here we are using basic information from the request as the LaunchDarkly context. If you have session auth in place, + // you will likely want to also include user and organization context. + const context: LDMultiKindContext = { + kind: 'multi', + user: { key: 'anonymous', anonymous: true }, + 'user-agent': { key: headersList.get('user-agent') || 'unknown' }, + method: { + key: 'GET', + }, + }; + + // The allFlagsState call is used to evaluate all feature flags for a given context so they can be bootstrapped but the + // LaunchDarkly React SDK in the `` component. + const allFlags = (await ldEdgeClient.allFlagsState(context)).toJSON() as { + 'bootstrap-flags': boolean; + }; + const bootstrappedFlags = allFlags['bootstrap-flags'] ? allFlags : undefined; + + return ( + + + +