diff --git a/apps/docs/components/custom/logos/supaship.tsx b/apps/docs/components/custom/logos/supaship.tsx new file mode 100644 index 00000000..19ebd81c --- /dev/null +++ b/apps/docs/components/custom/logos/supaship.tsx @@ -0,0 +1,61 @@ +export function SupashipLogo(props: React.SVGProps) { + return ( + + + + + + + + + + ); +} diff --git a/apps/docs/components/custom/provider-list.tsx b/apps/docs/components/custom/provider-list.tsx index 6f420d91..dbbf8c1d 100644 --- a/apps/docs/components/custom/provider-list.tsx +++ b/apps/docs/components/custom/provider-list.tsx @@ -10,6 +10,7 @@ import { OptimizelyLogo } from './logos/optimizely'; import { PostHogLogo } from './logos/posthog'; import { ReflagLogo } from './logos/reflag'; import { StatsigLogo } from './logos/statsig'; +import { SupashipLogo } from './logos/supaship'; type Provider = { key: string; @@ -104,6 +105,15 @@ const providers: Provider[] = [ badges: ['Adapter', 'Flags Explorer'], glowColor: '#5d5dff', }, + { + key: 'supaship', + name: 'Supaship', + href: '/providers/supaship', + logo: SupashipLogo, + badges: ['Adapter'], + glowColor: '#e4f222', + skipInvert: true, + }, { key: 'openfeature', name: 'OpenFeature', diff --git a/apps/docs/content/docs/providers/meta.json b/apps/docs/content/docs/providers/meta.json index 14236ac7..77f0fd6d 100644 --- a/apps/docs/content/docs/providers/meta.json +++ b/apps/docs/content/docs/providers/meta.json @@ -17,6 +17,7 @@ "reflag", "posthog", "flagsmith", + "supaship", "---OpenFeature---", "...openfeature", "---Others---", diff --git a/apps/docs/content/docs/providers/supaship.mdx b/apps/docs/content/docs/providers/supaship.mdx new file mode 100644 index 00000000..ca8ed5ba --- /dev/null +++ b/apps/docs/content/docs/providers/supaship.mdx @@ -0,0 +1,116 @@ +--- +title: 'Supaship' +--- + +The [Supaship](https://supaship.com/) provider contains support for Supaship feature flags. + +The `@flags-sdk/supaship` package provides an adapter for evaluating Supaship flags with the Flags SDK. + + + Learn more about Adapters + + +--- + +## Setup + +The Supaship provider is available in the `@flags-sdk/supaship` module. Install it with + +```bash +npm install @flags-sdk/supaship +``` + +Set required variables: + +```sh +SUPASHIP_SDK_KEY=your-supaship-sdk-key +SUPASHIP_ENVIRONMENT=production +``` + +--- + +## Provider Instance + +Import the default adapter instance `supashipAdapter` from `@flags-sdk/supaship`: + +```ts +import { supashipAdapter } from '@flags-sdk/supaship'; +``` + +If you need custom client settings, create an adapter instance manually: + +```ts +import { createSupashipAdapter } from '@flags-sdk/supaship'; + +const customSupashipAdapter = createSupashipAdapter({ + sdkKey: process.env.SUPASHIP_SDK_KEY, + environment: process.env.SUPASHIP_ENVIRONMENT, + context: { app: 'dashboard' }, + networkConfig: { + requestTimeoutMs: 5000, + }, +}); +``` + +--- + +## Identify Users + +Supaship uses context properties for targeting. Use `identify` to return those properties per request. + +```ts +import { dedupe, flag } from 'flags/next'; +import type { Identify } from 'flags'; +import { supashipAdapter, type FeatureContext } from '@flags-sdk/supaship'; + +const identify = dedupe((async ({ headers, cookies }) => { + const user = await getUser(headers, cookies); + + return { + userId: user.id, + email: user.email, + plan: user.plan, + } satisfies FeatureContext; +}) satisfies Identify); + +export const newHeader = flag({ + key: 'new-header', + defaultValue: false, + identify, + adapter: supashipAdapter.feature(), +}); +``` + + + Learn more about `dedupe` + + + + Learn more about `identify` + + +--- + +## Methods + +### `feature` + +Use `feature()` to evaluate a Supaship feature value: + +```ts +import { flag } from 'flags/next'; +import { supashipAdapter, type FeatureContext } from '@flags-sdk/supaship'; + +export const promoBanner = flag({ + key: 'promo-banner', + defaultValue: false, + adapter: supashipAdapter.feature(), +}); +``` + +--- + +## Read More + +- [Adapter Source Code](https://github.com/vercel/flags/tree/main/packages/adapter-supaship) +- [Supaship JavaScript SDK](https://www.npmjs.com/package/@supashiphq/javascript-sdk) diff --git a/packages/adapter-supaship/CHANGELOG.md b/packages/adapter-supaship/CHANGELOG.md new file mode 100644 index 00000000..0cf2fbd8 --- /dev/null +++ b/packages/adapter-supaship/CHANGELOG.md @@ -0,0 +1,7 @@ +# @flags-sdk/supaship + +## 0.1.0 + +### Minor Changes + +- Add initial Supaship adapter package. diff --git a/packages/adapter-supaship/README.md b/packages/adapter-supaship/README.md new file mode 100644 index 00000000..5326bbe4 --- /dev/null +++ b/packages/adapter-supaship/README.md @@ -0,0 +1,70 @@ +# Flags SDK - Supaship Adapter + +The Supaship adapter for the [Flags SDK](https://flags-sdk.dev/) supports server-side feature evaluation powered by [Supaship](https://supaship.com/). + +## Setup + +Install the adapter: + +```bash +pnpm i @flags-sdk/supaship +``` + +## Provider Instance + +Import the default adapter instance `supashipAdapter` from `@flags-sdk/supaship`: + +```ts +import { supashipAdapter } from "@flags-sdk/supaship"; +``` + +The default adapter is configured from: + +```sh +export SUPASHIP_SDK_KEY="your-supaship-sdk-key" +export SUPASHIP_ENVIRONMENT="production" +``` + +## Example + +```ts +import { dedupe, flag } from "flags/next"; +import { supashipAdapter, type FeatureContext } from "@flags-sdk/supaship"; + +const identify = dedupe(async (): Promise => { + return { + userId: "user-123", + plan: "pro", + }; +}); + +export const newHeader = flag({ + key: "new-header", + defaultValue: false, + identify, + adapter: supashipAdapter.feature(), +}); +``` + +## Custom Adapter + +Use `createSupashipAdapter` when you need custom client configuration: + +```ts +import { createSupashipAdapter } from "@flags-sdk/supaship"; + +const customSupashipAdapter = createSupashipAdapter({ + sdkKey: process.env.SUPASHIP_SDK_KEY!, + environment: "staging", + context: { app: "dashboard" }, + networkConfig: { + requestTimeoutMs: 5000, + }, +}); +``` + +## Notes + +- This adapter uses Supaship's JavaScript SDK (`@supashiphq/javascript-sdk`) under the hood. +- Supaship supports feature values of type `boolean`, `null`, `object`, and `array`. +- If Supaship returns `null` or does not return a value, the flag `defaultValue` is used. diff --git a/packages/adapter-supaship/package.json b/packages/adapter-supaship/package.json new file mode 100644 index 00000000..3f029f51 --- /dev/null +++ b/packages/adapter-supaship/package.json @@ -0,0 +1,66 @@ +{ + "name": "@flags-sdk/supaship", + "version": "0.1.0", + "description": "Supaship adapter for the Flags SDK", + "keywords": [ + "flags-sdk", + "supaship", + "vercel", + "feature flags", + "flags" + ], + "homepage": "https://flags-sdk.dev", + "bugs": { + "url": "https://github.com/vercel/flags/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vercel/flags.git" + }, + "license": "MIT", + "author": "", + "sideEffects": false, + "type": "module", + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, + "main": "./dist/index.js", + "typesVersions": { + "*": { + ".": [ + "dist/*.d.ts", + "dist/*.d.cts" + ] + } + }, + "files": [ + "dist", + "CHANGELOG.md" + ], + "scripts": { + "build": "rimraf dist && tsup", + "dev": "tsup --watch --clean=false", + "check": "biome check", + "test": "vitest --run", + "test:watch": "vitest", + "type-check": "tsc --noEmit" + }, + "dependencies": { + "@supashiphq/javascript-sdk": "1.0.1" + }, + "devDependencies": { + "@types/node": "20.11.17", + "flags": "workspace:*", + "rimraf": "6.1.2", + "tsup": "8.5.1", + "typescript": "5.6.3", + "vite": "5.4.21", + "vitest": "1.6.1" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/adapter-supaship/src/index.test.ts b/packages/adapter-supaship/src/index.test.ts new file mode 100644 index 00000000..6eb0e667 --- /dev/null +++ b/packages/adapter-supaship/src/index.test.ts @@ -0,0 +1,165 @@ +import type { ReadonlyRequestCookies } from 'flags'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { + createSupashipAdapter, + resetDefaultSupashipAdapter, + supashipAdapter, +} from '.'; + +const getFeatureMock = vi.fn(); +const supaClientConstructorMock = vi.fn(); + +vi.mock('@supashiphq/javascript-sdk', () => { + class MockSupaClient { + constructor(config: unknown) { + supaClientConstructorMock(config); + } + + getFeature = getFeatureMock; + } + + return { + SupaClient: MockSupaClient, + }; +}); + +describe('@flags-sdk/supaship', () => { + beforeEach(() => { + vi.clearAllMocks(); + resetDefaultSupashipAdapter(); + delete process.env.SUPASHIP_SDK_KEY; + delete process.env.SUPASHIP_ENVIRONMENT; + }); + + it('creates a client with expected defaults', async () => { + getFeatureMock.mockResolvedValueOnce(true); + const adapter = createSupashipAdapter({ + sdkKey: 'test-sdk-key', + environment: 'test', + }); + + await adapter.feature().decide({ + key: 'my-flag', + headers: new Headers(), + cookies: {} as ReadonlyRequestCookies, + defaultValue: false, + }); + + expect(supaClientConstructorMock).toHaveBeenCalledWith({ + sdkKey: 'test-sdk-key', + environment: 'test', + context: {}, + features: {}, + sensitiveContextProperties: undefined, + networkConfig: undefined, + plugins: undefined, + toolbar: false, + }); + }); + + it('merges adapter context and entities for each decision', async () => { + getFeatureMock.mockResolvedValueOnce(true); + const adapter = createSupashipAdapter({ + sdkKey: 'test-sdk-key', + environment: 'test', + context: { app: 'flags' }, + }); + + const result = await adapter + .feature({ + context: { region: 'us' }, + }) + .decide({ + key: 'my-flag', + entities: { userId: '123' }, + headers: new Headers(), + cookies: {} as ReadonlyRequestCookies, + }); + + expect(result).toBe(true); + expect(getFeatureMock).toHaveBeenCalledWith('my-flag', { + context: { app: 'flags', region: 'us', userId: '123' }, + }); + }); + + it('uses defaultValue when Supaship returns undefined', async () => { + getFeatureMock.mockResolvedValueOnce(undefined); + const adapter = createSupashipAdapter({ + sdkKey: 'test-sdk-key', + environment: 'test', + }); + + const result = await adapter.feature().decide({ + key: 'missing-flag', + headers: new Headers(), + cookies: {} as ReadonlyRequestCookies, + defaultValue: false, + }); + + expect(result).toBe(false); + }); + + it('uses defaultValue when Supaship returns null', async () => { + getFeatureMock.mockResolvedValueOnce(null); + const adapter = createSupashipAdapter({ + sdkKey: 'test-sdk-key', + environment: 'test', + }); + + const result = await adapter.feature().decide({ + key: 'null-flag', + headers: new Headers(), + cookies: {} as ReadonlyRequestCookies, + defaultValue: false, + }); + + expect(result).toBe(false); + }); + + it('throws when Supaship returns undefined and no default exists', async () => { + getFeatureMock.mockResolvedValueOnce(undefined); + const adapter = createSupashipAdapter({ + sdkKey: 'test-sdk-key', + environment: 'test', + }); + + await expect( + adapter.feature().decide({ + key: 'missing-flag', + headers: new Headers(), + cookies: {} as ReadonlyRequestCookies, + }), + ).rejects.toThrow( + '@flags-sdk/supaship: Feature "missing-flag" resolved to null/undefined and no defaultValue was provided.', + ); + }); + + it('default adapter throws when required env vars are missing', () => { + expect(() => supashipAdapter.feature()).toThrow( + '@flags-sdk/supaship: Missing SUPASHIP_SDK_KEY environment variable', + ); + }); + + it('default adapter is lazily initialized once', async () => { + process.env.SUPASHIP_SDK_KEY = 'sdk-key'; + process.env.SUPASHIP_ENVIRONMENT = 'production'; + + getFeatureMock.mockResolvedValue(true); + + await supashipAdapter.feature().decide({ + key: 'flag-a', + headers: new Headers(), + cookies: {} as ReadonlyRequestCookies, + defaultValue: false, + }); + + await supashipAdapter.feature().decide({ + key: 'flag-b', + headers: new Headers(), + cookies: {} as ReadonlyRequestCookies, + defaultValue: false, + }); + + expect(supaClientConstructorMock).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/adapter-supaship/src/index.ts b/packages/adapter-supaship/src/index.ts new file mode 100644 index 00000000..bbed208f --- /dev/null +++ b/packages/adapter-supaship/src/index.ts @@ -0,0 +1,129 @@ +import { + type FeatureContext, + type FeaturesWithFallbacks, + type FeatureValue, + type NetworkConfig, + SupaClient, + type SupaPlugin, +} from '@supashiphq/javascript-sdk'; +import type { Adapter } from 'flags'; + +export type { + FeatureContext, + FeatureValue as SupashipFeatureValue, + NetworkConfig, + SupaPlugin, +}; + +type AdapterOptions = { + /** Static context merged into every evaluation before request entities. */ + context?: FeatureContext; + /** Optional origin used for toolbar links. */ + origin?: string | ((key: string) => string | undefined); +}; + +type AdapterResponse = { + feature: ( + options?: AdapterOptions, + ) => Adapter; + /** The Supaship client instance used by the adapter. */ + supaClient: SupaClient; +}; + +type CreateSupashipAdapterOptions = { + sdkKey: string; + environment: string; + context?: FeatureContext; + sensitiveContextProperties?: string[]; + networkConfig?: NetworkConfig; + plugins?: SupaPlugin[]; +}; + +let defaultSupashipAdapter: + | ReturnType + | undefined; + +function assertEnv(name: string): string { + const value = process.env[name]; + if (!value) { + throw new Error( + `@flags-sdk/supaship: Missing ${name} environment variable`, + ); + } + return value; +} + +export function resetDefaultSupashipAdapter() { + defaultSupashipAdapter = undefined; +} + +export function createSupashipAdapter( + options: CreateSupashipAdapterOptions, +): AdapterResponse { + const adapterContext = options.context ?? {}; + + const supaClient = new SupaClient({ + sdkKey: options.sdkKey, + environment: options.environment, + context: adapterContext, + features: {}, + sensitiveContextProperties: options.sensitiveContextProperties, + networkConfig: options.networkConfig, + plugins: options.plugins, + toolbar: false, + }); + + function feature( + adapterOptions: AdapterOptions = {}, + ): Adapter { + return { + origin: adapterOptions.origin, + async decide({ key, entities, defaultValue }) { + const context = { + ...adapterContext, + ...(adapterOptions.context ?? {}), + ...(entities ?? {}), + }; + + const value = (await supaClient.getFeature(key as never, { + context, + })) as ValueType | undefined; + + if (typeof value !== 'undefined' && value !== null) { + return value; + } + + if (typeof defaultValue !== 'undefined') { + return defaultValue; + } + + throw new Error( + `@flags-sdk/supaship: Feature "${key}" resolved to null/undefined and no defaultValue was provided.`, + ); + }, + }; + } + + return { + feature, + supaClient, + }; +} + +function getOrCreateDefaultAdapter() { + if (!defaultSupashipAdapter) { + defaultSupashipAdapter = createSupashipAdapter({ + sdkKey: assertEnv('SUPASHIP_SDK_KEY'), + environment: assertEnv('SUPASHIP_ENVIRONMENT'), + }); + } + + return defaultSupashipAdapter; +} + +export const supashipAdapter: AdapterResponse = { + feature: (...args) => getOrCreateDefaultAdapter().feature(...args), + get supaClient() { + return getOrCreateDefaultAdapter().supaClient; + }, +}; diff --git a/packages/adapter-supaship/tsconfig.json b/packages/adapter-supaship/tsconfig.json new file mode 100644 index 00000000..d0d027cb --- /dev/null +++ b/packages/adapter-supaship/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig-base.json", + "include": ["src"] +} diff --git a/packages/adapter-supaship/tsup.config.js b/packages/adapter-supaship/tsup.config.js new file mode 100644 index 00000000..43813947 --- /dev/null +++ b/packages/adapter-supaship/tsup.config.js @@ -0,0 +1,13 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['esm', 'cjs'], + splitting: true, + sourcemap: true, + minify: false, + clean: false, + skipNodeModulesBundle: true, + dts: true, + external: ['node_modules'], +}); diff --git a/packages/adapter-supaship/vitest.config.ts b/packages/adapter-supaship/vitest.config.ts new file mode 100644 index 00000000..4af8bf98 --- /dev/null +++ b/packages/adapter-supaship/vitest.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + plugins: [], + test: { environment: 'node' }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 64cb3de8..2531ab4e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,7 +34,7 @@ importers: version: 15.2.2 next: specifier: ^16.1.6 - version: 16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305) + version: 16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312) playwright: specifier: 1.56.1 version: 1.56.1 @@ -43,7 +43,7 @@ importers: version: 0.3.14 react: specifier: canary - version: 19.3.0-canary-46103596-20260305 + version: 19.3.0-canary-c80a0750-20260312 turbo: specifier: 2.8.15 version: 2.8.15 @@ -435,7 +435,7 @@ importers: dependencies: '@vercel/edge-config': specifier: ^1.4.3 - version: 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305)) + version: 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312)) devDependencies: '@types/node': specifier: 20.11.17 @@ -497,7 +497,7 @@ importers: version: 1.6.1 '@vercel/edge-config': specifier: ^1.4.3 - version: 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305)) + version: 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312)) '@vercel/functions': specifier: ^1.5.2 version: 1.6.0 @@ -531,7 +531,7 @@ importers: dependencies: '@vercel/edge-config': specifier: ^1.4.3 - version: 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305)) + version: 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312)) hypertune: specifier: 2.8.3 version: 2.8.3 @@ -565,10 +565,10 @@ importers: dependencies: '@launchdarkly/vercel-server-sdk': specifier: ^1.3.34 - version: 1.3.34(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305)) + version: 1.3.34(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312)) '@vercel/edge-config': specifier: ^1.4.3 - version: 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305)) + version: 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312)) devDependencies: '@types/node': specifier: 20.11.17 @@ -656,7 +656,7 @@ importers: dependencies: '@vercel/edge-config': specifier: ^1.4.3 - version: 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305)) + version: 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312)) posthog-node: specifier: 4.11.1 version: 4.11.1 @@ -748,7 +748,7 @@ importers: dependencies: '@vercel/edge-config': specifier: ^1.4.3 - version: 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305)) + version: 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312)) '@vercel/functions': specifier: ^1.5.2 version: 1.6.0 @@ -757,7 +757,7 @@ importers: version: 0.5.2 statsig-node-vercel: specifier: ^0.7.0 - version: 0.7.0(@vercel/edge-config@1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305))) + version: 0.7.0(@vercel/edge-config@1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312))) devDependencies: '@types/node': specifier: 20.11.17 @@ -784,6 +784,34 @@ importers: specifier: 1.6.1 version: 1.6.1(@types/node@20.11.17)(lightningcss@1.30.2) + packages/adapter-supaship: + dependencies: + '@supashiphq/javascript-sdk': + specifier: 1.0.1 + version: 1.0.1 + devDependencies: + '@types/node': + specifier: 20.11.17 + version: 20.11.17 + flags: + specifier: workspace:* + version: link:../flags + rimraf: + specifier: 6.1.2 + version: 6.1.2 + tsup: + specifier: 8.5.1 + version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(typescript@5.6.3)(yaml@2.8.1) + typescript: + specifier: 5.6.3 + version: 5.6.3 + vite: + specifier: 5.4.21 + version: 5.4.21(@types/node@20.11.17)(lightningcss@1.30.2) + vitest: + specifier: 1.6.1 + version: 1.6.1(@types/node@20.11.17)(lightningcss@1.30.2) + packages/adapter-vercel: dependencies: '@vercel/flags-core': @@ -801,7 +829,7 @@ importers: version: 2.6.4(@types/node@20.11.17)(typescript@5.6.3) next: specifier: 16.1.5 - version: 16.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305) + version: 16.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312) tsup: specifier: 8.5.1 version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(typescript@5.6.3)(yaml@2.8.1) @@ -911,7 +939,7 @@ importers: version: 20.11.17 next: specifier: ^16.1.6 - version: 16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305) + version: 16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312) tsup: specifier: 8.5.1 version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(typescript@5.6.3)(yaml@2.8.1) @@ -3886,6 +3914,9 @@ packages: peerDependencies: react: ^18.0.0 || ^19.0.0 + '@supashiphq/javascript-sdk@1.0.1': + resolution: {integrity: sha512-CtWnzMws/WQ/zlGXvnKvlR5awSd8QaJBEBD932wVVAsXQo2IZGHR9IRC3WfafQbB7DsDmMt6Uw5PurYppWddRg==} + '@sveltejs/acorn-typescript@1.0.6': resolution: {integrity: sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==} peerDependencies: @@ -7626,8 +7657,8 @@ packages: resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} engines: {node: '>=0.10.0'} - react@19.3.0-canary-46103596-20260305: - resolution: {integrity: sha512-didbQF2PPuqv9GbxmIhfBSctPrye+oIDv3bjxKNjbRmy1o0zPwDYBMZ75cX1fMevZUOSfLtWCgipO4GadSNwkQ==} + react@19.3.0-canary-c80a0750-20260312: + resolution: {integrity: sha512-TCbBNAx4/CnOm0eRxY6hrrWMc5y/4u20Mw/xeMYvJd5w/Fo8f60hPES/VCFXltOjPS/UbM0SAN+m+K3yUQQAnw==} engines: {node: '>=0.10.0'} read-cache@1.0.0: @@ -9816,10 +9847,10 @@ snapshots: '@launchdarkly/js-sdk-common': 2.19.0 semver: 7.5.4 - '@launchdarkly/vercel-server-sdk@1.3.34(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305))': + '@launchdarkly/vercel-server-sdk@1.3.34(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312))': dependencies: '@launchdarkly/js-server-sdk-common-edge': 2.6.9 - '@vercel/edge-config': 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305)) + '@vercel/edge-config': 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312)) crypto-js: 4.2.0 transitivePeerDependencies: - '@opentelemetry/api' @@ -11423,6 +11454,8 @@ snapshots: react: 19.2.4 shiki: 3.22.0 + '@supashiphq/javascript-sdk@1.0.1': {} + '@sveltejs/acorn-typescript@1.0.6(acorn@8.15.0)': dependencies: acorn: 8.15.0 @@ -12375,12 +12408,12 @@ snapshots: '@opentelemetry/api': 1.9.0 next: 16.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@vercel/edge-config@1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305))': + '@vercel/edge-config@1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312))': dependencies: '@vercel/edge-config-fs': 0.1.0 optionalDependencies: '@opentelemetry/api': 1.9.0 - next: 16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305) + next: 16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312) '@vercel/edge@1.2.1': {} @@ -15451,16 +15484,16 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@16.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305): + next@16.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312): dependencies: '@next/env': 16.1.5 '@swc/helpers': 0.5.15 baseline-browser-mapping: 2.10.0 caniuse-lite: 1.0.30001777 postcss: 8.4.31 - react: 19.3.0-canary-46103596-20260305 - react-dom: 19.2.4(react@19.3.0-canary-46103596-20260305) - styled-jsx: 5.1.6(react@19.3.0-canary-46103596-20260305) + react: 19.3.0-canary-c80a0750-20260312 + react-dom: 19.2.4(react@19.3.0-canary-c80a0750-20260312) + styled-jsx: 5.1.6(react@19.3.0-canary-c80a0750-20260312) optionalDependencies: '@next/swc-darwin-arm64': 16.1.5 '@next/swc-darwin-x64': 16.1.5 @@ -15530,16 +15563,16 @@ snapshots: - babel-plugin-macros optional: true - next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305): + next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312): dependencies: '@next/env': 16.1.6 '@swc/helpers': 0.5.15 baseline-browser-mapping: 2.10.0 caniuse-lite: 1.0.30001777 postcss: 8.4.31 - react: 19.3.0-canary-46103596-20260305 - react-dom: 19.2.4(react@19.3.0-canary-46103596-20260305) - styled-jsx: 5.1.6(react@19.3.0-canary-46103596-20260305) + react: 19.3.0-canary-c80a0750-20260312 + react-dom: 19.2.4(react@19.3.0-canary-c80a0750-20260312) + styled-jsx: 5.1.6(react@19.3.0-canary-c80a0750-20260312) optionalDependencies: '@next/swc-darwin-arm64': 16.1.6 '@next/swc-darwin-x64': 16.1.6 @@ -15984,9 +16017,9 @@ snapshots: react: 19.2.4 scheduler: 0.27.0 - react-dom@19.2.4(react@19.3.0-canary-46103596-20260305): + react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312): dependencies: - react: 19.3.0-canary-46103596-20260305 + react: 19.3.0-canary-c80a0750-20260312 scheduler: 0.27.0 react-is@16.13.1: {} @@ -16094,7 +16127,7 @@ snapshots: react@19.2.4: {} - react@19.3.0-canary-46103596-20260305: {} + react@19.3.0-canary-c80a0750-20260312: {} read-cache@1.0.0: dependencies: @@ -16507,9 +16540,9 @@ snapshots: transitivePeerDependencies: - encoding - statsig-node-vercel@0.7.0(@vercel/edge-config@1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305))): + statsig-node-vercel@0.7.0(@vercel/edge-config@1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312))): dependencies: - '@vercel/edge-config': 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-46103596-20260305))(react@19.3.0-canary-46103596-20260305)) + '@vercel/edge-config': 1.4.3(@opentelemetry/api@1.9.0)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.58.1)(react-dom@19.2.4(react@19.3.0-canary-c80a0750-20260312))(react@19.3.0-canary-c80a0750-20260312)) statsig-node-lite: 0.4.4 transitivePeerDependencies: - encoding @@ -16622,10 +16655,10 @@ snapshots: react: 19.2.4 optional: true - styled-jsx@5.1.6(react@19.3.0-canary-46103596-20260305): + styled-jsx@5.1.6(react@19.3.0-canary-c80a0750-20260312): dependencies: client-only: 0.0.1 - react: 19.3.0-canary-46103596-20260305 + react: 19.3.0-canary-c80a0750-20260312 stylis@4.3.6: {}