diff --git a/.prettierignore b/.prettierignore
index 3220186056..f37aeebac2 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -6,3 +6,5 @@ node_modules/
docs/
.yarn
coverage
+.next
+.vscode
diff --git a/package.json b/package.json
index fda3549e38..050696dc3b 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"packages/sdk/server-node",
"packages/sdk/cloudflare",
"packages/sdk/cloudflare/example",
- "packages/sdk/vercel"
+ "packages/sdk/vercel",
+ "packages/sdk/example-vercel"
],
"private": true,
"scripts": {
diff --git a/packages/sdk/vercel/example-vercel/.gitignore b/packages/sdk/vercel/example-vercel/.gitignore
new file mode 100644
index 0000000000..983df7c7d3
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/.gitignore
@@ -0,0 +1,36 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+.vscode
diff --git a/packages/sdk/vercel/example-vercel/README.md b/packages/sdk/vercel/example-vercel/README.md
new file mode 100644
index 0000000000..5a60fc48eb
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/README.md
@@ -0,0 +1,30 @@
+# 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).
+
+## 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.
+
+## Usage
+
+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.
+
+2. After completing the guide above, you should have linked this example app to your Vercel project and created an `.env.development.local`.
+
+3. At the root of the js-core repo:
+
+```shell
+yarn && yarn build
+```
+
+4. Then back in this example folder:
+
+```shell
+yarn dev
+```
+
+5. Open [http://localhost:3000/api/hello](http://localhost:3000/api/hello).
diff --git a/packages/sdk/vercel/example-vercel/next.config.js b/packages/sdk/vercel/example-vercel/next.config.js
new file mode 100644
index 0000000000..4436b22b5b
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/next.config.js
@@ -0,0 +1,8 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ experimental: {
+ appDir: true,
+ },
+};
+
+module.exports = nextConfig;
diff --git a/packages/sdk/vercel/example-vercel/package.json b/packages/sdk/vercel/example-vercel/package.json
new file mode 100644
index 0000000000..3160d8da59
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "example-vercel",
+ "version": "0.0.0",
+ "packageManager": "yarn@3.4.1",
+ "scripts": {
+ "dev": "next dev",
+ "build": "tsc",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "@launchdarkly/vercel-server-sdk": "0.2.2",
+ "@vercel/edge-config": "^0.1.8",
+ "next": "13.3.1",
+ "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",
+ "autoprefixer": "10.4.14",
+ "postcss": "8.4.23",
+ "tailwindcss": "3.3.2",
+ "typescript": "5.0.4"
+ }
+}
diff --git a/packages/sdk/vercel/example-vercel/postcss.config.js b/packages/sdk/vercel/example-vercel/postcss.config.js
new file mode 100644
index 0000000000..12a703d900
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
diff --git a/packages/sdk/vercel/example-vercel/public/next.svg b/packages/sdk/vercel/example-vercel/public/next.svg
new file mode 100644
index 0000000000..5174b28c56
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/sdk/vercel/example-vercel/public/vercel.svg b/packages/sdk/vercel/example-vercel/public/vercel.svg
new file mode 100644
index 0000000000..d2f8422273
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/public/vercel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
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
new file mode 100644
index 0000000000..391d75db28
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/src/app/api/hello/route.ts
@@ -0,0 +1,37 @@
+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 async function GET() {
+ const sdkKey = 'test-sdk-key';
+ const flagKey = 'testFlag1';
+
+ // 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' };
+
+ const vercelClient = createClient(process.env.EDGE_CONFIG);
+
+ // start using ld
+ const client = initLD(sdkKey, 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);
+
+ return NextResponse.json({
+ flagKey: `${flagKey}`,
+ contextKey: `${contextFallthrough.key}`,
+ fallthrough,
+ emailRule,
+ emailRuleDetail: `${JSON.stringify(flagDetail)}`,
+ allFlags: `${JSON.stringify(allFlags)}`,
+ });
+}
diff --git a/packages/sdk/vercel/example-vercel/src/app/favicon.ico b/packages/sdk/vercel/example-vercel/src/app/favicon.ico
new file mode 100644
index 0000000000..718d6fea48
Binary files /dev/null and b/packages/sdk/vercel/example-vercel/src/app/favicon.ico differ
diff --git a/packages/sdk/vercel/example-vercel/src/app/globals.css b/packages/sdk/vercel/example-vercel/src/app/globals.css
new file mode 100644
index 0000000000..e0936eda7d
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/src/app/globals.css
@@ -0,0 +1,23 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+:root {
+ --foreground-rgb: 0, 0, 0;
+ --background-start-rgb: 214, 219, 220;
+ --background-end-rgb: 255, 255, 255;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --foreground-rgb: 255, 255, 255;
+ --background-start-rgb: 0, 0, 0;
+ --background-end-rgb: 0, 0, 0;
+ }
+}
+
+body {
+ color: rgb(var(--foreground-rgb));
+ background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb)))
+ rgb(var(--background-start-rgb));
+}
diff --git a/packages/sdk/vercel/example-vercel/src/app/layout.tsx b/packages/sdk/vercel/example-vercel/src/app/layout.tsx
new file mode 100644
index 0000000000..04c1f6fa55
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/src/app/layout.tsx
@@ -0,0 +1,17 @@
+import './globals.css';
+import { Inter } from 'next/font/google';
+
+const inter = Inter({ subsets: ['latin'] });
+
+export const metadata = {
+ title: 'Create Next App',
+ description: 'Generated by create next app',
+};
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+
{children}
+
+ );
+}
diff --git a/packages/sdk/vercel/example-vercel/src/app/page.tsx b/packages/sdk/vercel/example-vercel/src/app/page.tsx
new file mode 100644
index 0000000000..b83bb1bb26
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/src/app/page.tsx
@@ -0,0 +1,116 @@
+import Image from 'next/image';
+import { Inter } from 'next/font/google';
+
+const inter = Inter({ subsets: ['latin'] });
+
+export default function Home() {
+ return (
+
+
+
+ Get started by editing
+ src/app/page.tsx
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/sdk/vercel/example-vercel/tailwind.config.js b/packages/sdk/vercel/example-vercel/tailwind.config.js
new file mode 100644
index 0000000000..0fc0404cb3
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/tailwind.config.js
@@ -0,0 +1,17 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: [
+ './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
+ './src/components/**/*.{js,ts,jsx,tsx,mdx}',
+ './src/app/**/*.{js,ts,jsx,tsx,mdx}',
+ ],
+ theme: {
+ extend: {
+ backgroundImage: {
+ 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
+ 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
+ },
+ },
+ },
+ plugins: [],
+};
diff --git a/packages/sdk/vercel/example-vercel/testData.json b/packages/sdk/vercel/example-vercel/testData.json
new file mode 100644
index 0000000000..495819a24d
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/testData.json
@@ -0,0 +1,171 @@
+{
+ "flags": {
+ "testFlag1": {
+ "key": "testFlag1",
+ "on": true,
+ "prerequisites": [],
+ "targets": [],
+ "rules": [
+ {
+ "variation": 1,
+ "id": "rule1",
+ "clauses": [
+ {
+ "contextKind": "user",
+ "attribute": "/email",
+ "op": "contains",
+ "values": ["gmail"],
+ "negate": false
+ }
+ ],
+ "trackEvents": false,
+ "rollout": {
+ "bucketBy": "bucket",
+ "variations": [{ "variation": 1, "weight": 100 }]
+ }
+ }
+ ],
+ "fallthrough": {
+ "variation": 0
+ },
+ "offVariation": 1,
+ "variations": [true, false],
+ "clientSideAvailability": {
+ "usingMobileKey": true,
+ "usingEnvironmentId": true
+ },
+ "clientSide": true,
+ "salt": "aef830243d6640d0a973be89988e008d",
+ "trackEvents": false,
+ "trackEventsFallthrough": false,
+ "debugEventsUntilDate": null,
+ "version": 2,
+ "deleted": false
+ },
+ "testFlag2": {
+ "key": "testFlag2",
+ "on": true,
+ "prerequisites": [],
+ "targets": [],
+ "rules": [],
+ "fallthrough": {
+ "variation": 0,
+ "rollout": {
+ "bucketBy": "bucket",
+ "variations": [{ "variation": 1, "weight": 100 }],
+ "contextKind:": "user",
+ "attribute": "/email"
+ }
+ },
+ "offVariation": 1,
+ "variations": [true, false],
+ "clientSideAvailability": {
+ "usingMobileKey": true,
+ "usingEnvironmentId": true
+ },
+ "clientSide": true,
+ "salt": "aef830243d6640d0a973be89988e008d",
+ "trackEvents": false,
+ "trackEventsFallthrough": false,
+ "debugEventsUntilDate": null,
+ "version": 2,
+ "deleted": false
+ },
+ "testFlag3": {
+ "key": "testFlag3",
+ "on": true,
+ "prerequisites": [],
+ "targets": [],
+ "rules": [
+ {
+ "variation": 1,
+ "id": "rule1",
+ "clauses": [
+ {
+ "op": "segmentMatch",
+ "values": ["testSegment1"],
+ "negate": false
+ }
+ ],
+ "trackEvents": false
+ }
+ ],
+ "fallthrough": {
+ "variation": 0
+ },
+ "offVariation": 1,
+ "variations": [true, false],
+ "clientSideAvailability": {
+ "usingMobileKey": true,
+ "usingEnvironmentId": true
+ },
+ "clientSide": true,
+ "salt": "aef830243d6640d0a973be89988e008d",
+ "trackEvents": false,
+ "trackEventsFallthrough": false,
+ "debugEventsUntilDate": null,
+ "version": 2,
+ "deleted": false
+ }
+ },
+ "segments": {
+ "testSegment1": {
+ "name": "testSegment1",
+ "tags": [],
+ "creationDate": 1676063792158,
+ "key": "testSegment1",
+ "included": [],
+ "excluded": [],
+ "includedContexts": [],
+ "excludedContexts": [],
+ "_links": {
+ "parent": { "href": "/api/v2/segments/default/test", "type": "application/json" },
+ "self": {
+ "href": "/api/v2/segments/default/test/beta-users-1",
+ "type": "application/json"
+ },
+ "site": { "href": "/default/test/segments/beta-users-1", "type": "text/html" }
+ },
+ "rules": [
+ {
+ "id": "rule-country",
+ "clauses": [
+ {
+ "attribute": "country",
+ "op": "in",
+ "values": ["australia"],
+ "negate": false
+ }
+ ]
+ }
+ ],
+ "version": 1,
+ "deleted": false,
+ "_access": { "denied": [], "allowed": [] },
+ "generation": 1
+ },
+ "testSegment2": {
+ "name": "testSegment2",
+ "tags": [],
+ "creationDate": 1676063792158,
+ "key": "testSegment2",
+ "included": [],
+ "excluded": [],
+ "includedContexts": [],
+ "excludedContexts": [],
+ "_links": {
+ "parent": { "href": "/api/v2/segments/default/test", "type": "application/json" },
+ "self": {
+ "href": "/api/v2/segments/default/test/beta-users-1",
+ "type": "application/json"
+ },
+ "site": { "href": "/default/test/segments/beta-users-1", "type": "text/html" }
+ },
+ "rules": [],
+ "version": 1,
+ "deleted": false,
+ "_access": { "denied": [], "allowed": [] },
+ "generation": 1
+ }
+ }
+}
diff --git a/packages/sdk/vercel/example-vercel/tsconfig.eslint.json b/packages/sdk/vercel/example-vercel/tsconfig.eslint.json
new file mode 100644
index 0000000000..8241f86c36
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/tsconfig.eslint.json
@@ -0,0 +1,5 @@
+{
+ "extends": "./tsconfig.json",
+ "include": ["/**/*.ts", "/**/*.tsx"],
+ "exclude": ["node_modules"]
+}
diff --git a/packages/sdk/vercel/example-vercel/tsconfig.json b/packages/sdk/vercel/example-vercel/tsconfig.json
new file mode 100644
index 0000000000..0c7555fa76
--- /dev/null
+++ b/packages/sdk/vercel/example-vercel/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/packages/sdk/vercel/tsconfig.json b/packages/sdk/vercel/tsconfig.json
index d52294d88d..b1cf0c57b2 100644
--- a/packages/sdk/vercel/tsconfig.json
+++ b/packages/sdk/vercel/tsconfig.json
@@ -18,5 +18,5 @@
"types": ["jest", "node"],
"skipLibCheck": true
},
- "exclude": ["**/*.test.ts", "dist", "node_modules", "__tests__", "example"]
+ "exclude": ["**/*.test.ts", "dist", "node_modules", "__tests__", "example-vercel"]
}