From 29e0fc123ba5f9dec7eb2b76ff33ed9dec69d17d Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Sat, 7 Feb 2026 21:10:56 +0100 Subject: [PATCH 01/26] new: REST API SDK --- .claude/settings.local.json | 17 + package.json | 1 + packages/rest-api-sdk/.prettierignore | 2 + packages/rest-api-sdk/README.md | 220 ++++ packages/rest-api-sdk/eslint.config.js | 8 + .../examples/customer-admin-panel/README.md | 23 + .../customer-admin-panel/app/flags/actions.ts | 136 +++ .../app/flags/company/page.tsx | 161 +++ .../customer-admin-panel/app/flags/page.tsx | 144 +++ .../app/flags/user/page.tsx | 152 +++ .../customer-admin-panel/app/globals.css | 77 ++ .../customer-admin-panel/app/layout.tsx | 18 + .../customer-admin-panel/app/page.tsx | 5 + .../customer-admin-panel/next-env.d.ts | 6 + .../customer-admin-panel/next.config.js | 7 + .../customer-admin-panel/package.json | 17 + .../customer-admin-panel/tsconfig.json | 32 + .../openapi-generator.config.yaml | 14 + packages/rest-api-sdk/openapi.json | 1 + packages/rest-api-sdk/openapitools.json | 7 + packages/rest-api-sdk/package.json | 41 + packages/rest-api-sdk/src/api.ts | 104 ++ .../src/generated/.openapi-generator-ignore | 23 + .../src/generated/.openapi-generator/FILES | 36 + .../src/generated/.openapi-generator/VERSION | 1 + .../src/generated/apis/DefaultApi.ts | 967 ++++++++++++++++++ .../rest-api-sdk/src/generated/apis/index.ts | 3 + packages/rest-api-sdk/src/generated/index.ts | 5 + .../rest-api-sdk/src/generated/models/App.ts | 167 +++ .../src/generated/models/AppHeader.ts | 119 +++ .../generated/models/AppHeaderCollection.ts | 74 ++ .../BulkUpdateFlagSpecificTargetsSchema.ts | 90 ++ .../src/generated/models/EntityFlag.ts | 165 +++ .../src/generated/models/EntityFlagUpdate.ts | 85 ++ .../generated/models/EntityFlagsResponse.ts | 101 ++ .../src/generated/models/Environment.ts | 110 ++ .../src/generated/models/EnvironmentHeader.ts | 93 ++ .../models/EnvironmentHeaderCollection.ts | 108 ++ .../models/EnvironmentHeaderSortByColumn.ts | 53 + .../generated/models/EnvironmentSdkAccess.ts | 75 ++ .../src/generated/models/ErrorResponse.ts | 82 ++ .../generated/models/ErrorResponseError.ts | 92 ++ .../src/generated/models/FlagHeader.ts | 174 ++++ .../generated/models/FlagHeaderCollection.ts | 146 +++ .../src/generated/models/FlagKeyFormat.ts | 58 ++ .../src/generated/models/FlagTargeting.ts | 101 ++ .../models/FlagTargetingCollection.ts | 74 ++ .../src/generated/models/FlagValue.ts | 52 + .../generated/models/FlagValueTargeting.ts | 75 ++ .../src/generated/models/OrgHeader.ts | 75 ++ .../src/generated/models/ReflagUserHeader.ts | 92 ++ .../src/generated/models/SegmentHeader.ts | 94 ++ .../src/generated/models/SegmentType.ts | 53 + .../src/generated/models/SortOrder.ts | 53 + .../src/generated/models/StageHeader.ts | 102 ++ .../generated/models/UpdateEntityFlagsBody.ts | 90 ++ .../models/UpdateFlagSpecificTargets.ts | 99 ++ .../models/UpdateFlagSpecificTargetsValue.ts | 46 + .../src/generated/models/index.ts | 32 + .../rest-api-sdk/src/generated/runtime.ts | 432 ++++++++ packages/rest-api-sdk/src/index.ts | 3 + packages/rest-api-sdk/tsconfig.build.json | 8 + packages/rest-api-sdk/tsconfig.eslint.json | 4 + packages/rest-api-sdk/tsconfig.json | 12 + reflag-openapi.json | 1 + yarn.lock | 836 ++++++++++++++- 66 files changed, 6327 insertions(+), 27 deletions(-) create mode 100644 .claude/settings.local.json create mode 100644 packages/rest-api-sdk/.prettierignore create mode 100644 packages/rest-api-sdk/README.md create mode 100644 packages/rest-api-sdk/eslint.config.js create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/README.md create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/app/flags/page.tsx create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/app/globals.css create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/app/layout.tsx create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/app/page.tsx create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/next-env.d.ts create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/next.config.js create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/package.json create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/tsconfig.json create mode 100644 packages/rest-api-sdk/openapi-generator.config.yaml create mode 100644 packages/rest-api-sdk/openapi.json create mode 100644 packages/rest-api-sdk/openapitools.json create mode 100644 packages/rest-api-sdk/package.json create mode 100644 packages/rest-api-sdk/src/api.ts create mode 100644 packages/rest-api-sdk/src/generated/.openapi-generator-ignore create mode 100644 packages/rest-api-sdk/src/generated/.openapi-generator/FILES create mode 100644 packages/rest-api-sdk/src/generated/.openapi-generator/VERSION create mode 100644 packages/rest-api-sdk/src/generated/apis/DefaultApi.ts create mode 100644 packages/rest-api-sdk/src/generated/apis/index.ts create mode 100644 packages/rest-api-sdk/src/generated/index.ts create mode 100644 packages/rest-api-sdk/src/generated/models/App.ts create mode 100644 packages/rest-api-sdk/src/generated/models/AppHeader.ts create mode 100644 packages/rest-api-sdk/src/generated/models/AppHeaderCollection.ts create mode 100644 packages/rest-api-sdk/src/generated/models/BulkUpdateFlagSpecificTargetsSchema.ts create mode 100644 packages/rest-api-sdk/src/generated/models/EntityFlag.ts create mode 100644 packages/rest-api-sdk/src/generated/models/EntityFlagUpdate.ts create mode 100644 packages/rest-api-sdk/src/generated/models/EntityFlagsResponse.ts create mode 100644 packages/rest-api-sdk/src/generated/models/Environment.ts create mode 100644 packages/rest-api-sdk/src/generated/models/EnvironmentHeader.ts create mode 100644 packages/rest-api-sdk/src/generated/models/EnvironmentHeaderCollection.ts create mode 100644 packages/rest-api-sdk/src/generated/models/EnvironmentHeaderSortByColumn.ts create mode 100644 packages/rest-api-sdk/src/generated/models/EnvironmentSdkAccess.ts create mode 100644 packages/rest-api-sdk/src/generated/models/ErrorResponse.ts create mode 100644 packages/rest-api-sdk/src/generated/models/ErrorResponseError.ts create mode 100644 packages/rest-api-sdk/src/generated/models/FlagHeader.ts create mode 100644 packages/rest-api-sdk/src/generated/models/FlagHeaderCollection.ts create mode 100644 packages/rest-api-sdk/src/generated/models/FlagKeyFormat.ts create mode 100644 packages/rest-api-sdk/src/generated/models/FlagTargeting.ts create mode 100644 packages/rest-api-sdk/src/generated/models/FlagTargetingCollection.ts create mode 100644 packages/rest-api-sdk/src/generated/models/FlagValue.ts create mode 100644 packages/rest-api-sdk/src/generated/models/FlagValueTargeting.ts create mode 100644 packages/rest-api-sdk/src/generated/models/OrgHeader.ts create mode 100644 packages/rest-api-sdk/src/generated/models/ReflagUserHeader.ts create mode 100644 packages/rest-api-sdk/src/generated/models/SegmentHeader.ts create mode 100644 packages/rest-api-sdk/src/generated/models/SegmentType.ts create mode 100644 packages/rest-api-sdk/src/generated/models/SortOrder.ts create mode 100644 packages/rest-api-sdk/src/generated/models/StageHeader.ts create mode 100644 packages/rest-api-sdk/src/generated/models/UpdateEntityFlagsBody.ts create mode 100644 packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargets.ts create mode 100644 packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargetsValue.ts create mode 100644 packages/rest-api-sdk/src/generated/models/index.ts create mode 100644 packages/rest-api-sdk/src/generated/runtime.ts create mode 100644 packages/rest-api-sdk/src/index.ts create mode 100644 packages/rest-api-sdk/tsconfig.build.json create mode 100644 packages/rest-api-sdk/tsconfig.eslint.json create mode 100644 packages/rest-api-sdk/tsconfig.json create mode 100644 reflag-openapi.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 00000000..789e1b62 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,17 @@ +{ + "permissions": { + "allow": [ + "WebFetch(domain:raw.githubusercontent.com)", + "Bash(yarn install)", + "Bash(yarn generate)", + "WebFetch(domain:heyapi.dev)", + "Bash(yarn build)", + "Bash(yarn tsc:*)", + "Bash(yarn format:*)", + "Bash(yarn lint)", + "Bash(yarn prettier)", + "Bash(yarn prettier:*)", + "Bash(NODE_TLS_REJECT_UNAUTHORIZED=0 yarn build:*)" + ] + } +} diff --git a/package.json b/package.json index a4ba6605..9d268d5c 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "license": "MIT", "workspaces": [ "packages/*", + "packages/rest-api-sdk/examples/*", "packages/react-sdk/dev/*", "packages/openfeature-browser-provider/example" ], diff --git a/packages/rest-api-sdk/.prettierignore b/packages/rest-api-sdk/.prettierignore new file mode 100644 index 00000000..1ea6743a --- /dev/null +++ b/packages/rest-api-sdk/.prettierignore @@ -0,0 +1,2 @@ +dist/ +src/generated/ diff --git a/packages/rest-api-sdk/README.md b/packages/rest-api-sdk/README.md new file mode 100644 index 00000000..0a8b4792 --- /dev/null +++ b/packages/rest-api-sdk/README.md @@ -0,0 +1,220 @@ +# @reflag/rest-api-sdk + +Type-safe REST API client for the Reflag API. + +## Installation + +```bash +npm install @reflag/rest-api-sdk +# or +yarn add @reflag/rest-api-sdk +``` + +## Quick Start + +```typescript +import { Api } from "@reflag/rest-api-sdk"; + +const api = new Api({ + accessToken: process.env.REFLAG_API_KEY, +}); + +const apps = await api.listApps(); +console.log(apps.data); +``` + +```typescript +import { createAppClient } from "@reflag/rest-api-sdk"; + +// App-scoped client keeps appId out of each call. +const appApi = createAppClient("app-123", { + accessToken: process.env.REFLAG_API_KEY, +}); + +const environments = await appApi.listEnvironments({ + sortBy: "order", + sortOrder: "asc", +}); +console.log(environments.data); +``` + +## Authentication + +All API requests require a Bearer token. Get your API key from the Reflag dashboard. + +```typescript +import { Api } from "@reflag/rest-api-sdk"; + +const api = new Api({ + accessToken: "your-api-key", +}); +``` + +## API Methods + +### Applications + +```typescript +import { Api } from "@reflag/rest-api-sdk"; + +const api = new Api(); + +// List all apps +const apps = await api.listApps(); + +// Filter by organization +const appsByOrg = await api.listApps({ orgId: "org-123" }); + +// Get a single app +const app = await api.getApp({ + appId: "app-123", +}); +``` + +### Environments + +```typescript +import { createAppClient } from "@reflag/rest-api-sdk"; + +const appApi = createAppClient("app-123"); + +// App-scoped client (appId is implicit) +const environments = await appApi.listEnvironments({ + sortBy: "order", + sortOrder: "asc", +}); + +const environment = await appApi.getEnvironment({ + envId: "env-456", +}); +``` + +You can also pass `appId` with every call instead of using `createAppClient`: + +```typescript +const environments = await api.listEnvironments({ + appId: "app-123", + sortBy: "order", + sortOrder: "asc", +}); + +const environment = await api.getEnvironment({ + appId: "app-123", + envId: "env-456", +}); +``` + +### Flags + +```typescript +const flags = await api.listFlags({ appId: "app-123" }); + +const targeting = await api.getFlagTargeting({ + appId: "app-123", + flagKey: "my-feature-flag", + envId: "env-456", +}); + +const updated = await api.updateBulkFlagSpecificTargets({ + appId: "app-123", + envId: "env-456", + bulkUpdateFlagSpecificTargetsSchema: { + updates: [ + { flagKey: "new-feature", value: true, companyId: "company-1" }, + { flagKey: "new-feature", value: true, userId: "user-1" }, + { flagKey: "old-feature", value: null, companyId: "company-1" }, + ], + notifications: true, + changeDescription: "Enabling new feature for beta testers", + }, +}); +``` + +### Company Flags + +```typescript +const companyFlags = await api.getCompanyFlags({ + appId: "app-123", + companyId: "company-1", + envId: "env-456", +}); + +const updatedCompanyFlags = await api.updateCompanyFlags({ + appId: "app-123", + companyId: "company-1", + envId: "env-456", + updateEntityFlagsBody: { + flags: { + "feature-flag": true, + "another-flag": false, + }, + }, +}); +``` + +### User Flags + +```typescript +const userFlags = await api.getUserFlags({ + appId: "app-123", + userId: "user-1", + envId: "env-456", +}); + +const updatedUserFlags = await api.updateUserFlags({ + appId: "app-123", + userId: "user-1", + envId: "env-456", + updateEntityFlagsBody: { + flags: { + "feature-flag": true, + "beta-feature": true, + }, + }, +}); +``` + +## Error Handling + +Methods throw on non-2xx responses. Catch errors and inspect the response when needed. + +```typescript +try { + const apps = await api.listApps(); + console.log(apps.data); +} catch (error) { + if (error instanceof Error) { + console.error("Request failed", error.message); + } + throw error; +} +``` + +## Types + +All generated types are exported for use in your application: + +```typescript +import type { + AppHeader, + EnvironmentHeader, + EnvironmentDetails, + FlagHeader, + FlagTargeting, + ErrorResponse, +} from "@reflag/rest-api-sdk"; +``` + +## Regenerating the SDK + +To regenerate the SDK: + +```bash +yarn generate +``` + +The schema source lives at `https://app.reflag.com/openapi.json`. + +## License + +MIT diff --git a/packages/rest-api-sdk/eslint.config.js b/packages/rest-api-sdk/eslint.config.js new file mode 100644 index 00000000..85c50578 --- /dev/null +++ b/packages/rest-api-sdk/eslint.config.js @@ -0,0 +1,8 @@ +const base = require("@reflag/eslint-config"); + +module.exports = [ + ...base, + { + ignores: ["dist/", "src/generated/", "examples/**", "**/.next/**"], + }, +]; diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/README.md b/packages/rest-api-sdk/examples/customer-admin-panel/README.md new file mode 100644 index 00000000..fbfe9de8 --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/README.md @@ -0,0 +1,23 @@ +# Customer Admin Panel + +Small Next.js (App Router) app that uses `@reflag/rest-api-sdk` with server actions to view and toggle flags for users and companies. + +## Setup + +Create a `.env.local` file in this folder with: + +``` +REFLAG_API_KEY=your-api-key +# Optional +REFLAG_BASE_URL=https://app.reflag.com/api +``` + +## Run + +```bash +yarn dev +``` + +Visit: +- http://localhost:3000/flags/user +- http://localhost:3000/flags/company diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts new file mode 100644 index 00000000..184d6701 --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts @@ -0,0 +1,136 @@ +"use server"; + +import type { + AppHeaderCollection, + EnvironmentHeaderCollection, + EntityFlagsResponse, + FlagHeaderCollection, +} from "@reflag/rest-api-sdk"; +import { Api, createAppClient } from "@reflag/rest-api-sdk"; + +const apiKey = process.env.REFLAG_API_KEY; +const basePath = process.env.REFLAG_BASE_URL ?? "https://app.reflag.com/api"; + +function getClient() { + if (!apiKey) { + throw new Error("REFLAG_API_KEY is not set"); + } + return new Api({ + accessToken: apiKey, + basePath, + }); +} + +function getAppClient(appId: string) { + if (!appId) { + throw new Error("appId is required"); + } + return createAppClient(appId, { + accessToken: apiKey ?? "", + basePath, + }); +} + +export async function listApps(): Promise { + const client = getClient(); + return await client.listApps(); +} + +export async function listEnvironments( + appId: string, +): Promise { + const client = getAppClient(appId); + return await client.listEnvironments({ + sortBy: "order", + sortOrder: "asc", + }); +} + +export async function listFlags(appId: string): Promise { + const client = getAppClient(appId); + return await client.listFlags({}); +} + +export async function fetchUserFlags( + appId: string, + envId: string, + userId: string, +): Promise { + if (!envId) { + throw new Error("envId is required"); + } + if (!userId) { + throw new Error("userId is required"); + } + const client = getAppClient(appId); + return await client.getUserFlags({ + envId, + userId, + }); +} + +export async function toggleUserFlag( + appId: string, + envId: string, + userId: string, + flagKey: string, + enabled: boolean, +): Promise { + if (!envId) { + throw new Error("envId is required"); + } + if (!userId || !flagKey) { + throw new Error("userId and flagKey are required"); + } + const client = getAppClient(appId); + await client.updateUserFlags({ + envId, + userId, + updateEntityFlagsBody: { + updates: [{ flagKey, value: enabled ? true : null }], + }, + }); + return await fetchUserFlags(appId, envId, userId); +} + +export async function fetchCompanyFlags( + appId: string, + envId: string, + companyId: string, +): Promise { + if (!envId) { + throw new Error("envId is required"); + } + if (!companyId) { + throw new Error("companyId is required"); + } + const client = getAppClient(appId); + return await client.getCompanyFlags({ + envId, + companyId, + }); +} + +export async function toggleCompanyFlag( + appId: string, + envId: string, + companyId: string, + flagKey: string, + enabled: boolean, +): Promise { + if (!envId) { + throw new Error("envId is required"); + } + if (!companyId || !flagKey) { + throw new Error("companyId and flagKey are required"); + } + const client = getAppClient(appId); + await client.updateCompanyFlags({ + envId, + companyId, + updateEntityFlagsBody: { + updates: [{ flagKey, value: enabled ? true : null }], + }, + }); + return await fetchCompanyFlags(appId, envId, companyId); +} diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx new file mode 100644 index 00000000..4af0a290 --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx @@ -0,0 +1,161 @@ +import Link from "next/link"; +import { redirect } from "next/navigation"; +import { + fetchCompanyFlags, + listApps, + listEnvironments, + toggleCompanyFlag, +} from "../actions"; + +type QueryValue = string | string[] | undefined; + +type PageProps = { + searchParams?: { + appId?: QueryValue; + envId?: QueryValue; + companyId?: QueryValue; + }; +}; + +function getQueryValue(value: QueryValue) { + return Array.isArray(value) ? (value[0] ?? "") : (value ?? ""); +} + +export default async function CompanyFlagsPage({ searchParams }: PageProps) { + const apps = (await listApps()).data ?? []; + const requestedAppId = getQueryValue(searchParams?.appId); + const selectedAppId = + apps.find((app) => app.id === requestedAppId)?.id ?? apps[0]?.id ?? ""; + + const envs = selectedAppId + ? ((await listEnvironments(selectedAppId)).data ?? []) + : []; + + const requestedEnvId = getQueryValue(searchParams?.envId); + const selectedEnvId = + envs.find((env) => env.id === requestedEnvId)?.id ?? envs[0]?.id ?? ""; + + const companyId = getQueryValue(searchParams?.companyId); + + const flags = + selectedAppId && selectedEnvId && companyId + ? (await fetchCompanyFlags(selectedAppId, selectedEnvId, companyId)).data ?? [] + : []; + + async function updateCompanyFlagAction(formData: FormData) { + "use server"; + + const appId = String(formData.get("appId") ?? ""); + const envId = String(formData.get("envId") ?? ""); + const nextCompanyId = String(formData.get("companyId") ?? ""); + const flagKey = String(formData.get("flagKey") ?? ""); + const nextValue = String(formData.get("nextValue") ?? "") === "true"; + + await toggleCompanyFlag(appId, envId, nextCompanyId, flagKey, nextValue); + + const query = new URLSearchParams({ appId, envId, companyId: nextCompanyId }); + redirect(`/flags/company?${query.toString()}`); + } + + return ( +
+ Back to flags +

Company Flags

+ +
+ + + +
+ + +
+
+ + {!companyId ?

Enter a company ID to load flags.

: null} + {companyId && flags.length === 0 ?

No flags loaded.

: null} + + {flags.length > 0 ? ( + + + + + + + + + + + + {flags.map((flag) => { + const isInherited = flag.value && flag.specificallyTargetedValue === null; + const canToggle = !isInherited; + const nextValue = !flag.value; + + return ( + + + + + + + + ); + })} + +
FlagKeyEnabledStatusAction
{flag.name}{flag.key} + + {isInherited ? "Inherited" : "Targeted"} +
+ + + + + + +
+
+ ) : null} +
+ ); +} diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/page.tsx new file mode 100644 index 00000000..6b702fba --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/page.tsx @@ -0,0 +1,144 @@ +import Link from "next/link"; +import { listApps, listEnvironments, listFlags } from "./actions"; + +type QueryValue = string | string[] | undefined; + +type PageProps = { + searchParams?: { + appId?: QueryValue; + envId?: QueryValue; + }; +}; + +function getQueryValue(value: QueryValue) { + return Array.isArray(value) ? (value[0] ?? "") : (value ?? ""); +} + +export default async function FlagsPage({ searchParams }: PageProps) { + const apps = (await listApps()).data ?? []; + const requestedAppId = getQueryValue(searchParams?.appId); + const selectedAppId = + apps.find((app) => app.id === requestedAppId)?.id ?? apps[0]?.id ?? ""; + + const envs = selectedAppId + ? ((await listEnvironments(selectedAppId)).data ?? []) + : []; + + const requestedEnvId = getQueryValue(searchParams?.envId); + const selectedEnvId = + envs.find((env) => env.id === requestedEnvId)?.id ?? envs[0]?.id ?? ""; + + const flags = selectedAppId ? (await listFlags(selectedAppId)).data ?? [] : []; + + return ( +
+

Flags

+ +
+ + +
+ +
+
+ +
+
+ + + + +
+ +
+ + + + +
+
+ + {flags.length === 0 ?

No flags loaded.

: null} + {flags.length > 0 ? ( + + + + + + + + + + {flags.map((flag) => ( + + + + + + ))} + +
FlagKeyOpen
{flag.name}{flag.key} + + User + + {" / "} + + Company + +
+ ) : null} +
+ ); +} diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx new file mode 100644 index 00000000..1953edce --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx @@ -0,0 +1,152 @@ +import Link from "next/link"; +import { redirect } from "next/navigation"; +import { fetchUserFlags, listApps, listEnvironments, toggleUserFlag } from "../actions"; + +type QueryValue = string | string[] | undefined; + +type PageProps = { + searchParams?: { + appId?: QueryValue; + envId?: QueryValue; + userId?: QueryValue; + }; +}; + +function getQueryValue(value: QueryValue) { + return Array.isArray(value) ? (value[0] ?? "") : (value ?? ""); +} + +export default async function UserFlagsPage({ searchParams }: PageProps) { + const apps = (await listApps()).data ?? []; + const requestedAppId = getQueryValue(searchParams?.appId); + const selectedAppId = + apps.find((app) => app.id === requestedAppId)?.id ?? apps[0]?.id ?? ""; + + const envs = selectedAppId + ? ((await listEnvironments(selectedAppId)).data ?? []) + : []; + + const requestedEnvId = getQueryValue(searchParams?.envId); + const selectedEnvId = + envs.find((env) => env.id === requestedEnvId)?.id ?? envs[0]?.id ?? ""; + + const userId = getQueryValue(searchParams?.userId); + + const flags = + selectedAppId && selectedEnvId && userId + ? (await fetchUserFlags(selectedAppId, selectedEnvId, userId)).data ?? [] + : []; + + async function updateUserFlagAction(formData: FormData) { + "use server"; + + const appId = String(formData.get("appId") ?? ""); + const envId = String(formData.get("envId") ?? ""); + const nextUserId = String(formData.get("userId") ?? ""); + const flagKey = String(formData.get("flagKey") ?? ""); + const nextValue = String(formData.get("nextValue") ?? "") === "true"; + + await toggleUserFlag(appId, envId, nextUserId, flagKey, nextValue); + + const query = new URLSearchParams({ appId, envId, userId: nextUserId }); + redirect(`/flags/user?${query.toString()}`); + } + + return ( +
+ Back to flags +

User Flags

+ +
+ + + +
+ + +
+
+ + {!userId ?

Enter a user ID to load flags.

: null} + {userId && flags.length === 0 ?

No flags loaded.

: null} + + {flags.length > 0 ? ( + + + + + + + + + + + + {flags.map((flag) => { + const isInherited = flag.value && flag.specificallyTargetedValue === null; + const canToggle = !isInherited; + const nextValue = !flag.value; + + return ( + + + + + + + + ); + })} + +
FlagKeyEnabledStatusAction
{flag.name}{flag.key} + + {isInherited ? "Inherited" : "Targeted"} +
+ + + + + + +
+
+ ) : null} +
+ ); +} diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/globals.css b/packages/rest-api-sdk/examples/customer-admin-panel/app/globals.css new file mode 100644 index 00000000..e071fa0d --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/globals.css @@ -0,0 +1,77 @@ +:root { + color-scheme: light; + font-family: "Inter", "SF Pro Text", system-ui, -apple-system, sans-serif; + line-height: 1.5; + background: #f7f7f8; + color: #111827; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + background: #f7f7f8; +} + +main { + background: #ffffff; + border: 1px solid #e5e7eb; + border-radius: 16px; + box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06); +} + +h1 { + margin-top: 0; +} + +label span { + font-size: 0.9rem; + color: #4b5563; +} + +input, +select, +button { + font: inherit; + border-radius: 10px; + border: 1px solid #d1d5db; + padding: 8px 12px; +} + +button { + background: #111827; + color: #ffffff; + cursor: pointer; +} + +button:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +a { + color: #111827; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +table { + width: 100%; + border-collapse: collapse; +} + +thead th { + font-size: 0.85rem; + text-transform: uppercase; + letter-spacing: 0.04em; + color: #6b7280; +} + +tbody tr:nth-child(even) { + background: #f9fafb; +} diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/layout.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/layout.tsx new file mode 100644 index 00000000..287bc98a --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/layout.tsx @@ -0,0 +1,18 @@ +import "./globals.css"; + +export const metadata = { + title: "Customer Admin Panel", + description: "Customer admin panel example app powered by the Reflag REST API SDK", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/page.tsx new file mode 100644 index 00000000..b73b4f49 --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/page.tsx @@ -0,0 +1,5 @@ +import FlagsPage from "./flags/page"; + +export default function Home() { + return ; +} diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/next-env.d.ts b/packages/rest-api-sdk/examples/customer-admin-panel/next-env.d.ts new file mode 100644 index 00000000..fd36f949 --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/next.config.js b/packages/rest-api-sdk/examples/customer-admin-panel/next.config.js new file mode 100644 index 00000000..22a21b45 --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/next.config.js @@ -0,0 +1,7 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + transpilePackages: ["@reflag/rest-api-sdk"], + reactStrictMode: true, +}; + +module.exports = nextConfig; diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/package.json b/packages/rest-api-sdk/examples/customer-admin-panel/package.json new file mode 100644 index 00000000..2282b7a7 --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/package.json @@ -0,0 +1,17 @@ +{ + "name": "customer-admin-panel", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@reflag/rest-api-sdk": "workspace:*", + "next": "14.2.5", + "react": "18.3.1", + "react-dom": "18.3.1" + } +} diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/tsconfig.json b/packages/rest-api-sdk/examples/customer-admin-panel/tsconfig.json new file mode 100644 index 00000000..0fb5969b --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/tsconfig.json @@ -0,0 +1,32 @@ +{ + "extends": "@reflag/tsconfig/library", + "compilerOptions": { + "lib": [ + "ES2020", + "DOM" + ], + "module": "ESNext", + "jsx": "preserve", + "noEmit": true, + "moduleResolution": "Bundler", + "allowJs": true, + "skipLibCheck": true, + "incremental": true, + "isolatedModules": true, + "plugins": [ + { + "name": "next" + } + ], + "strictNullChecks": true + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/packages/rest-api-sdk/openapi-generator.config.yaml b/packages/rest-api-sdk/openapi-generator.config.yaml new file mode 100644 index 00000000..9ab6ecf9 --- /dev/null +++ b/packages/rest-api-sdk/openapi-generator.config.yaml @@ -0,0 +1,14 @@ +generatorName: typescript-fetch +inputSpec: ./openapi.json +outputDir: src/generated +additionalProperties: + supportsES6: true + typescriptThreePlus: true + useSingleRequestParameter: true + withInterfaces: true + useTags: false +globalProperties: + apiDocs: false + modelDocs: false + apiTests: false + modelTests: false diff --git a/packages/rest-api-sdk/openapi.json b/packages/rest-api-sdk/openapi.json new file mode 100644 index 00000000..25f8eff9 --- /dev/null +++ b/packages/rest-api-sdk/openapi.json @@ -0,0 +1 @@ +{"openapi":"3.1.0","info":{"title":"Reflag API","version":"2.0.0","description":"Feature flag management API","contact":{"name":"Reflag Support","url":"https://reflag.com/support"},"license":{"name":"Proprietary","url":"https://reflag.com/"}},"servers":[{"url":"https://app.reflag.com/api","description":"Production server"}],"security":[{"APIKey":[]}],"paths":{"/apps/{appId}":{"get":{"summary":"Get details of an application","description":"Retrieve a specific application by its identifier","operationId":"getApp","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps":{"get":{"summary":"List of applications","description":"Retrieve all accessible applications","operationId":"listApps","parameters":[{"in":"query","name":"orgId","schema":{"$ref":"#/components/schemas/orgId"}}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/appHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/environments":{"get":{"summary":"List environments for application","description":"Retrieve all environments for a specific application","operationId":"listEnvironments","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"query","name":"sortOrder","schema":{"$ref":"#/components/schemas/sortOrder"},"description":"Sort order applied to the sorting column"},{"in":"query","name":"sortBy","schema":{"$ref":"#/components/schemas/environmentHeaderSortByColumn"},"description":"The column to sort by"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/environmentHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/environments/{envId}":{"get":{"summary":"Get environment details","description":"Retrieve details for a specific environment","operationId":"getEnvironment","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/environment"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags":{"get":{"summary":"List flags for application","description":"Retrieve all flags for a specific application","operationId":"listFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/{flagKey}/targeting/{envId}":{"get":{"summary":"Get flag targeting for an environment","description":"Retrieve targeting for a flag in an environment","operationId":"getFlagTargeting","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"flagKey","schema":{"$ref":"#/components/schemas/flagKey"},"required":true,"description":"Unique flag key"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagTargeting"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/specific-targets/{envId}":{"patch":{"summary":"Update flag specific targets for an environment","description":"Update specific companies and users for flags in an environment","operationId":"updateBulkFlagSpecificTargets","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/bulkUpdateFlagSpecificTargetsSchema"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagTargetingCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/companies/{companyId}/flags":{"get":{"summary":"Get flags for a company","description":"Retrieve all flags with their targeting status for a specific company","operationId":"getCompanyFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"companyId","schema":{"$ref":"#/components/schemas/companyId"},"required":true,"description":"Company ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Update flag targeting for a company","description":"Update specific targeting for flags for a company in an environment","operationId":"updateCompanyFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"companyId","schema":{"$ref":"#/components/schemas/companyId"},"required":true,"description":"Company ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/updateEntityFlagsBody"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/users/{userId}/flags":{"get":{"summary":"Get flags for a user","description":"Retrieve all flags with their targeting status for a specific user","operationId":"getUserFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"userId","schema":{"$ref":"#/components/schemas/userId"},"required":true,"description":"User ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Update flag targeting for a user","description":"Update specific targeting for flags for a user in an environment","operationId":"updateUserFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"userId","schema":{"$ref":"#/components/schemas/userId"},"required":true,"description":"User ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/updateEntityFlagsBody"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"appId":{"description":"App identifier","type":"string","minLength":1},"orgId":{"description":"Organization identifier","type":"string","minLength":1},"sortOrder":{"description":"Sort order applied to the sorting column","default":"asc","type":"string","enum":["asc","desc"]},"environmentHeaderSortByColumn":{"description":"The column to sort by","default":"order","type":"string","enum":["name","order"]},"envId":{"description":"Environment identifier","type":"string","minLength":1},"flagKey":{"description":"Unique flag key","type":"string","minLength":1},"bulkUpdateFlagSpecificTargetsSchema":{"description":"Update the explicit value of multiple flags for a given audience","type":"object","properties":{"updates":{"description":"The list of updates to make to the flags' targeting","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/updateFlagSpecificTargets"}},"notifications":{"description":"Whether to send notifications about the change to configured integration (eg. Slack, Linear, etc). Defaults to true.","default":true,"type":"boolean"},"changeDescription":{"description":"The description of the change recorded in the change history","type":"string"}},"required":["updates"]},"updateFlagSpecificTargets":{"description":"Update the explicit value of the flag for a given audience (null means to remove the flag from the audience).","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"value":{"description":"The value of the flag for the given audience (null means to remove the flag from the audience).","anyOf":[{"type":"string","const":"true"},{"type":"boolean","const":true},{"type":"null"}]},"companyId":{"$ref":"#/components/schemas/companyId"},"userId":{"$ref":"#/components/schemas/userId"}},"required":["flagKey","value"]},"companyId":{"description":"Company ID within your application","type":"string","minLength":1},"userId":{"description":"User ID within your application","type":"string","minLength":1},"updateEntityFlagsBody":{"description":"Request body for updating flags for an entity","type":"object","properties":{"updates":{"description":"List of flag updates to apply","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/entityFlagUpdate"}},"changeDescription":{"description":"Description of the change for audit history","type":"string"},"notifications":{"description":"Whether to send notifications about the change (default: true)","default":true,"type":"boolean"}},"required":["updates"]},"entityFlagUpdate":{"description":"Update for a single flag's specific targeting","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"value":{"description":"Set to true to add specific targeting, or null to remove it","anyOf":[{"type":"boolean","const":true},{"type":"null"}]}},"required":["flagKey","value"]},"app":{"description":"App information with related collections","type":"object","properties":{"org":{"$ref":"#/components/schemas/orgHeader"},"id":{"$ref":"#/components/schemas/appId"},"name":{"description":"App name","type":"string"},"demo":{"description":"Whether the app is a demo app","type":"boolean"},"flagKeyFormat":{"$ref":"#/components/schemas/flagKeyFormat"},"environments":{"description":"Environments within the app","type":"array","items":{"$ref":"#/components/schemas/environment"}},"stages":{"description":"Stages within the app","type":"array","items":{"$ref":"#/components/schemas/stageHeader"}},"segments":{"description":"Segments within the app","type":"array","items":{"$ref":"#/components/schemas/segmentHeader"}}},"required":["org","id","name","demo","flagKeyFormat","environments","stages","segments"],"additionalProperties":false},"orgHeader":{"description":"Organization's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/orgId"},"name":{"description":"Organization name","type":"string","minLength":1}},"required":["id","name"],"additionalProperties":false},"flagKeyFormat":{"description":"The enforced key format when creating flags","type":"string","enum":["custom","pascalCase","camelCase","snakeCaseUpper","snakeCaseLower","kebabCaseUpper","kebabCaseLower"]},"environment":{"description":"Environment details","type":"object","properties":{"id":{"$ref":"#/components/schemas/envId"},"name":{"description":"Environment name","type":"string"},"isProduction":{"description":"Whether the environment is a production environment","type":"boolean"},"order":{"description":"Environment order in the app (zero-indexed)","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sdkAccess":{"description":"SDK access details","type":"object","properties":{"publishableKey":{"description":"Publishable key","type":"string","minLength":1,"maxLength":36},"secretKey":{"description":"Secret key","type":"string","minLength":1,"maxLength":36}},"required":["publishableKey","secretKey"],"additionalProperties":false}},"required":["id","name","isProduction","order","sdkAccess"],"additionalProperties":false},"stageHeader":{"description":"Stage's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/stageId"},"name":{"description":"Stage name","type":"string","minLength":1},"color":{"description":"Stage color (HTML color name or hex code)","type":"string","minLength":1,"maxLength":64},"assignedFlagCount":{"description":"Flags currently in this stage","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"order":{"description":"Stage order","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name","color","assignedFlagCount","order"],"additionalProperties":false},"stageId":{"description":"Stage identifier","type":"string","minLength":1},"segmentHeader":{"description":"Segment's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/segmentId"},"name":{"description":"Segment name","type":"string","minLength":1},"type":{"$ref":"#/components/schemas/segmentType"}},"required":["id","name","type"],"additionalProperties":false},"segmentId":{"description":"Segment identifier","type":"string","minLength":1},"segmentType":{"description":"Segment type","type":"string","enum":["all","custom"]},"ErrorResponse":{"description":"The error response, including individual issues, if applicable","type":"object","properties":{"error":{"description":"The error","type":"object","properties":{"code":{"description":"Error code","type":"string","enum":["invalid_request","not_found","not_possible","not_allowed","not_available","unknown_error","unauthorized","unauthenticated"]},"message":{"description":"Human readable error message","type":"string"}},"required":["code","message"],"additionalProperties":false},"issues":{"description":"Individual validation issues, if applicable","type":"object","propertyNames":{"description":"The field that has the issue (uses dot notation). Empty string if the issue is at the root.","type":"string"},"additionalProperties":{"description":"Error messages for this field","type":"array","items":{"description":"The error message","type":"string"}}}},"required":["error"],"additionalProperties":false},"appHeaderCollection":{"description":"Collection of Basic app information","type":"object","properties":{"data":{"description":"The individual items in the collection","type":"array","items":{"$ref":"#/components/schemas/appHeader"}}},"required":["data"],"additionalProperties":false},"appHeader":{"description":"Basic app information","type":"object","properties":{"org":{"$ref":"#/components/schemas/orgHeader"},"id":{"$ref":"#/components/schemas/appId"},"name":{"description":"App name","type":"string"},"demo":{"description":"Whether the app is a demo app","type":"boolean"},"flagKeyFormat":{"$ref":"#/components/schemas/flagKeyFormat"}},"required":["org","id","name","demo","flagKeyFormat"],"additionalProperties":false},"environmentHeaderCollection":{"description":"Collection of Basic environment information","type":"object","properties":{"data":{"description":"The individual items in the collection","type":"array","items":{"$ref":"#/components/schemas/environmentHeader"}},"sortOrder":{"$ref":"#/components/schemas/sortOrder"},"sortBy":{"$ref":"#/components/schemas/environmentHeaderSortByColumn"}},"required":["data","sortOrder","sortBy"],"additionalProperties":false},"environmentHeader":{"description":"Basic environment information","type":"object","properties":{"id":{"$ref":"#/components/schemas/envId"},"name":{"description":"Environment name","type":"string"},"isProduction":{"description":"Whether the environment is a production environment","type":"boolean"},"order":{"description":"Environment order in the app (zero-indexed)","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name","isProduction","order"],"additionalProperties":false},"flagHeaderCollection":{"description":"Collection response containing flags","type":"object","properties":{"data":{"description":"Page of the collection of flags","type":"array","items":{"$ref":"#/components/schemas/flagHeader"}},"totalCount":{"description":"Total number of flags in collection","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageSize":{"description":"Page size","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageIndex":{"description":"Page index","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sortBy":{"description":"Sort by","type":"string","enum":["name","key","stage","autoFeedbackSurveysEnabled","createdAt","environmentStatus","owner","lastCheck","lastTrack","stale","archivingChecks"]},"sortOrder":{"description":"Sort order","$ref":"#/components/schemas/sortOrder"}},"required":["data","totalCount","pageSize","pageIndex","sortBy","sortOrder"],"additionalProperties":false},"flagHeader":{"description":"Basic flag information","type":"object","properties":{"id":{"description":"Flag ID","type":"string","minLength":1},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"description":{"description":"Flag description","type":"string"},"stage":{"$ref":"#/components/schemas/stageHeader"},"owner":{"$ref":"#/components/schemas/reflagUserHeader"},"archived":{"description":"Whether the flag is archived","type":"boolean"},"stale":{"description":"Whether the flag is stale","type":"boolean"},"permanent":{"description":"Whether the flag is permanent","type":"boolean"},"createdAt":{"description":"Timestamp when the flag was created","type":"string"},"lastCheckAt":{"description":"Timestamp when the flag was last checked","type":"string"},"lastTrackAt":{"description":"Timestamp when the flag was last tracked","type":"string"}},"required":["id","key","name","archived","stale","permanent"],"additionalProperties":false},"reflagUserHeader":{"description":"Reflag user's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/reflagUserId"},"name":{"description":"User's name","type":"string","minLength":1},"email":{"description":"User's email","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"avatarUrl":{"description":"User's avatar URL","type":"string","format":"uri"}},"required":["id","name","email"],"additionalProperties":false},"reflagUserId":{"description":"Reflag user identifier","type":"string","minLength":1},"flagTargeting":{"description":"Flag targeting information and its audience","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"version":{"$ref":"#/components/schemas/flagVersion"},"updatedAt":{"description":"Last time the targeting was updated","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"specificTargets":{"description":"The flag targeting for each value","type":"object","propertyNames":{"$ref":"#/components/schemas/flagValue"},"additionalProperties":{"$ref":"#/components/schemas/flagValueTargeting"}}},"required":["flagKey","version","updatedAt","specificTargets"],"additionalProperties":false},"flagVersion":{"description":"Flag targeting version","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"flagValue":{"description":"The value of the flag served to the audience.","type":"string","const":"true"},"flagValueTargeting":{"description":"Flag targeting value and its audience","type":"object","properties":{"companyIds":{"description":"Companies that were explicitly given the value","type":"array","items":{"$ref":"#/components/schemas/companyId"}},"userIds":{"description":"Users that were explicitly given the value","type":"array","items":{"$ref":"#/components/schemas/userId"}}},"required":["companyIds","userIds"],"additionalProperties":false},"flagTargetingCollection":{"description":"Collection response containing flags' targeting information","type":"object","properties":{"data":{"description":"Collection of flags' targeting information","type":"array","items":{"$ref":"#/components/schemas/flagTargeting"}}},"required":["data"],"additionalProperties":false},"entityFlagsResponse":{"description":"Response containing flags for an entity","type":"object","properties":{"data":{"description":"List of flags with their enabled status","type":"array","items":{"$ref":"#/components/schemas/entityFlag"}},"totalCount":{"description":"Total number of flags","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageSize":{"description":"Page size","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageIndex":{"description":"Page index","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["data","totalCount","pageSize","pageIndex"],"additionalProperties":false},"entityFlag":{"description":"Flag information with enabled status for an entity","type":"object","properties":{"id":{"description":"Flag ID","type":"string","minLength":1},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"value":{"description":"Whether the flag is enabled for this entity","type":"boolean"},"specificallyTargetedValue":{"description":"Value if directly added via specific targets, null if not specifically targeted","anyOf":[{"type":"boolean"},{"type":"null"}]},"firstExposureAt":{"description":"First time the entity was exposed to this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastExposureAt":{"description":"Last time the entity was exposed to this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastCheckAt":{"description":"Last time the flag was checked for this entity","anyOf":[{"type":"string"},{"type":"null"}]},"exposureCount":{"description":"Number of times the entity was exposed to this flag","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"firstTrackAt":{"description":"First time a track event was recorded for this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastTrackAt":{"description":"Last time a track event was recorded for this flag","anyOf":[{"type":"string"},{"type":"null"}]},"trackCount":{"description":"Number of track events for this flag","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","key","name","value","specificallyTargetedValue","firstExposureAt","lastExposureAt","lastCheckAt","exposureCount","firstTrackAt","lastTrackAt","trackCount"],"additionalProperties":false}},"securitySchemes":{"APIKey":{"type":"http","scheme":"bearer","description":"API key authentication, for service access"}}}} \ No newline at end of file diff --git a/packages/rest-api-sdk/openapitools.json b/packages/rest-api-sdk/openapitools.json new file mode 100644 index 00000000..dae25538 --- /dev/null +++ b/packages/rest-api-sdk/openapitools.json @@ -0,0 +1,7 @@ +{ + "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "7.19.0" + } +} diff --git a/packages/rest-api-sdk/package.json b/packages/rest-api-sdk/package.json new file mode 100644 index 00000000..d2546682 --- /dev/null +++ b/packages/rest-api-sdk/package.json @@ -0,0 +1,41 @@ +{ + "name": "@reflag/rest-api-sdk", + "version": "0.0.1", + "description": "Reflag REST API SDK", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/reflagcom/javascript.git" + }, + "scripts": { + "generate": "rm -rf src/generated && openapi-generator-cli generate -c openapi-generator.config.yaml", + "build": "yarn generate && tsc --project tsconfig.build.json", + "test": "vitest run", + "test:watch": "vitest", + "test:ci": "yarn test --reporter=default --reporter=junit --outputFile=junit.xml", + "lint": "eslint .", + "lint:ci": "eslint --max-warnings=0 --output-file eslint-report.json --format json .", + "prettier": "prettier --check .", + "format": "yarn lint --fix && yarn prettier --write", + "preversion": "yarn lint && yarn prettier && yarn test && yarn build" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "main": "./dist/index.js", + "types": "./dist/types/index.d.ts", + "devDependencies": { + "@openapitools/openapi-generator-cli": "^2.13.5", + "@reflag/eslint-config": "~0.0.2", + "@reflag/tsconfig": "~0.0.2", + "@types/node": "^22.12.0", + "@vitest/coverage-v8": "~1.6.0", + "eslint": "^9.21.0", + "prettier": "^3.5.2", + "typescript": "^5.7.3", + "vitest": "~1.6.0" + } +} diff --git a/packages/rest-api-sdk/src/api.ts b/packages/rest-api-sdk/src/api.ts new file mode 100644 index 00000000..0f1c734f --- /dev/null +++ b/packages/rest-api-sdk/src/api.ts @@ -0,0 +1,104 @@ +import { Configuration, DefaultApi } from "./generated"; +import type { ConfigurationParameters, RequestOpts, InitOverrideFunction } from "./generated/runtime"; +import { ResponseError } from "./generated/runtime"; + +type FirstArg = F extends (arg1: infer A, ...rest: infer R) => any ? [A, R] : never; + +type OmitAppIdParam = F extends (arg1: infer A, ...rest: infer R) => infer Ret + ? A extends { appId: string } + ? (arg1: Omit, ...rest: R) => Ret + : F + : F; + +export type AppScopedApi = { + [K in keyof T]: OmitAppIdParam; +}; + +export class ReflagApiError extends Error { + status: number; + code?: string; + details?: unknown; + + constructor(status: number, message: string, code?: string, details?: unknown) { + super(message); + this.name = "ReflagApiError"; + this.status = status; + this.code = code; + this.details = details; + } +} + +async function buildApiError(response: Response) { + const status = response.status; + const contentType = response.headers.get("content-type") ?? ""; + let details: unknown = undefined; + let message = `Request failed with ${status}`; + let code: string | undefined; + + if (contentType.includes("application/json")) { + try { + details = await response.clone().json(); + const maybeError = (details as { error?: { message?: string; code?: string } })?.error; + if (maybeError?.message) { + message = maybeError.message; + } + if (maybeError?.code) { + code = maybeError.code; + } + } catch { + details = undefined; + } + } else { + try { + const text = await response.clone().text(); + if (text) { + details = text; + message = `Request failed with ${status}: ${text}`; + } + } catch { + details = undefined; + } + } + + return new ReflagApiError(status, message, code, details); +} + +export class Api extends DefaultApi { + constructor(config?: ConfigurationParameters) { + super(new Configuration(config)); + } + + protected async request( + context: RequestOpts, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + try { + return await super.request(context, initOverrides); + } catch (error) { + if (error instanceof ResponseError) { + throw await buildApiError(error.response); + } + throw error; + } + } +} + +export function withAppId(api: T, appId: string): AppScopedApi { + return new Proxy(api, { + get(target, prop, receiver) { + const value = Reflect.get(target, prop, receiver); + if (typeof value !== "function") return value; + return (arg1: unknown, ...rest: unknown[]) => { + if (arg1 && typeof arg1 === "object" && !Array.isArray(arg1)) { + const args = "appId" in (arg1 as object) ? arg1 : { ...(arg1 as object), appId }; + return (value as (...args: unknown[]) => unknown).call(target, args, ...rest); + } + return (value as (...args: unknown[]) => unknown).call(target, arg1, ...rest); + }; + }, + }) as AppScopedApi; +} + +export function createAppClient(appId: string, config?: ConfigurationParameters) { + return withAppId(new Api(config), appId); +} diff --git a/packages/rest-api-sdk/src/generated/.openapi-generator-ignore b/packages/rest-api-sdk/src/generated/.openapi-generator-ignore new file mode 100644 index 00000000..7484ee59 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/packages/rest-api-sdk/src/generated/.openapi-generator/FILES b/packages/rest-api-sdk/src/generated/.openapi-generator/FILES new file mode 100644 index 00000000..3bb71efe --- /dev/null +++ b/packages/rest-api-sdk/src/generated/.openapi-generator/FILES @@ -0,0 +1,36 @@ +.openapi-generator-ignore +apis/DefaultApi.ts +apis/index.ts +index.ts +models/App.ts +models/AppHeader.ts +models/AppHeaderCollection.ts +models/BulkUpdateFlagSpecificTargetsSchema.ts +models/EntityFlag.ts +models/EntityFlagUpdate.ts +models/EntityFlagsResponse.ts +models/Environment.ts +models/EnvironmentHeader.ts +models/EnvironmentHeaderCollection.ts +models/EnvironmentHeaderSortByColumn.ts +models/EnvironmentSdkAccess.ts +models/ErrorResponse.ts +models/ErrorResponseError.ts +models/FlagHeader.ts +models/FlagHeaderCollection.ts +models/FlagKeyFormat.ts +models/FlagTargeting.ts +models/FlagTargetingCollection.ts +models/FlagValue.ts +models/FlagValueTargeting.ts +models/OrgHeader.ts +models/ReflagUserHeader.ts +models/SegmentHeader.ts +models/SegmentType.ts +models/SortOrder.ts +models/StageHeader.ts +models/UpdateEntityFlagsBody.ts +models/UpdateFlagSpecificTargets.ts +models/UpdateFlagSpecificTargetsValue.ts +models/index.ts +runtime.ts diff --git a/packages/rest-api-sdk/src/generated/.openapi-generator/VERSION b/packages/rest-api-sdk/src/generated/.openapi-generator/VERSION new file mode 100644 index 00000000..3821090f --- /dev/null +++ b/packages/rest-api-sdk/src/generated/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.19.0 diff --git a/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts b/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts new file mode 100644 index 00000000..5a0de6ec --- /dev/null +++ b/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts @@ -0,0 +1,967 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import * as runtime from '../runtime'; +import type { + App, + AppHeaderCollection, + BulkUpdateFlagSpecificTargetsSchema, + EntityFlagsResponse, + Environment, + EnvironmentHeaderCollection, + EnvironmentHeaderSortByColumn, + ErrorResponse, + FlagHeaderCollection, + FlagTargeting, + FlagTargetingCollection, + SortOrder, + UpdateEntityFlagsBody, +} from '../models/index'; +import { + AppFromJSON, + AppToJSON, + AppHeaderCollectionFromJSON, + AppHeaderCollectionToJSON, + BulkUpdateFlagSpecificTargetsSchemaFromJSON, + BulkUpdateFlagSpecificTargetsSchemaToJSON, + EntityFlagsResponseFromJSON, + EntityFlagsResponseToJSON, + EnvironmentFromJSON, + EnvironmentToJSON, + EnvironmentHeaderCollectionFromJSON, + EnvironmentHeaderCollectionToJSON, + EnvironmentHeaderSortByColumnFromJSON, + EnvironmentHeaderSortByColumnToJSON, + ErrorResponseFromJSON, + ErrorResponseToJSON, + FlagHeaderCollectionFromJSON, + FlagHeaderCollectionToJSON, + FlagTargetingFromJSON, + FlagTargetingToJSON, + FlagTargetingCollectionFromJSON, + FlagTargetingCollectionToJSON, + SortOrderFromJSON, + SortOrderToJSON, + UpdateEntityFlagsBodyFromJSON, + UpdateEntityFlagsBodyToJSON, +} from '../models/index'; + +export interface GetAppRequest { + appId: string; +} + +export interface GetCompanyFlagsRequest { + appId: string; + companyId: string; + envId: string; +} + +export interface GetEnvironmentRequest { + appId: string; + envId: string; +} + +export interface GetFlagTargetingRequest { + appId: string; + flagKey: string; + envId: string; +} + +export interface GetUserFlagsRequest { + appId: string; + userId: string; + envId: string; +} + +export interface ListAppsRequest { + orgId?: string; +} + +export interface ListEnvironmentsRequest { + appId: string; + sortOrder?: SortOrder; + sortBy?: EnvironmentHeaderSortByColumn; +} + +export interface ListFlagsRequest { + appId: string; +} + +export interface UpdateBulkFlagSpecificTargetsRequest { + appId: string; + envId: string; + bulkUpdateFlagSpecificTargetsSchema?: BulkUpdateFlagSpecificTargetsSchema; +} + +export interface UpdateCompanyFlagsRequest { + appId: string; + companyId: string; + envId: string; + updateEntityFlagsBody?: UpdateEntityFlagsBody; +} + +export interface UpdateUserFlagsRequest { + appId: string; + userId: string; + envId: string; + updateEntityFlagsBody?: UpdateEntityFlagsBody; +} + +/** + * DefaultApi - interface + * + * @export + * @interface DefaultApiInterface + */ +export interface DefaultApiInterface { + /** + * Retrieve a specific application by its identifier + * @summary Get details of an application + * @param {string} appId App identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApiInterface + */ + getAppRaw(requestParameters: GetAppRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Retrieve a specific application by its identifier + * Get details of an application + */ + getApp(requestParameters: GetAppRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + + /** + * Retrieve all flags with their targeting status for a specific company + * @summary Get flags for a company + * @param {string} appId App identifier + * @param {string} companyId Company ID within your application + * @param {string} envId Environment ID to evaluate targeting for + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApiInterface + */ + getCompanyFlagsRaw(requestParameters: GetCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Retrieve all flags with their targeting status for a specific company + * Get flags for a company + */ + getCompanyFlags(requestParameters: GetCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + + /** + * Retrieve details for a specific environment + * @summary Get environment details + * @param {string} appId App identifier + * @param {string} envId Environment identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApiInterface + */ + getEnvironmentRaw(requestParameters: GetEnvironmentRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Retrieve details for a specific environment + * Get environment details + */ + getEnvironment(requestParameters: GetEnvironmentRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + + /** + * Retrieve targeting for a flag in an environment + * @summary Get flag targeting for an environment + * @param {string} appId App identifier + * @param {string} flagKey Unique flag key + * @param {string} envId Environment identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApiInterface + */ + getFlagTargetingRaw(requestParameters: GetFlagTargetingRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Retrieve targeting for a flag in an environment + * Get flag targeting for an environment + */ + getFlagTargeting(requestParameters: GetFlagTargetingRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + + /** + * Retrieve all flags with their targeting status for a specific user + * @summary Get flags for a user + * @param {string} appId App identifier + * @param {string} userId User ID within your application + * @param {string} envId Environment ID to evaluate targeting for + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApiInterface + */ + getUserFlagsRaw(requestParameters: GetUserFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Retrieve all flags with their targeting status for a specific user + * Get flags for a user + */ + getUserFlags(requestParameters: GetUserFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + + /** + * Retrieve all accessible applications + * @summary List of applications + * @param {string} [orgId] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApiInterface + */ + listAppsRaw(requestParameters: ListAppsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Retrieve all accessible applications + * List of applications + */ + listApps(requestParameters: ListAppsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + + /** + * Retrieve all environments for a specific application + * @summary List environments for application + * @param {string} appId App identifier + * @param {SortOrder} [sortOrder] Sort order applied to the sorting column + * @param {EnvironmentHeaderSortByColumn} [sortBy] The column to sort by + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApiInterface + */ + listEnvironmentsRaw(requestParameters: ListEnvironmentsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Retrieve all environments for a specific application + * List environments for application + */ + listEnvironments(requestParameters: ListEnvironmentsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + + /** + * Retrieve all flags for a specific application + * @summary List flags for application + * @param {string} appId App identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApiInterface + */ + listFlagsRaw(requestParameters: ListFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Retrieve all flags for a specific application + * List flags for application + */ + listFlags(requestParameters: ListFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + + /** + * Update specific companies and users for flags in an environment + * @summary Update flag specific targets for an environment + * @param {string} appId App identifier + * @param {string} envId Environment identifier + * @param {BulkUpdateFlagSpecificTargetsSchema} [bulkUpdateFlagSpecificTargetsSchema] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApiInterface + */ + updateBulkFlagSpecificTargetsRaw(requestParameters: UpdateBulkFlagSpecificTargetsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Update specific companies and users for flags in an environment + * Update flag specific targets for an environment + */ + updateBulkFlagSpecificTargets(requestParameters: UpdateBulkFlagSpecificTargetsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + + /** + * Update specific targeting for flags for a company in an environment + * @summary Update flag targeting for a company + * @param {string} appId App identifier + * @param {string} companyId Company ID within your application + * @param {string} envId Environment ID to evaluate targeting for + * @param {UpdateEntityFlagsBody} [updateEntityFlagsBody] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApiInterface + */ + updateCompanyFlagsRaw(requestParameters: UpdateCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Update specific targeting for flags for a company in an environment + * Update flag targeting for a company + */ + updateCompanyFlags(requestParameters: UpdateCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + + /** + * Update specific targeting for flags for a user in an environment + * @summary Update flag targeting for a user + * @param {string} appId App identifier + * @param {string} userId User ID within your application + * @param {string} envId Environment ID to evaluate targeting for + * @param {UpdateEntityFlagsBody} [updateEntityFlagsBody] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApiInterface + */ + updateUserFlagsRaw(requestParameters: UpdateUserFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Update specific targeting for flags for a user in an environment + * Update flag targeting for a user + */ + updateUserFlags(requestParameters: UpdateUserFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + +} + +/** + * + */ +export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { + + /** + * Retrieve a specific application by its identifier + * Get details of an application + */ + async getAppRaw(requestParameters: GetAppRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['appId'] == null) { + throw new runtime.RequiredError( + 'appId', + 'Required parameter "appId" was null or undefined when calling getApp().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("APIKey", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/apps/{appId}`; + urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + + const response = await this.request({ + path: urlPath, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => AppFromJSON(jsonValue)); + } + + /** + * Retrieve a specific application by its identifier + * Get details of an application + */ + async getApp(requestParameters: GetAppRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getAppRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Retrieve all flags with their targeting status for a specific company + * Get flags for a company + */ + async getCompanyFlagsRaw(requestParameters: GetCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['appId'] == null) { + throw new runtime.RequiredError( + 'appId', + 'Required parameter "appId" was null or undefined when calling getCompanyFlags().' + ); + } + + if (requestParameters['companyId'] == null) { + throw new runtime.RequiredError( + 'companyId', + 'Required parameter "companyId" was null or undefined when calling getCompanyFlags().' + ); + } + + if (requestParameters['envId'] == null) { + throw new runtime.RequiredError( + 'envId', + 'Required parameter "envId" was null or undefined when calling getCompanyFlags().' + ); + } + + const queryParameters: any = {}; + + if (requestParameters['envId'] != null) { + queryParameters['envId'] = requestParameters['envId']; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("APIKey", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/apps/{appId}/companies/{companyId}/flags`; + urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + urlPath = urlPath.replace(`{${"companyId"}}`, encodeURIComponent(String(requestParameters['companyId']))); + + const response = await this.request({ + path: urlPath, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => EntityFlagsResponseFromJSON(jsonValue)); + } + + /** + * Retrieve all flags with their targeting status for a specific company + * Get flags for a company + */ + async getCompanyFlags(requestParameters: GetCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getCompanyFlagsRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Retrieve details for a specific environment + * Get environment details + */ + async getEnvironmentRaw(requestParameters: GetEnvironmentRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['appId'] == null) { + throw new runtime.RequiredError( + 'appId', + 'Required parameter "appId" was null or undefined when calling getEnvironment().' + ); + } + + if (requestParameters['envId'] == null) { + throw new runtime.RequiredError( + 'envId', + 'Required parameter "envId" was null or undefined when calling getEnvironment().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("APIKey", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/apps/{appId}/environments/{envId}`; + urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + urlPath = urlPath.replace(`{${"envId"}}`, encodeURIComponent(String(requestParameters['envId']))); + + const response = await this.request({ + path: urlPath, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => EnvironmentFromJSON(jsonValue)); + } + + /** + * Retrieve details for a specific environment + * Get environment details + */ + async getEnvironment(requestParameters: GetEnvironmentRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getEnvironmentRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Retrieve targeting for a flag in an environment + * Get flag targeting for an environment + */ + async getFlagTargetingRaw(requestParameters: GetFlagTargetingRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['appId'] == null) { + throw new runtime.RequiredError( + 'appId', + 'Required parameter "appId" was null or undefined when calling getFlagTargeting().' + ); + } + + if (requestParameters['flagKey'] == null) { + throw new runtime.RequiredError( + 'flagKey', + 'Required parameter "flagKey" was null or undefined when calling getFlagTargeting().' + ); + } + + if (requestParameters['envId'] == null) { + throw new runtime.RequiredError( + 'envId', + 'Required parameter "envId" was null or undefined when calling getFlagTargeting().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("APIKey", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/apps/{appId}/flags/{flagKey}/targeting/{envId}`; + urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + urlPath = urlPath.replace(`{${"flagKey"}}`, encodeURIComponent(String(requestParameters['flagKey']))); + urlPath = urlPath.replace(`{${"envId"}}`, encodeURIComponent(String(requestParameters['envId']))); + + const response = await this.request({ + path: urlPath, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => FlagTargetingFromJSON(jsonValue)); + } + + /** + * Retrieve targeting for a flag in an environment + * Get flag targeting for an environment + */ + async getFlagTargeting(requestParameters: GetFlagTargetingRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getFlagTargetingRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Retrieve all flags with their targeting status for a specific user + * Get flags for a user + */ + async getUserFlagsRaw(requestParameters: GetUserFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['appId'] == null) { + throw new runtime.RequiredError( + 'appId', + 'Required parameter "appId" was null or undefined when calling getUserFlags().' + ); + } + + if (requestParameters['userId'] == null) { + throw new runtime.RequiredError( + 'userId', + 'Required parameter "userId" was null or undefined when calling getUserFlags().' + ); + } + + if (requestParameters['envId'] == null) { + throw new runtime.RequiredError( + 'envId', + 'Required parameter "envId" was null or undefined when calling getUserFlags().' + ); + } + + const queryParameters: any = {}; + + if (requestParameters['envId'] != null) { + queryParameters['envId'] = requestParameters['envId']; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("APIKey", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/apps/{appId}/users/{userId}/flags`; + urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + urlPath = urlPath.replace(`{${"userId"}}`, encodeURIComponent(String(requestParameters['userId']))); + + const response = await this.request({ + path: urlPath, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => EntityFlagsResponseFromJSON(jsonValue)); + } + + /** + * Retrieve all flags with their targeting status for a specific user + * Get flags for a user + */ + async getUserFlags(requestParameters: GetUserFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getUserFlagsRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Retrieve all accessible applications + * List of applications + */ + async listAppsRaw(requestParameters: ListAppsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + if (requestParameters['orgId'] != null) { + queryParameters['orgId'] = requestParameters['orgId']; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("APIKey", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/apps`; + + const response = await this.request({ + path: urlPath, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => AppHeaderCollectionFromJSON(jsonValue)); + } + + /** + * Retrieve all accessible applications + * List of applications + */ + async listApps(requestParameters: ListAppsRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.listAppsRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Retrieve all environments for a specific application + * List environments for application + */ + async listEnvironmentsRaw(requestParameters: ListEnvironmentsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['appId'] == null) { + throw new runtime.RequiredError( + 'appId', + 'Required parameter "appId" was null or undefined when calling listEnvironments().' + ); + } + + const queryParameters: any = {}; + + if (requestParameters['sortOrder'] != null) { + queryParameters['sortOrder'] = requestParameters['sortOrder']; + } + + if (requestParameters['sortBy'] != null) { + queryParameters['sortBy'] = requestParameters['sortBy']; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("APIKey", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/apps/{appId}/environments`; + urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + + const response = await this.request({ + path: urlPath, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => EnvironmentHeaderCollectionFromJSON(jsonValue)); + } + + /** + * Retrieve all environments for a specific application + * List environments for application + */ + async listEnvironments(requestParameters: ListEnvironmentsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.listEnvironmentsRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Retrieve all flags for a specific application + * List flags for application + */ + async listFlagsRaw(requestParameters: ListFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['appId'] == null) { + throw new runtime.RequiredError( + 'appId', + 'Required parameter "appId" was null or undefined when calling listFlags().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("APIKey", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/apps/{appId}/flags`; + urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + + const response = await this.request({ + path: urlPath, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => FlagHeaderCollectionFromJSON(jsonValue)); + } + + /** + * Retrieve all flags for a specific application + * List flags for application + */ + async listFlags(requestParameters: ListFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.listFlagsRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Update specific companies and users for flags in an environment + * Update flag specific targets for an environment + */ + async updateBulkFlagSpecificTargetsRaw(requestParameters: UpdateBulkFlagSpecificTargetsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['appId'] == null) { + throw new runtime.RequiredError( + 'appId', + 'Required parameter "appId" was null or undefined when calling updateBulkFlagSpecificTargets().' + ); + } + + if (requestParameters['envId'] == null) { + throw new runtime.RequiredError( + 'envId', + 'Required parameter "envId" was null or undefined when calling updateBulkFlagSpecificTargets().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("APIKey", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/apps/{appId}/flags/specific-targets/{envId}`; + urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + urlPath = urlPath.replace(`{${"envId"}}`, encodeURIComponent(String(requestParameters['envId']))); + + const response = await this.request({ + path: urlPath, + method: 'PATCH', + headers: headerParameters, + query: queryParameters, + body: BulkUpdateFlagSpecificTargetsSchemaToJSON(requestParameters['bulkUpdateFlagSpecificTargetsSchema']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => FlagTargetingCollectionFromJSON(jsonValue)); + } + + /** + * Update specific companies and users for flags in an environment + * Update flag specific targets for an environment + */ + async updateBulkFlagSpecificTargets(requestParameters: UpdateBulkFlagSpecificTargetsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.updateBulkFlagSpecificTargetsRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Update specific targeting for flags for a company in an environment + * Update flag targeting for a company + */ + async updateCompanyFlagsRaw(requestParameters: UpdateCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['appId'] == null) { + throw new runtime.RequiredError( + 'appId', + 'Required parameter "appId" was null or undefined when calling updateCompanyFlags().' + ); + } + + if (requestParameters['companyId'] == null) { + throw new runtime.RequiredError( + 'companyId', + 'Required parameter "companyId" was null or undefined when calling updateCompanyFlags().' + ); + } + + if (requestParameters['envId'] == null) { + throw new runtime.RequiredError( + 'envId', + 'Required parameter "envId" was null or undefined when calling updateCompanyFlags().' + ); + } + + const queryParameters: any = {}; + + if (requestParameters['envId'] != null) { + queryParameters['envId'] = requestParameters['envId']; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("APIKey", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/apps/{appId}/companies/{companyId}/flags`; + urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + urlPath = urlPath.replace(`{${"companyId"}}`, encodeURIComponent(String(requestParameters['companyId']))); + + const response = await this.request({ + path: urlPath, + method: 'PATCH', + headers: headerParameters, + query: queryParameters, + body: UpdateEntityFlagsBodyToJSON(requestParameters['updateEntityFlagsBody']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => EntityFlagsResponseFromJSON(jsonValue)); + } + + /** + * Update specific targeting for flags for a company in an environment + * Update flag targeting for a company + */ + async updateCompanyFlags(requestParameters: UpdateCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.updateCompanyFlagsRaw(requestParameters, initOverrides); + return await response.value(); + } + + /** + * Update specific targeting for flags for a user in an environment + * Update flag targeting for a user + */ + async updateUserFlagsRaw(requestParameters: UpdateUserFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['appId'] == null) { + throw new runtime.RequiredError( + 'appId', + 'Required parameter "appId" was null or undefined when calling updateUserFlags().' + ); + } + + if (requestParameters['userId'] == null) { + throw new runtime.RequiredError( + 'userId', + 'Required parameter "userId" was null or undefined when calling updateUserFlags().' + ); + } + + if (requestParameters['envId'] == null) { + throw new runtime.RequiredError( + 'envId', + 'Required parameter "envId" was null or undefined when calling updateUserFlags().' + ); + } + + const queryParameters: any = {}; + + if (requestParameters['envId'] != null) { + queryParameters['envId'] = requestParameters['envId']; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("APIKey", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/apps/{appId}/users/{userId}/flags`; + urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + urlPath = urlPath.replace(`{${"userId"}}`, encodeURIComponent(String(requestParameters['userId']))); + + const response = await this.request({ + path: urlPath, + method: 'PATCH', + headers: headerParameters, + query: queryParameters, + body: UpdateEntityFlagsBodyToJSON(requestParameters['updateEntityFlagsBody']), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => EntityFlagsResponseFromJSON(jsonValue)); + } + + /** + * Update specific targeting for flags for a user in an environment + * Update flag targeting for a user + */ + async updateUserFlags(requestParameters: UpdateUserFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.updateUserFlagsRaw(requestParameters, initOverrides); + return await response.value(); + } + +} diff --git a/packages/rest-api-sdk/src/generated/apis/index.ts b/packages/rest-api-sdk/src/generated/apis/index.ts new file mode 100644 index 00000000..69c44c00 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/apis/index.ts @@ -0,0 +1,3 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './DefaultApi'; diff --git a/packages/rest-api-sdk/src/generated/index.ts b/packages/rest-api-sdk/src/generated/index.ts new file mode 100644 index 00000000..bebe8bbb --- /dev/null +++ b/packages/rest-api-sdk/src/generated/index.ts @@ -0,0 +1,5 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './runtime'; +export * from './apis/index'; +export * from './models/index'; diff --git a/packages/rest-api-sdk/src/generated/models/App.ts b/packages/rest-api-sdk/src/generated/models/App.ts new file mode 100644 index 00000000..12c57732 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/App.ts @@ -0,0 +1,167 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { SegmentHeader } from './SegmentHeader'; +import { + SegmentHeaderFromJSON, + SegmentHeaderFromJSONTyped, + SegmentHeaderToJSON, + SegmentHeaderToJSONTyped, +} from './SegmentHeader'; +import type { StageHeader } from './StageHeader'; +import { + StageHeaderFromJSON, + StageHeaderFromJSONTyped, + StageHeaderToJSON, + StageHeaderToJSONTyped, +} from './StageHeader'; +import type { Environment } from './Environment'; +import { + EnvironmentFromJSON, + EnvironmentFromJSONTyped, + EnvironmentToJSON, + EnvironmentToJSONTyped, +} from './Environment'; +import type { FlagKeyFormat } from './FlagKeyFormat'; +import { + FlagKeyFormatFromJSON, + FlagKeyFormatFromJSONTyped, + FlagKeyFormatToJSON, + FlagKeyFormatToJSONTyped, +} from './FlagKeyFormat'; +import type { OrgHeader } from './OrgHeader'; +import { + OrgHeaderFromJSON, + OrgHeaderFromJSONTyped, + OrgHeaderToJSON, + OrgHeaderToJSONTyped, +} from './OrgHeader'; + +/** + * App information with related collections + * @export + * @interface App + */ +export interface App { + /** + * + * @type {OrgHeader} + * @memberof App + */ + org: OrgHeader; + /** + * App identifier + * @type {string} + * @memberof App + */ + id: string; + /** + * App name + * @type {string} + * @memberof App + */ + name: string; + /** + * Whether the app is a demo app + * @type {boolean} + * @memberof App + */ + demo: boolean; + /** + * + * @type {FlagKeyFormat} + * @memberof App + */ + flagKeyFormat: FlagKeyFormat; + /** + * Environments within the app + * @type {Array} + * @memberof App + */ + environments: Array; + /** + * Stages within the app + * @type {Array} + * @memberof App + */ + stages: Array; + /** + * Segments within the app + * @type {Array} + * @memberof App + */ + segments: Array; +} + + + +/** + * Check if a given object implements the App interface. + */ +export function instanceOfApp(value: object): value is App { + if (!('org' in value) || value['org'] === undefined) return false; + if (!('id' in value) || value['id'] === undefined) return false; + if (!('name' in value) || value['name'] === undefined) return false; + if (!('demo' in value) || value['demo'] === undefined) return false; + if (!('flagKeyFormat' in value) || value['flagKeyFormat'] === undefined) return false; + if (!('environments' in value) || value['environments'] === undefined) return false; + if (!('stages' in value) || value['stages'] === undefined) return false; + if (!('segments' in value) || value['segments'] === undefined) return false; + return true; +} + +export function AppFromJSON(json: any): App { + return AppFromJSONTyped(json, false); +} + +export function AppFromJSONTyped(json: any, ignoreDiscriminator: boolean): App { + if (json == null) { + return json; + } + return { + + 'org': OrgHeaderFromJSON(json['org']), + 'id': json['id'], + 'name': json['name'], + 'demo': json['demo'], + 'flagKeyFormat': FlagKeyFormatFromJSON(json['flagKeyFormat']), + 'environments': ((json['environments'] as Array).map(EnvironmentFromJSON)), + 'stages': ((json['stages'] as Array).map(StageHeaderFromJSON)), + 'segments': ((json['segments'] as Array).map(SegmentHeaderFromJSON)), + }; +} + +export function AppToJSON(json: any): App { + return AppToJSONTyped(json, false); +} + +export function AppToJSONTyped(value?: App | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'org': OrgHeaderToJSON(value['org']), + 'id': value['id'], + 'name': value['name'], + 'demo': value['demo'], + 'flagKeyFormat': FlagKeyFormatToJSON(value['flagKeyFormat']), + 'environments': ((value['environments'] as Array).map(EnvironmentToJSON)), + 'stages': ((value['stages'] as Array).map(StageHeaderToJSON)), + 'segments': ((value['segments'] as Array).map(SegmentHeaderToJSON)), + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/AppHeader.ts b/packages/rest-api-sdk/src/generated/models/AppHeader.ts new file mode 100644 index 00000000..4306938c --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/AppHeader.ts @@ -0,0 +1,119 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { FlagKeyFormat } from './FlagKeyFormat'; +import { + FlagKeyFormatFromJSON, + FlagKeyFormatFromJSONTyped, + FlagKeyFormatToJSON, + FlagKeyFormatToJSONTyped, +} from './FlagKeyFormat'; +import type { OrgHeader } from './OrgHeader'; +import { + OrgHeaderFromJSON, + OrgHeaderFromJSONTyped, + OrgHeaderToJSON, + OrgHeaderToJSONTyped, +} from './OrgHeader'; + +/** + * Basic app information + * @export + * @interface AppHeader + */ +export interface AppHeader { + /** + * + * @type {OrgHeader} + * @memberof AppHeader + */ + org: OrgHeader; + /** + * App identifier + * @type {string} + * @memberof AppHeader + */ + id: string; + /** + * App name + * @type {string} + * @memberof AppHeader + */ + name: string; + /** + * Whether the app is a demo app + * @type {boolean} + * @memberof AppHeader + */ + demo: boolean; + /** + * + * @type {FlagKeyFormat} + * @memberof AppHeader + */ + flagKeyFormat: FlagKeyFormat; +} + + + +/** + * Check if a given object implements the AppHeader interface. + */ +export function instanceOfAppHeader(value: object): value is AppHeader { + if (!('org' in value) || value['org'] === undefined) return false; + if (!('id' in value) || value['id'] === undefined) return false; + if (!('name' in value) || value['name'] === undefined) return false; + if (!('demo' in value) || value['demo'] === undefined) return false; + if (!('flagKeyFormat' in value) || value['flagKeyFormat'] === undefined) return false; + return true; +} + +export function AppHeaderFromJSON(json: any): AppHeader { + return AppHeaderFromJSONTyped(json, false); +} + +export function AppHeaderFromJSONTyped(json: any, ignoreDiscriminator: boolean): AppHeader { + if (json == null) { + return json; + } + return { + + 'org': OrgHeaderFromJSON(json['org']), + 'id': json['id'], + 'name': json['name'], + 'demo': json['demo'], + 'flagKeyFormat': FlagKeyFormatFromJSON(json['flagKeyFormat']), + }; +} + +export function AppHeaderToJSON(json: any): AppHeader { + return AppHeaderToJSONTyped(json, false); +} + +export function AppHeaderToJSONTyped(value?: AppHeader | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'org': OrgHeaderToJSON(value['org']), + 'id': value['id'], + 'name': value['name'], + 'demo': value['demo'], + 'flagKeyFormat': FlagKeyFormatToJSON(value['flagKeyFormat']), + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/AppHeaderCollection.ts b/packages/rest-api-sdk/src/generated/models/AppHeaderCollection.ts new file mode 100644 index 00000000..77066dde --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/AppHeaderCollection.ts @@ -0,0 +1,74 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { AppHeader } from './AppHeader'; +import { + AppHeaderFromJSON, + AppHeaderFromJSONTyped, + AppHeaderToJSON, + AppHeaderToJSONTyped, +} from './AppHeader'; + +/** + * Collection of Basic app information + * @export + * @interface AppHeaderCollection + */ +export interface AppHeaderCollection { + /** + * The individual items in the collection + * @type {Array} + * @memberof AppHeaderCollection + */ + data: Array; +} + +/** + * Check if a given object implements the AppHeaderCollection interface. + */ +export function instanceOfAppHeaderCollection(value: object): value is AppHeaderCollection { + if (!('data' in value) || value['data'] === undefined) return false; + return true; +} + +export function AppHeaderCollectionFromJSON(json: any): AppHeaderCollection { + return AppHeaderCollectionFromJSONTyped(json, false); +} + +export function AppHeaderCollectionFromJSONTyped(json: any, ignoreDiscriminator: boolean): AppHeaderCollection { + if (json == null) { + return json; + } + return { + + 'data': ((json['data'] as Array).map(AppHeaderFromJSON)), + }; +} + +export function AppHeaderCollectionToJSON(json: any): AppHeaderCollection { + return AppHeaderCollectionToJSONTyped(json, false); +} + +export function AppHeaderCollectionToJSONTyped(value?: AppHeaderCollection | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'data': ((value['data'] as Array).map(AppHeaderToJSON)), + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/BulkUpdateFlagSpecificTargetsSchema.ts b/packages/rest-api-sdk/src/generated/models/BulkUpdateFlagSpecificTargetsSchema.ts new file mode 100644 index 00000000..20145a14 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/BulkUpdateFlagSpecificTargetsSchema.ts @@ -0,0 +1,90 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { UpdateFlagSpecificTargets } from './UpdateFlagSpecificTargets'; +import { + UpdateFlagSpecificTargetsFromJSON, + UpdateFlagSpecificTargetsFromJSONTyped, + UpdateFlagSpecificTargetsToJSON, + UpdateFlagSpecificTargetsToJSONTyped, +} from './UpdateFlagSpecificTargets'; + +/** + * Update the explicit value of multiple flags for a given audience + * @export + * @interface BulkUpdateFlagSpecificTargetsSchema + */ +export interface BulkUpdateFlagSpecificTargetsSchema { + /** + * The list of updates to make to the flags' targeting + * @type {Array} + * @memberof BulkUpdateFlagSpecificTargetsSchema + */ + updates: Array; + /** + * Whether to send notifications about the change to configured integration (eg. Slack, Linear, etc). Defaults to true. + * @type {boolean} + * @memberof BulkUpdateFlagSpecificTargetsSchema + */ + notifications?: boolean; + /** + * The description of the change recorded in the change history + * @type {string} + * @memberof BulkUpdateFlagSpecificTargetsSchema + */ + changeDescription?: string; +} + +/** + * Check if a given object implements the BulkUpdateFlagSpecificTargetsSchema interface. + */ +export function instanceOfBulkUpdateFlagSpecificTargetsSchema(value: object): value is BulkUpdateFlagSpecificTargetsSchema { + if (!('updates' in value) || value['updates'] === undefined) return false; + return true; +} + +export function BulkUpdateFlagSpecificTargetsSchemaFromJSON(json: any): BulkUpdateFlagSpecificTargetsSchema { + return BulkUpdateFlagSpecificTargetsSchemaFromJSONTyped(json, false); +} + +export function BulkUpdateFlagSpecificTargetsSchemaFromJSONTyped(json: any, ignoreDiscriminator: boolean): BulkUpdateFlagSpecificTargetsSchema { + if (json == null) { + return json; + } + return { + + 'updates': ((json['updates'] as Array).map(UpdateFlagSpecificTargetsFromJSON)), + 'notifications': json['notifications'] == null ? undefined : json['notifications'], + 'changeDescription': json['changeDescription'] == null ? undefined : json['changeDescription'], + }; +} + +export function BulkUpdateFlagSpecificTargetsSchemaToJSON(json: any): BulkUpdateFlagSpecificTargetsSchema { + return BulkUpdateFlagSpecificTargetsSchemaToJSONTyped(json, false); +} + +export function BulkUpdateFlagSpecificTargetsSchemaToJSONTyped(value?: BulkUpdateFlagSpecificTargetsSchema | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'updates': ((value['updates'] as Array).map(UpdateFlagSpecificTargetsToJSON)), + 'notifications': value['notifications'], + 'changeDescription': value['changeDescription'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/EntityFlag.ts b/packages/rest-api-sdk/src/generated/models/EntityFlag.ts new file mode 100644 index 00000000..719c3423 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/EntityFlag.ts @@ -0,0 +1,165 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * Flag information with enabled status for an entity + * @export + * @interface EntityFlag + */ +export interface EntityFlag { + /** + * Flag ID + * @type {string} + * @memberof EntityFlag + */ + id: string; + /** + * Unique flag key + * @type {string} + * @memberof EntityFlag + */ + key: string; + /** + * Flag name + * @type {string} + * @memberof EntityFlag + */ + name: string; + /** + * Whether the flag is enabled for this entity + * @type {boolean} + * @memberof EntityFlag + */ + value: boolean; + /** + * + * @type {boolean} + * @memberof EntityFlag + */ + specificallyTargetedValue: boolean | null; + /** + * + * @type {string} + * @memberof EntityFlag + */ + firstExposureAt: string | null; + /** + * + * @type {string} + * @memberof EntityFlag + */ + lastExposureAt: string | null; + /** + * + * @type {string} + * @memberof EntityFlag + */ + lastCheckAt: string | null; + /** + * Number of times the entity was exposed to this flag + * @type {number} + * @memberof EntityFlag + */ + exposureCount: number; + /** + * + * @type {string} + * @memberof EntityFlag + */ + firstTrackAt: string | null; + /** + * + * @type {string} + * @memberof EntityFlag + */ + lastTrackAt: string | null; + /** + * Number of track events for this flag + * @type {number} + * @memberof EntityFlag + */ + trackCount: number; +} + +/** + * Check if a given object implements the EntityFlag interface. + */ +export function instanceOfEntityFlag(value: object): value is EntityFlag { + if (!('id' in value) || value['id'] === undefined) return false; + if (!('key' in value) || value['key'] === undefined) return false; + if (!('name' in value) || value['name'] === undefined) return false; + if (!('value' in value) || value['value'] === undefined) return false; + if (!('specificallyTargetedValue' in value) || value['specificallyTargetedValue'] === undefined) return false; + if (!('firstExposureAt' in value) || value['firstExposureAt'] === undefined) return false; + if (!('lastExposureAt' in value) || value['lastExposureAt'] === undefined) return false; + if (!('lastCheckAt' in value) || value['lastCheckAt'] === undefined) return false; + if (!('exposureCount' in value) || value['exposureCount'] === undefined) return false; + if (!('firstTrackAt' in value) || value['firstTrackAt'] === undefined) return false; + if (!('lastTrackAt' in value) || value['lastTrackAt'] === undefined) return false; + if (!('trackCount' in value) || value['trackCount'] === undefined) return false; + return true; +} + +export function EntityFlagFromJSON(json: any): EntityFlag { + return EntityFlagFromJSONTyped(json, false); +} + +export function EntityFlagFromJSONTyped(json: any, ignoreDiscriminator: boolean): EntityFlag { + if (json == null) { + return json; + } + return { + + 'id': json['id'], + 'key': json['key'], + 'name': json['name'], + 'value': json['value'], + 'specificallyTargetedValue': json['specificallyTargetedValue'], + 'firstExposureAt': json['firstExposureAt'], + 'lastExposureAt': json['lastExposureAt'], + 'lastCheckAt': json['lastCheckAt'], + 'exposureCount': json['exposureCount'], + 'firstTrackAt': json['firstTrackAt'], + 'lastTrackAt': json['lastTrackAt'], + 'trackCount': json['trackCount'], + }; +} + +export function EntityFlagToJSON(json: any): EntityFlag { + return EntityFlagToJSONTyped(json, false); +} + +export function EntityFlagToJSONTyped(value?: EntityFlag | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'id': value['id'], + 'key': value['key'], + 'name': value['name'], + 'value': value['value'], + 'specificallyTargetedValue': value['specificallyTargetedValue'], + 'firstExposureAt': value['firstExposureAt'], + 'lastExposureAt': value['lastExposureAt'], + 'lastCheckAt': value['lastCheckAt'], + 'exposureCount': value['exposureCount'], + 'firstTrackAt': value['firstTrackAt'], + 'lastTrackAt': value['lastTrackAt'], + 'trackCount': value['trackCount'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/EntityFlagUpdate.ts b/packages/rest-api-sdk/src/generated/models/EntityFlagUpdate.ts new file mode 100644 index 00000000..10d9d326 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/EntityFlagUpdate.ts @@ -0,0 +1,85 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * Update for a single flag's specific targeting + * @export + * @interface EntityFlagUpdate + */ +export interface EntityFlagUpdate { + /** + * Unique flag key + * @type {string} + * @memberof EntityFlagUpdate + */ + flagKey: string; + /** + * + * @type {boolean} + * @memberof EntityFlagUpdate + */ + value: EntityFlagUpdateValueEnum | null; +} + + +/** + * @export + */ +export const EntityFlagUpdateValueEnum = { + True: true +} as const; +export type EntityFlagUpdateValueEnum = typeof EntityFlagUpdateValueEnum[keyof typeof EntityFlagUpdateValueEnum]; + + +/** + * Check if a given object implements the EntityFlagUpdate interface. + */ +export function instanceOfEntityFlagUpdate(value: object): value is EntityFlagUpdate { + if (!('flagKey' in value) || value['flagKey'] === undefined) return false; + if (!('value' in value) || value['value'] === undefined) return false; + return true; +} + +export function EntityFlagUpdateFromJSON(json: any): EntityFlagUpdate { + return EntityFlagUpdateFromJSONTyped(json, false); +} + +export function EntityFlagUpdateFromJSONTyped(json: any, ignoreDiscriminator: boolean): EntityFlagUpdate { + if (json == null) { + return json; + } + return { + + 'flagKey': json['flagKey'], + 'value': json['value'], + }; +} + +export function EntityFlagUpdateToJSON(json: any): EntityFlagUpdate { + return EntityFlagUpdateToJSONTyped(json, false); +} + +export function EntityFlagUpdateToJSONTyped(value?: EntityFlagUpdate | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'flagKey': value['flagKey'], + 'value': value['value'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/EntityFlagsResponse.ts b/packages/rest-api-sdk/src/generated/models/EntityFlagsResponse.ts new file mode 100644 index 00000000..af956367 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/EntityFlagsResponse.ts @@ -0,0 +1,101 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { EntityFlag } from './EntityFlag'; +import { + EntityFlagFromJSON, + EntityFlagFromJSONTyped, + EntityFlagToJSON, + EntityFlagToJSONTyped, +} from './EntityFlag'; + +/** + * Response containing flags for an entity + * @export + * @interface EntityFlagsResponse + */ +export interface EntityFlagsResponse { + /** + * List of flags with their enabled status + * @type {Array} + * @memberof EntityFlagsResponse + */ + data: Array; + /** + * Total number of flags + * @type {number} + * @memberof EntityFlagsResponse + */ + totalCount: number; + /** + * Page size + * @type {number} + * @memberof EntityFlagsResponse + */ + pageSize: number; + /** + * Page index + * @type {number} + * @memberof EntityFlagsResponse + */ + pageIndex: number; +} + +/** + * Check if a given object implements the EntityFlagsResponse interface. + */ +export function instanceOfEntityFlagsResponse(value: object): value is EntityFlagsResponse { + if (!('data' in value) || value['data'] === undefined) return false; + if (!('totalCount' in value) || value['totalCount'] === undefined) return false; + if (!('pageSize' in value) || value['pageSize'] === undefined) return false; + if (!('pageIndex' in value) || value['pageIndex'] === undefined) return false; + return true; +} + +export function EntityFlagsResponseFromJSON(json: any): EntityFlagsResponse { + return EntityFlagsResponseFromJSONTyped(json, false); +} + +export function EntityFlagsResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): EntityFlagsResponse { + if (json == null) { + return json; + } + return { + + 'data': ((json['data'] as Array).map(EntityFlagFromJSON)), + 'totalCount': json['totalCount'], + 'pageSize': json['pageSize'], + 'pageIndex': json['pageIndex'], + }; +} + +export function EntityFlagsResponseToJSON(json: any): EntityFlagsResponse { + return EntityFlagsResponseToJSONTyped(json, false); +} + +export function EntityFlagsResponseToJSONTyped(value?: EntityFlagsResponse | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'data': ((value['data'] as Array).map(EntityFlagToJSON)), + 'totalCount': value['totalCount'], + 'pageSize': value['pageSize'], + 'pageIndex': value['pageIndex'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/Environment.ts b/packages/rest-api-sdk/src/generated/models/Environment.ts new file mode 100644 index 00000000..1c64a7b2 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/Environment.ts @@ -0,0 +1,110 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { EnvironmentSdkAccess } from './EnvironmentSdkAccess'; +import { + EnvironmentSdkAccessFromJSON, + EnvironmentSdkAccessFromJSONTyped, + EnvironmentSdkAccessToJSON, + EnvironmentSdkAccessToJSONTyped, +} from './EnvironmentSdkAccess'; + +/** + * Environment details + * @export + * @interface Environment + */ +export interface Environment { + /** + * Environment identifier + * @type {string} + * @memberof Environment + */ + id: string; + /** + * Environment name + * @type {string} + * @memberof Environment + */ + name: string; + /** + * Whether the environment is a production environment + * @type {boolean} + * @memberof Environment + */ + isProduction: boolean; + /** + * Environment order in the app (zero-indexed) + * @type {number} + * @memberof Environment + */ + order: number; + /** + * + * @type {EnvironmentSdkAccess} + * @memberof Environment + */ + sdkAccess: EnvironmentSdkAccess; +} + +/** + * Check if a given object implements the Environment interface. + */ +export function instanceOfEnvironment(value: object): value is Environment { + if (!('id' in value) || value['id'] === undefined) return false; + if (!('name' in value) || value['name'] === undefined) return false; + if (!('isProduction' in value) || value['isProduction'] === undefined) return false; + if (!('order' in value) || value['order'] === undefined) return false; + if (!('sdkAccess' in value) || value['sdkAccess'] === undefined) return false; + return true; +} + +export function EnvironmentFromJSON(json: any): Environment { + return EnvironmentFromJSONTyped(json, false); +} + +export function EnvironmentFromJSONTyped(json: any, ignoreDiscriminator: boolean): Environment { + if (json == null) { + return json; + } + return { + + 'id': json['id'], + 'name': json['name'], + 'isProduction': json['isProduction'], + 'order': json['order'], + 'sdkAccess': EnvironmentSdkAccessFromJSON(json['sdkAccess']), + }; +} + +export function EnvironmentToJSON(json: any): Environment { + return EnvironmentToJSONTyped(json, false); +} + +export function EnvironmentToJSONTyped(value?: Environment | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'id': value['id'], + 'name': value['name'], + 'isProduction': value['isProduction'], + 'order': value['order'], + 'sdkAccess': EnvironmentSdkAccessToJSON(value['sdkAccess']), + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/EnvironmentHeader.ts b/packages/rest-api-sdk/src/generated/models/EnvironmentHeader.ts new file mode 100644 index 00000000..2f040fd4 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/EnvironmentHeader.ts @@ -0,0 +1,93 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * Basic environment information + * @export + * @interface EnvironmentHeader + */ +export interface EnvironmentHeader { + /** + * Environment identifier + * @type {string} + * @memberof EnvironmentHeader + */ + id: string; + /** + * Environment name + * @type {string} + * @memberof EnvironmentHeader + */ + name: string; + /** + * Whether the environment is a production environment + * @type {boolean} + * @memberof EnvironmentHeader + */ + isProduction: boolean; + /** + * Environment order in the app (zero-indexed) + * @type {number} + * @memberof EnvironmentHeader + */ + order: number; +} + +/** + * Check if a given object implements the EnvironmentHeader interface. + */ +export function instanceOfEnvironmentHeader(value: object): value is EnvironmentHeader { + if (!('id' in value) || value['id'] === undefined) return false; + if (!('name' in value) || value['name'] === undefined) return false; + if (!('isProduction' in value) || value['isProduction'] === undefined) return false; + if (!('order' in value) || value['order'] === undefined) return false; + return true; +} + +export function EnvironmentHeaderFromJSON(json: any): EnvironmentHeader { + return EnvironmentHeaderFromJSONTyped(json, false); +} + +export function EnvironmentHeaderFromJSONTyped(json: any, ignoreDiscriminator: boolean): EnvironmentHeader { + if (json == null) { + return json; + } + return { + + 'id': json['id'], + 'name': json['name'], + 'isProduction': json['isProduction'], + 'order': json['order'], + }; +} + +export function EnvironmentHeaderToJSON(json: any): EnvironmentHeader { + return EnvironmentHeaderToJSONTyped(json, false); +} + +export function EnvironmentHeaderToJSONTyped(value?: EnvironmentHeader | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'id': value['id'], + 'name': value['name'], + 'isProduction': value['isProduction'], + 'order': value['order'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderCollection.ts b/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderCollection.ts new file mode 100644 index 00000000..0540c54f --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderCollection.ts @@ -0,0 +1,108 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { EnvironmentHeaderSortByColumn } from './EnvironmentHeaderSortByColumn'; +import { + EnvironmentHeaderSortByColumnFromJSON, + EnvironmentHeaderSortByColumnFromJSONTyped, + EnvironmentHeaderSortByColumnToJSON, + EnvironmentHeaderSortByColumnToJSONTyped, +} from './EnvironmentHeaderSortByColumn'; +import type { EnvironmentHeader } from './EnvironmentHeader'; +import { + EnvironmentHeaderFromJSON, + EnvironmentHeaderFromJSONTyped, + EnvironmentHeaderToJSON, + EnvironmentHeaderToJSONTyped, +} from './EnvironmentHeader'; +import type { SortOrder } from './SortOrder'; +import { + SortOrderFromJSON, + SortOrderFromJSONTyped, + SortOrderToJSON, + SortOrderToJSONTyped, +} from './SortOrder'; + +/** + * Collection of Basic environment information + * @export + * @interface EnvironmentHeaderCollection + */ +export interface EnvironmentHeaderCollection { + /** + * The individual items in the collection + * @type {Array} + * @memberof EnvironmentHeaderCollection + */ + data: Array; + /** + * + * @type {SortOrder} + * @memberof EnvironmentHeaderCollection + */ + sortOrder: SortOrder; + /** + * + * @type {EnvironmentHeaderSortByColumn} + * @memberof EnvironmentHeaderCollection + */ + sortBy: EnvironmentHeaderSortByColumn; +} + + + +/** + * Check if a given object implements the EnvironmentHeaderCollection interface. + */ +export function instanceOfEnvironmentHeaderCollection(value: object): value is EnvironmentHeaderCollection { + if (!('data' in value) || value['data'] === undefined) return false; + if (!('sortOrder' in value) || value['sortOrder'] === undefined) return false; + if (!('sortBy' in value) || value['sortBy'] === undefined) return false; + return true; +} + +export function EnvironmentHeaderCollectionFromJSON(json: any): EnvironmentHeaderCollection { + return EnvironmentHeaderCollectionFromJSONTyped(json, false); +} + +export function EnvironmentHeaderCollectionFromJSONTyped(json: any, ignoreDiscriminator: boolean): EnvironmentHeaderCollection { + if (json == null) { + return json; + } + return { + + 'data': ((json['data'] as Array).map(EnvironmentHeaderFromJSON)), + 'sortOrder': SortOrderFromJSON(json['sortOrder']), + 'sortBy': EnvironmentHeaderSortByColumnFromJSON(json['sortBy']), + }; +} + +export function EnvironmentHeaderCollectionToJSON(json: any): EnvironmentHeaderCollection { + return EnvironmentHeaderCollectionToJSONTyped(json, false); +} + +export function EnvironmentHeaderCollectionToJSONTyped(value?: EnvironmentHeaderCollection | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'data': ((value['data'] as Array).map(EnvironmentHeaderToJSON)), + 'sortOrder': SortOrderToJSON(value['sortOrder']), + 'sortBy': EnvironmentHeaderSortByColumnToJSON(value['sortBy']), + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderSortByColumn.ts b/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderSortByColumn.ts new file mode 100644 index 00000000..246e4e20 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderSortByColumn.ts @@ -0,0 +1,53 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * The column to sort by + * @export + */ +export const EnvironmentHeaderSortByColumn = { + Name: 'name', + Order: 'order' +} as const; +export type EnvironmentHeaderSortByColumn = typeof EnvironmentHeaderSortByColumn[keyof typeof EnvironmentHeaderSortByColumn]; + + +export function instanceOfEnvironmentHeaderSortByColumn(value: any): boolean { + for (const key in EnvironmentHeaderSortByColumn) { + if (Object.prototype.hasOwnProperty.call(EnvironmentHeaderSortByColumn, key)) { + if (EnvironmentHeaderSortByColumn[key as keyof typeof EnvironmentHeaderSortByColumn] === value) { + return true; + } + } + } + return false; +} + +export function EnvironmentHeaderSortByColumnFromJSON(json: any): EnvironmentHeaderSortByColumn { + return EnvironmentHeaderSortByColumnFromJSONTyped(json, false); +} + +export function EnvironmentHeaderSortByColumnFromJSONTyped(json: any, ignoreDiscriminator: boolean): EnvironmentHeaderSortByColumn { + return json as EnvironmentHeaderSortByColumn; +} + +export function EnvironmentHeaderSortByColumnToJSON(value?: EnvironmentHeaderSortByColumn | null): any { + return value as any; +} + +export function EnvironmentHeaderSortByColumnToJSONTyped(value: any, ignoreDiscriminator: boolean): EnvironmentHeaderSortByColumn { + return value as EnvironmentHeaderSortByColumn; +} + diff --git a/packages/rest-api-sdk/src/generated/models/EnvironmentSdkAccess.ts b/packages/rest-api-sdk/src/generated/models/EnvironmentSdkAccess.ts new file mode 100644 index 00000000..687b6364 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/EnvironmentSdkAccess.ts @@ -0,0 +1,75 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * SDK access details + * @export + * @interface EnvironmentSdkAccess + */ +export interface EnvironmentSdkAccess { + /** + * Publishable key + * @type {string} + * @memberof EnvironmentSdkAccess + */ + publishableKey: string; + /** + * Secret key + * @type {string} + * @memberof EnvironmentSdkAccess + */ + secretKey: string; +} + +/** + * Check if a given object implements the EnvironmentSdkAccess interface. + */ +export function instanceOfEnvironmentSdkAccess(value: object): value is EnvironmentSdkAccess { + if (!('publishableKey' in value) || value['publishableKey'] === undefined) return false; + if (!('secretKey' in value) || value['secretKey'] === undefined) return false; + return true; +} + +export function EnvironmentSdkAccessFromJSON(json: any): EnvironmentSdkAccess { + return EnvironmentSdkAccessFromJSONTyped(json, false); +} + +export function EnvironmentSdkAccessFromJSONTyped(json: any, ignoreDiscriminator: boolean): EnvironmentSdkAccess { + if (json == null) { + return json; + } + return { + + 'publishableKey': json['publishableKey'], + 'secretKey': json['secretKey'], + }; +} + +export function EnvironmentSdkAccessToJSON(json: any): EnvironmentSdkAccess { + return EnvironmentSdkAccessToJSONTyped(json, false); +} + +export function EnvironmentSdkAccessToJSONTyped(value?: EnvironmentSdkAccess | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'publishableKey': value['publishableKey'], + 'secretKey': value['secretKey'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/ErrorResponse.ts b/packages/rest-api-sdk/src/generated/models/ErrorResponse.ts new file mode 100644 index 00000000..9ba1225c --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/ErrorResponse.ts @@ -0,0 +1,82 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { ErrorResponseError } from './ErrorResponseError'; +import { + ErrorResponseErrorFromJSON, + ErrorResponseErrorFromJSONTyped, + ErrorResponseErrorToJSON, + ErrorResponseErrorToJSONTyped, +} from './ErrorResponseError'; + +/** + * The error response, including individual issues, if applicable + * @export + * @interface ErrorResponse + */ +export interface ErrorResponse { + /** + * + * @type {ErrorResponseError} + * @memberof ErrorResponse + */ + error: ErrorResponseError; + /** + * Individual validation issues, if applicable + * @type {{ [key: string]: Array; }} + * @memberof ErrorResponse + */ + issues?: { [key: string]: Array; }; +} + +/** + * Check if a given object implements the ErrorResponse interface. + */ +export function instanceOfErrorResponse(value: object): value is ErrorResponse { + if (!('error' in value) || value['error'] === undefined) return false; + return true; +} + +export function ErrorResponseFromJSON(json: any): ErrorResponse { + return ErrorResponseFromJSONTyped(json, false); +} + +export function ErrorResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): ErrorResponse { + if (json == null) { + return json; + } + return { + + 'error': ErrorResponseErrorFromJSON(json['error']), + 'issues': json['issues'] == null ? undefined : json['issues'], + }; +} + +export function ErrorResponseToJSON(json: any): ErrorResponse { + return ErrorResponseToJSONTyped(json, false); +} + +export function ErrorResponseToJSONTyped(value?: ErrorResponse | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'error': ErrorResponseErrorToJSON(value['error']), + 'issues': value['issues'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/ErrorResponseError.ts b/packages/rest-api-sdk/src/generated/models/ErrorResponseError.ts new file mode 100644 index 00000000..3180bf4e --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/ErrorResponseError.ts @@ -0,0 +1,92 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * The error + * @export + * @interface ErrorResponseError + */ +export interface ErrorResponseError { + /** + * Error code + * @type {string} + * @memberof ErrorResponseError + */ + code: ErrorResponseErrorCodeEnum; + /** + * Human readable error message + * @type {string} + * @memberof ErrorResponseError + */ + message: string; +} + + +/** + * @export + */ +export const ErrorResponseErrorCodeEnum = { + InvalidRequest: 'invalid_request', + NotFound: 'not_found', + NotPossible: 'not_possible', + NotAllowed: 'not_allowed', + NotAvailable: 'not_available', + UnknownError: 'unknown_error', + Unauthorized: 'unauthorized', + Unauthenticated: 'unauthenticated' +} as const; +export type ErrorResponseErrorCodeEnum = typeof ErrorResponseErrorCodeEnum[keyof typeof ErrorResponseErrorCodeEnum]; + + +/** + * Check if a given object implements the ErrorResponseError interface. + */ +export function instanceOfErrorResponseError(value: object): value is ErrorResponseError { + if (!('code' in value) || value['code'] === undefined) return false; + if (!('message' in value) || value['message'] === undefined) return false; + return true; +} + +export function ErrorResponseErrorFromJSON(json: any): ErrorResponseError { + return ErrorResponseErrorFromJSONTyped(json, false); +} + +export function ErrorResponseErrorFromJSONTyped(json: any, ignoreDiscriminator: boolean): ErrorResponseError { + if (json == null) { + return json; + } + return { + + 'code': json['code'], + 'message': json['message'], + }; +} + +export function ErrorResponseErrorToJSON(json: any): ErrorResponseError { + return ErrorResponseErrorToJSONTyped(json, false); +} + +export function ErrorResponseErrorToJSONTyped(value?: ErrorResponseError | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'code': value['code'], + 'message': value['message'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/FlagHeader.ts b/packages/rest-api-sdk/src/generated/models/FlagHeader.ts new file mode 100644 index 00000000..fb2fc853 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/FlagHeader.ts @@ -0,0 +1,174 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { StageHeader } from './StageHeader'; +import { + StageHeaderFromJSON, + StageHeaderFromJSONTyped, + StageHeaderToJSON, + StageHeaderToJSONTyped, +} from './StageHeader'; +import type { ReflagUserHeader } from './ReflagUserHeader'; +import { + ReflagUserHeaderFromJSON, + ReflagUserHeaderFromJSONTyped, + ReflagUserHeaderToJSON, + ReflagUserHeaderToJSONTyped, +} from './ReflagUserHeader'; + +/** + * Basic flag information + * @export + * @interface FlagHeader + */ +export interface FlagHeader { + /** + * Flag ID + * @type {string} + * @memberof FlagHeader + */ + id: string; + /** + * Unique flag key + * @type {string} + * @memberof FlagHeader + */ + key: string; + /** + * Flag name + * @type {string} + * @memberof FlagHeader + */ + name: string; + /** + * Flag description + * @type {string} + * @memberof FlagHeader + */ + description?: string; + /** + * + * @type {StageHeader} + * @memberof FlagHeader + */ + stage?: StageHeader; + /** + * + * @type {ReflagUserHeader} + * @memberof FlagHeader + */ + owner?: ReflagUserHeader; + /** + * Whether the flag is archived + * @type {boolean} + * @memberof FlagHeader + */ + archived: boolean; + /** + * Whether the flag is stale + * @type {boolean} + * @memberof FlagHeader + */ + stale: boolean; + /** + * Whether the flag is permanent + * @type {boolean} + * @memberof FlagHeader + */ + permanent: boolean; + /** + * Timestamp when the flag was created + * @type {string} + * @memberof FlagHeader + */ + createdAt?: string; + /** + * Timestamp when the flag was last checked + * @type {string} + * @memberof FlagHeader + */ + lastCheckAt?: string; + /** + * Timestamp when the flag was last tracked + * @type {string} + * @memberof FlagHeader + */ + lastTrackAt?: string; +} + +/** + * Check if a given object implements the FlagHeader interface. + */ +export function instanceOfFlagHeader(value: object): value is FlagHeader { + if (!('id' in value) || value['id'] === undefined) return false; + if (!('key' in value) || value['key'] === undefined) return false; + if (!('name' in value) || value['name'] === undefined) return false; + if (!('archived' in value) || value['archived'] === undefined) return false; + if (!('stale' in value) || value['stale'] === undefined) return false; + if (!('permanent' in value) || value['permanent'] === undefined) return false; + return true; +} + +export function FlagHeaderFromJSON(json: any): FlagHeader { + return FlagHeaderFromJSONTyped(json, false); +} + +export function FlagHeaderFromJSONTyped(json: any, ignoreDiscriminator: boolean): FlagHeader { + if (json == null) { + return json; + } + return { + + 'id': json['id'], + 'key': json['key'], + 'name': json['name'], + 'description': json['description'] == null ? undefined : json['description'], + 'stage': json['stage'] == null ? undefined : StageHeaderFromJSON(json['stage']), + 'owner': json['owner'] == null ? undefined : ReflagUserHeaderFromJSON(json['owner']), + 'archived': json['archived'], + 'stale': json['stale'], + 'permanent': json['permanent'], + 'createdAt': json['createdAt'] == null ? undefined : json['createdAt'], + 'lastCheckAt': json['lastCheckAt'] == null ? undefined : json['lastCheckAt'], + 'lastTrackAt': json['lastTrackAt'] == null ? undefined : json['lastTrackAt'], + }; +} + +export function FlagHeaderToJSON(json: any): FlagHeader { + return FlagHeaderToJSONTyped(json, false); +} + +export function FlagHeaderToJSONTyped(value?: FlagHeader | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'id': value['id'], + 'key': value['key'], + 'name': value['name'], + 'description': value['description'], + 'stage': StageHeaderToJSON(value['stage']), + 'owner': ReflagUserHeaderToJSON(value['owner']), + 'archived': value['archived'], + 'stale': value['stale'], + 'permanent': value['permanent'], + 'createdAt': value['createdAt'], + 'lastCheckAt': value['lastCheckAt'], + 'lastTrackAt': value['lastTrackAt'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/FlagHeaderCollection.ts b/packages/rest-api-sdk/src/generated/models/FlagHeaderCollection.ts new file mode 100644 index 00000000..d966999b --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/FlagHeaderCollection.ts @@ -0,0 +1,146 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { FlagHeader } from './FlagHeader'; +import { + FlagHeaderFromJSON, + FlagHeaderFromJSONTyped, + FlagHeaderToJSON, + FlagHeaderToJSONTyped, +} from './FlagHeader'; +import type { SortOrder } from './SortOrder'; +import { + SortOrderFromJSON, + SortOrderFromJSONTyped, + SortOrderToJSON, + SortOrderToJSONTyped, +} from './SortOrder'; + +/** + * Collection response containing flags + * @export + * @interface FlagHeaderCollection + */ +export interface FlagHeaderCollection { + /** + * Page of the collection of flags + * @type {Array} + * @memberof FlagHeaderCollection + */ + data: Array; + /** + * Total number of flags in collection + * @type {number} + * @memberof FlagHeaderCollection + */ + totalCount: number; + /** + * Page size + * @type {number} + * @memberof FlagHeaderCollection + */ + pageSize: number; + /** + * Page index + * @type {number} + * @memberof FlagHeaderCollection + */ + pageIndex: number; + /** + * Sort by + * @type {string} + * @memberof FlagHeaderCollection + */ + sortBy: FlagHeaderCollectionSortByEnum; + /** + * Sort order + * @type {SortOrder} + * @memberof FlagHeaderCollection + */ + sortOrder: SortOrder; +} + + +/** + * @export + */ +export const FlagHeaderCollectionSortByEnum = { + Name: 'name', + Key: 'key', + Stage: 'stage', + AutoFeedbackSurveysEnabled: 'autoFeedbackSurveysEnabled', + CreatedAt: 'createdAt', + EnvironmentStatus: 'environmentStatus', + Owner: 'owner', + LastCheck: 'lastCheck', + LastTrack: 'lastTrack', + Stale: 'stale', + ArchivingChecks: 'archivingChecks' +} as const; +export type FlagHeaderCollectionSortByEnum = typeof FlagHeaderCollectionSortByEnum[keyof typeof FlagHeaderCollectionSortByEnum]; + + +/** + * Check if a given object implements the FlagHeaderCollection interface. + */ +export function instanceOfFlagHeaderCollection(value: object): value is FlagHeaderCollection { + if (!('data' in value) || value['data'] === undefined) return false; + if (!('totalCount' in value) || value['totalCount'] === undefined) return false; + if (!('pageSize' in value) || value['pageSize'] === undefined) return false; + if (!('pageIndex' in value) || value['pageIndex'] === undefined) return false; + if (!('sortBy' in value) || value['sortBy'] === undefined) return false; + if (!('sortOrder' in value) || value['sortOrder'] === undefined) return false; + return true; +} + +export function FlagHeaderCollectionFromJSON(json: any): FlagHeaderCollection { + return FlagHeaderCollectionFromJSONTyped(json, false); +} + +export function FlagHeaderCollectionFromJSONTyped(json: any, ignoreDiscriminator: boolean): FlagHeaderCollection { + if (json == null) { + return json; + } + return { + + 'data': ((json['data'] as Array).map(FlagHeaderFromJSON)), + 'totalCount': json['totalCount'], + 'pageSize': json['pageSize'], + 'pageIndex': json['pageIndex'], + 'sortBy': json['sortBy'], + 'sortOrder': SortOrderFromJSON(json['sortOrder']), + }; +} + +export function FlagHeaderCollectionToJSON(json: any): FlagHeaderCollection { + return FlagHeaderCollectionToJSONTyped(json, false); +} + +export function FlagHeaderCollectionToJSONTyped(value?: FlagHeaderCollection | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'data': ((value['data'] as Array).map(FlagHeaderToJSON)), + 'totalCount': value['totalCount'], + 'pageSize': value['pageSize'], + 'pageIndex': value['pageIndex'], + 'sortBy': value['sortBy'], + 'sortOrder': SortOrderToJSON(value['sortOrder']), + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/FlagKeyFormat.ts b/packages/rest-api-sdk/src/generated/models/FlagKeyFormat.ts new file mode 100644 index 00000000..10e35969 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/FlagKeyFormat.ts @@ -0,0 +1,58 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * The enforced key format when creating flags + * @export + */ +export const FlagKeyFormat = { + Custom: 'custom', + PascalCase: 'pascalCase', + CamelCase: 'camelCase', + SnakeCaseUpper: 'snakeCaseUpper', + SnakeCaseLower: 'snakeCaseLower', + KebabCaseUpper: 'kebabCaseUpper', + KebabCaseLower: 'kebabCaseLower' +} as const; +export type FlagKeyFormat = typeof FlagKeyFormat[keyof typeof FlagKeyFormat]; + + +export function instanceOfFlagKeyFormat(value: any): boolean { + for (const key in FlagKeyFormat) { + if (Object.prototype.hasOwnProperty.call(FlagKeyFormat, key)) { + if (FlagKeyFormat[key as keyof typeof FlagKeyFormat] === value) { + return true; + } + } + } + return false; +} + +export function FlagKeyFormatFromJSON(json: any): FlagKeyFormat { + return FlagKeyFormatFromJSONTyped(json, false); +} + +export function FlagKeyFormatFromJSONTyped(json: any, ignoreDiscriminator: boolean): FlagKeyFormat { + return json as FlagKeyFormat; +} + +export function FlagKeyFormatToJSON(value?: FlagKeyFormat | null): any { + return value as any; +} + +export function FlagKeyFormatToJSONTyped(value: any, ignoreDiscriminator: boolean): FlagKeyFormat { + return value as FlagKeyFormat; +} + diff --git a/packages/rest-api-sdk/src/generated/models/FlagTargeting.ts b/packages/rest-api-sdk/src/generated/models/FlagTargeting.ts new file mode 100644 index 00000000..15fc9d2f --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/FlagTargeting.ts @@ -0,0 +1,101 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { FlagValueTargeting } from './FlagValueTargeting'; +import { + FlagValueTargetingFromJSON, + FlagValueTargetingFromJSONTyped, + FlagValueTargetingToJSON, + FlagValueTargetingToJSONTyped, +} from './FlagValueTargeting'; + +/** + * Flag targeting information and its audience + * @export + * @interface FlagTargeting + */ +export interface FlagTargeting { + /** + * Unique flag key + * @type {string} + * @memberof FlagTargeting + */ + flagKey: string; + /** + * Flag targeting version + * @type {number} + * @memberof FlagTargeting + */ + version: number; + /** + * Last time the targeting was updated + * @type {Date} + * @memberof FlagTargeting + */ + updatedAt: Date; + /** + * The flag targeting for each value + * @type {{ [key: string]: FlagValueTargeting; }} + * @memberof FlagTargeting + */ + specificTargets: { [key: string]: FlagValueTargeting; }; +} + +/** + * Check if a given object implements the FlagTargeting interface. + */ +export function instanceOfFlagTargeting(value: object): value is FlagTargeting { + if (!('flagKey' in value) || value['flagKey'] === undefined) return false; + if (!('version' in value) || value['version'] === undefined) return false; + if (!('updatedAt' in value) || value['updatedAt'] === undefined) return false; + if (!('specificTargets' in value) || value['specificTargets'] === undefined) return false; + return true; +} + +export function FlagTargetingFromJSON(json: any): FlagTargeting { + return FlagTargetingFromJSONTyped(json, false); +} + +export function FlagTargetingFromJSONTyped(json: any, ignoreDiscriminator: boolean): FlagTargeting { + if (json == null) { + return json; + } + return { + + 'flagKey': json['flagKey'], + 'version': json['version'], + 'updatedAt': (new Date(json['updatedAt'])), + 'specificTargets': (mapValues(json['specificTargets'], FlagValueTargetingFromJSON)), + }; +} + +export function FlagTargetingToJSON(json: any): FlagTargeting { + return FlagTargetingToJSONTyped(json, false); +} + +export function FlagTargetingToJSONTyped(value?: FlagTargeting | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'flagKey': value['flagKey'], + 'version': value['version'], + 'updatedAt': value['updatedAt'].toISOString(), + 'specificTargets': (mapValues(value['specificTargets'], FlagValueTargetingToJSON)), + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/FlagTargetingCollection.ts b/packages/rest-api-sdk/src/generated/models/FlagTargetingCollection.ts new file mode 100644 index 00000000..e00e7977 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/FlagTargetingCollection.ts @@ -0,0 +1,74 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { FlagTargeting } from './FlagTargeting'; +import { + FlagTargetingFromJSON, + FlagTargetingFromJSONTyped, + FlagTargetingToJSON, + FlagTargetingToJSONTyped, +} from './FlagTargeting'; + +/** + * Collection response containing flags' targeting information + * @export + * @interface FlagTargetingCollection + */ +export interface FlagTargetingCollection { + /** + * Collection of flags' targeting information + * @type {Array} + * @memberof FlagTargetingCollection + */ + data: Array; +} + +/** + * Check if a given object implements the FlagTargetingCollection interface. + */ +export function instanceOfFlagTargetingCollection(value: object): value is FlagTargetingCollection { + if (!('data' in value) || value['data'] === undefined) return false; + return true; +} + +export function FlagTargetingCollectionFromJSON(json: any): FlagTargetingCollection { + return FlagTargetingCollectionFromJSONTyped(json, false); +} + +export function FlagTargetingCollectionFromJSONTyped(json: any, ignoreDiscriminator: boolean): FlagTargetingCollection { + if (json == null) { + return json; + } + return { + + 'data': ((json['data'] as Array).map(FlagTargetingFromJSON)), + }; +} + +export function FlagTargetingCollectionToJSON(json: any): FlagTargetingCollection { + return FlagTargetingCollectionToJSONTyped(json, false); +} + +export function FlagTargetingCollectionToJSONTyped(value?: FlagTargetingCollection | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'data': ((value['data'] as Array).map(FlagTargetingToJSON)), + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/FlagValue.ts b/packages/rest-api-sdk/src/generated/models/FlagValue.ts new file mode 100644 index 00000000..3c6441e9 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/FlagValue.ts @@ -0,0 +1,52 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * The value of the flag served to the audience. + * @export + */ +export const FlagValue = { + True: 'true' +} as const; +export type FlagValue = typeof FlagValue[keyof typeof FlagValue]; + + +export function instanceOfFlagValue(value: any): boolean { + for (const key in FlagValue) { + if (Object.prototype.hasOwnProperty.call(FlagValue, key)) { + if (FlagValue[key as keyof typeof FlagValue] === value) { + return true; + } + } + } + return false; +} + +export function FlagValueFromJSON(json: any): FlagValue { + return FlagValueFromJSONTyped(json, false); +} + +export function FlagValueFromJSONTyped(json: any, ignoreDiscriminator: boolean): FlagValue { + return json as FlagValue; +} + +export function FlagValueToJSON(value?: FlagValue | null): any { + return value as any; +} + +export function FlagValueToJSONTyped(value: any, ignoreDiscriminator: boolean): FlagValue { + return value as FlagValue; +} + diff --git a/packages/rest-api-sdk/src/generated/models/FlagValueTargeting.ts b/packages/rest-api-sdk/src/generated/models/FlagValueTargeting.ts new file mode 100644 index 00000000..b0e2c8bb --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/FlagValueTargeting.ts @@ -0,0 +1,75 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * Flag targeting value and its audience + * @export + * @interface FlagValueTargeting + */ +export interface FlagValueTargeting { + /** + * Companies that were explicitly given the value + * @type {Array} + * @memberof FlagValueTargeting + */ + companyIds: Array; + /** + * Users that were explicitly given the value + * @type {Array} + * @memberof FlagValueTargeting + */ + userIds: Array; +} + +/** + * Check if a given object implements the FlagValueTargeting interface. + */ +export function instanceOfFlagValueTargeting(value: object): value is FlagValueTargeting { + if (!('companyIds' in value) || value['companyIds'] === undefined) return false; + if (!('userIds' in value) || value['userIds'] === undefined) return false; + return true; +} + +export function FlagValueTargetingFromJSON(json: any): FlagValueTargeting { + return FlagValueTargetingFromJSONTyped(json, false); +} + +export function FlagValueTargetingFromJSONTyped(json: any, ignoreDiscriminator: boolean): FlagValueTargeting { + if (json == null) { + return json; + } + return { + + 'companyIds': json['companyIds'], + 'userIds': json['userIds'], + }; +} + +export function FlagValueTargetingToJSON(json: any): FlagValueTargeting { + return FlagValueTargetingToJSONTyped(json, false); +} + +export function FlagValueTargetingToJSONTyped(value?: FlagValueTargeting | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'companyIds': value['companyIds'], + 'userIds': value['userIds'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/OrgHeader.ts b/packages/rest-api-sdk/src/generated/models/OrgHeader.ts new file mode 100644 index 00000000..c4355a93 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/OrgHeader.ts @@ -0,0 +1,75 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * Organization's basic information + * @export + * @interface OrgHeader + */ +export interface OrgHeader { + /** + * Organization identifier + * @type {string} + * @memberof OrgHeader + */ + id: string; + /** + * Organization name + * @type {string} + * @memberof OrgHeader + */ + name: string; +} + +/** + * Check if a given object implements the OrgHeader interface. + */ +export function instanceOfOrgHeader(value: object): value is OrgHeader { + if (!('id' in value) || value['id'] === undefined) return false; + if (!('name' in value) || value['name'] === undefined) return false; + return true; +} + +export function OrgHeaderFromJSON(json: any): OrgHeader { + return OrgHeaderFromJSONTyped(json, false); +} + +export function OrgHeaderFromJSONTyped(json: any, ignoreDiscriminator: boolean): OrgHeader { + if (json == null) { + return json; + } + return { + + 'id': json['id'], + 'name': json['name'], + }; +} + +export function OrgHeaderToJSON(json: any): OrgHeader { + return OrgHeaderToJSONTyped(json, false); +} + +export function OrgHeaderToJSONTyped(value?: OrgHeader | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'id': value['id'], + 'name': value['name'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/ReflagUserHeader.ts b/packages/rest-api-sdk/src/generated/models/ReflagUserHeader.ts new file mode 100644 index 00000000..b8c85a37 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/ReflagUserHeader.ts @@ -0,0 +1,92 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * Reflag user's basic information + * @export + * @interface ReflagUserHeader + */ +export interface ReflagUserHeader { + /** + * Reflag user identifier + * @type {string} + * @memberof ReflagUserHeader + */ + id: string; + /** + * User's name + * @type {string} + * @memberof ReflagUserHeader + */ + name: string; + /** + * User's email + * @type {string} + * @memberof ReflagUserHeader + */ + email: string; + /** + * User's avatar URL + * @type {string} + * @memberof ReflagUserHeader + */ + avatarUrl?: string; +} + +/** + * Check if a given object implements the ReflagUserHeader interface. + */ +export function instanceOfReflagUserHeader(value: object): value is ReflagUserHeader { + if (!('id' in value) || value['id'] === undefined) return false; + if (!('name' in value) || value['name'] === undefined) return false; + if (!('email' in value) || value['email'] === undefined) return false; + return true; +} + +export function ReflagUserHeaderFromJSON(json: any): ReflagUserHeader { + return ReflagUserHeaderFromJSONTyped(json, false); +} + +export function ReflagUserHeaderFromJSONTyped(json: any, ignoreDiscriminator: boolean): ReflagUserHeader { + if (json == null) { + return json; + } + return { + + 'id': json['id'], + 'name': json['name'], + 'email': json['email'], + 'avatarUrl': json['avatarUrl'] == null ? undefined : json['avatarUrl'], + }; +} + +export function ReflagUserHeaderToJSON(json: any): ReflagUserHeader { + return ReflagUserHeaderToJSONTyped(json, false); +} + +export function ReflagUserHeaderToJSONTyped(value?: ReflagUserHeader | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'id': value['id'], + 'name': value['name'], + 'email': value['email'], + 'avatarUrl': value['avatarUrl'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/SegmentHeader.ts b/packages/rest-api-sdk/src/generated/models/SegmentHeader.ts new file mode 100644 index 00000000..1b08c6e7 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/SegmentHeader.ts @@ -0,0 +1,94 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { SegmentType } from './SegmentType'; +import { + SegmentTypeFromJSON, + SegmentTypeFromJSONTyped, + SegmentTypeToJSON, + SegmentTypeToJSONTyped, +} from './SegmentType'; + +/** + * Segment's basic information + * @export + * @interface SegmentHeader + */ +export interface SegmentHeader { + /** + * Segment identifier + * @type {string} + * @memberof SegmentHeader + */ + id: string; + /** + * Segment name + * @type {string} + * @memberof SegmentHeader + */ + name: string; + /** + * + * @type {SegmentType} + * @memberof SegmentHeader + */ + type: SegmentType; +} + + + +/** + * Check if a given object implements the SegmentHeader interface. + */ +export function instanceOfSegmentHeader(value: object): value is SegmentHeader { + if (!('id' in value) || value['id'] === undefined) return false; + if (!('name' in value) || value['name'] === undefined) return false; + if (!('type' in value) || value['type'] === undefined) return false; + return true; +} + +export function SegmentHeaderFromJSON(json: any): SegmentHeader { + return SegmentHeaderFromJSONTyped(json, false); +} + +export function SegmentHeaderFromJSONTyped(json: any, ignoreDiscriminator: boolean): SegmentHeader { + if (json == null) { + return json; + } + return { + + 'id': json['id'], + 'name': json['name'], + 'type': SegmentTypeFromJSON(json['type']), + }; +} + +export function SegmentHeaderToJSON(json: any): SegmentHeader { + return SegmentHeaderToJSONTyped(json, false); +} + +export function SegmentHeaderToJSONTyped(value?: SegmentHeader | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'id': value['id'], + 'name': value['name'], + 'type': SegmentTypeToJSON(value['type']), + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/SegmentType.ts b/packages/rest-api-sdk/src/generated/models/SegmentType.ts new file mode 100644 index 00000000..e1c57595 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/SegmentType.ts @@ -0,0 +1,53 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Segment type + * @export + */ +export const SegmentType = { + All: 'all', + Custom: 'custom' +} as const; +export type SegmentType = typeof SegmentType[keyof typeof SegmentType]; + + +export function instanceOfSegmentType(value: any): boolean { + for (const key in SegmentType) { + if (Object.prototype.hasOwnProperty.call(SegmentType, key)) { + if (SegmentType[key as keyof typeof SegmentType] === value) { + return true; + } + } + } + return false; +} + +export function SegmentTypeFromJSON(json: any): SegmentType { + return SegmentTypeFromJSONTyped(json, false); +} + +export function SegmentTypeFromJSONTyped(json: any, ignoreDiscriminator: boolean): SegmentType { + return json as SegmentType; +} + +export function SegmentTypeToJSON(value?: SegmentType | null): any { + return value as any; +} + +export function SegmentTypeToJSONTyped(value: any, ignoreDiscriminator: boolean): SegmentType { + return value as SegmentType; +} + diff --git a/packages/rest-api-sdk/src/generated/models/SortOrder.ts b/packages/rest-api-sdk/src/generated/models/SortOrder.ts new file mode 100644 index 00000000..1aa9fc0c --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/SortOrder.ts @@ -0,0 +1,53 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Sort order applied to the sorting column + * @export + */ +export const SortOrder = { + Asc: 'asc', + Desc: 'desc' +} as const; +export type SortOrder = typeof SortOrder[keyof typeof SortOrder]; + + +export function instanceOfSortOrder(value: any): boolean { + for (const key in SortOrder) { + if (Object.prototype.hasOwnProperty.call(SortOrder, key)) { + if (SortOrder[key as keyof typeof SortOrder] === value) { + return true; + } + } + } + return false; +} + +export function SortOrderFromJSON(json: any): SortOrder { + return SortOrderFromJSONTyped(json, false); +} + +export function SortOrderFromJSONTyped(json: any, ignoreDiscriminator: boolean): SortOrder { + return json as SortOrder; +} + +export function SortOrderToJSON(value?: SortOrder | null): any { + return value as any; +} + +export function SortOrderToJSONTyped(value: any, ignoreDiscriminator: boolean): SortOrder { + return value as SortOrder; +} + diff --git a/packages/rest-api-sdk/src/generated/models/StageHeader.ts b/packages/rest-api-sdk/src/generated/models/StageHeader.ts new file mode 100644 index 00000000..c37d6a47 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/StageHeader.ts @@ -0,0 +1,102 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * Stage's basic information + * @export + * @interface StageHeader + */ +export interface StageHeader { + /** + * Stage identifier + * @type {string} + * @memberof StageHeader + */ + id: string; + /** + * Stage name + * @type {string} + * @memberof StageHeader + */ + name: string; + /** + * Stage color (HTML color name or hex code) + * @type {string} + * @memberof StageHeader + */ + color: string; + /** + * Flags currently in this stage + * @type {number} + * @memberof StageHeader + */ + assignedFlagCount: number; + /** + * Stage order + * @type {number} + * @memberof StageHeader + */ + order: number; +} + +/** + * Check if a given object implements the StageHeader interface. + */ +export function instanceOfStageHeader(value: object): value is StageHeader { + if (!('id' in value) || value['id'] === undefined) return false; + if (!('name' in value) || value['name'] === undefined) return false; + if (!('color' in value) || value['color'] === undefined) return false; + if (!('assignedFlagCount' in value) || value['assignedFlagCount'] === undefined) return false; + if (!('order' in value) || value['order'] === undefined) return false; + return true; +} + +export function StageHeaderFromJSON(json: any): StageHeader { + return StageHeaderFromJSONTyped(json, false); +} + +export function StageHeaderFromJSONTyped(json: any, ignoreDiscriminator: boolean): StageHeader { + if (json == null) { + return json; + } + return { + + 'id': json['id'], + 'name': json['name'], + 'color': json['color'], + 'assignedFlagCount': json['assignedFlagCount'], + 'order': json['order'], + }; +} + +export function StageHeaderToJSON(json: any): StageHeader { + return StageHeaderToJSONTyped(json, false); +} + +export function StageHeaderToJSONTyped(value?: StageHeader | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'id': value['id'], + 'name': value['name'], + 'color': value['color'], + 'assignedFlagCount': value['assignedFlagCount'], + 'order': value['order'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/UpdateEntityFlagsBody.ts b/packages/rest-api-sdk/src/generated/models/UpdateEntityFlagsBody.ts new file mode 100644 index 00000000..ab8ef8c7 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/UpdateEntityFlagsBody.ts @@ -0,0 +1,90 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { EntityFlagUpdate } from './EntityFlagUpdate'; +import { + EntityFlagUpdateFromJSON, + EntityFlagUpdateFromJSONTyped, + EntityFlagUpdateToJSON, + EntityFlagUpdateToJSONTyped, +} from './EntityFlagUpdate'; + +/** + * Request body for updating flags for an entity + * @export + * @interface UpdateEntityFlagsBody + */ +export interface UpdateEntityFlagsBody { + /** + * List of flag updates to apply + * @type {Array} + * @memberof UpdateEntityFlagsBody + */ + updates: Array; + /** + * Description of the change for audit history + * @type {string} + * @memberof UpdateEntityFlagsBody + */ + changeDescription?: string; + /** + * Whether to send notifications about the change (default: true) + * @type {boolean} + * @memberof UpdateEntityFlagsBody + */ + notifications?: boolean; +} + +/** + * Check if a given object implements the UpdateEntityFlagsBody interface. + */ +export function instanceOfUpdateEntityFlagsBody(value: object): value is UpdateEntityFlagsBody { + if (!('updates' in value) || value['updates'] === undefined) return false; + return true; +} + +export function UpdateEntityFlagsBodyFromJSON(json: any): UpdateEntityFlagsBody { + return UpdateEntityFlagsBodyFromJSONTyped(json, false); +} + +export function UpdateEntityFlagsBodyFromJSONTyped(json: any, ignoreDiscriminator: boolean): UpdateEntityFlagsBody { + if (json == null) { + return json; + } + return { + + 'updates': ((json['updates'] as Array).map(EntityFlagUpdateFromJSON)), + 'changeDescription': json['changeDescription'] == null ? undefined : json['changeDescription'], + 'notifications': json['notifications'] == null ? undefined : json['notifications'], + }; +} + +export function UpdateEntityFlagsBodyToJSON(json: any): UpdateEntityFlagsBody { + return UpdateEntityFlagsBodyToJSONTyped(json, false); +} + +export function UpdateEntityFlagsBodyToJSONTyped(value?: UpdateEntityFlagsBody | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'updates': ((value['updates'] as Array).map(EntityFlagUpdateToJSON)), + 'changeDescription': value['changeDescription'], + 'notifications': value['notifications'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargets.ts b/packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargets.ts new file mode 100644 index 00000000..293ffcb8 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargets.ts @@ -0,0 +1,99 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { UpdateFlagSpecificTargetsValue } from './UpdateFlagSpecificTargetsValue'; +import { + UpdateFlagSpecificTargetsValueFromJSON, + UpdateFlagSpecificTargetsValueFromJSONTyped, + UpdateFlagSpecificTargetsValueToJSON, + UpdateFlagSpecificTargetsValueToJSONTyped, +} from './UpdateFlagSpecificTargetsValue'; + +/** + * Update the explicit value of the flag for a given audience (null means to remove the flag from the audience). + * @export + * @interface UpdateFlagSpecificTargets + */ +export interface UpdateFlagSpecificTargets { + /** + * Unique flag key + * @type {string} + * @memberof UpdateFlagSpecificTargets + */ + flagKey: string; + /** + * + * @type {UpdateFlagSpecificTargetsValue} + * @memberof UpdateFlagSpecificTargets + */ + value: UpdateFlagSpecificTargetsValue | null; + /** + * Company ID within your application + * @type {string} + * @memberof UpdateFlagSpecificTargets + */ + companyId?: string; + /** + * User ID within your application + * @type {string} + * @memberof UpdateFlagSpecificTargets + */ + userId?: string; +} + +/** + * Check if a given object implements the UpdateFlagSpecificTargets interface. + */ +export function instanceOfUpdateFlagSpecificTargets(value: object): value is UpdateFlagSpecificTargets { + if (!('flagKey' in value) || value['flagKey'] === undefined) return false; + if (!('value' in value) || value['value'] === undefined) return false; + return true; +} + +export function UpdateFlagSpecificTargetsFromJSON(json: any): UpdateFlagSpecificTargets { + return UpdateFlagSpecificTargetsFromJSONTyped(json, false); +} + +export function UpdateFlagSpecificTargetsFromJSONTyped(json: any, ignoreDiscriminator: boolean): UpdateFlagSpecificTargets { + if (json == null) { + return json; + } + return { + + 'flagKey': json['flagKey'], + 'value': UpdateFlagSpecificTargetsValueFromJSON(json['value']), + 'companyId': json['companyId'] == null ? undefined : json['companyId'], + 'userId': json['userId'] == null ? undefined : json['userId'], + }; +} + +export function UpdateFlagSpecificTargetsToJSON(json: any): UpdateFlagSpecificTargets { + return UpdateFlagSpecificTargetsToJSONTyped(json, false); +} + +export function UpdateFlagSpecificTargetsToJSONTyped(value?: UpdateFlagSpecificTargets | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'flagKey': value['flagKey'], + 'value': UpdateFlagSpecificTargetsValueToJSON(value['value']), + 'companyId': value['companyId'], + 'userId': value['userId'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargetsValue.ts b/packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargetsValue.ts new file mode 100644 index 00000000..1cc3158b --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargetsValue.ts @@ -0,0 +1,46 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * The value of the flag for the given audience (null means to remove the flag from the audience). + * @export + * @interface UpdateFlagSpecificTargetsValue + */ +export interface UpdateFlagSpecificTargetsValue { +} + +/** + * Check if a given object implements the UpdateFlagSpecificTargetsValue interface. + */ +export function instanceOfUpdateFlagSpecificTargetsValue(value: object): value is UpdateFlagSpecificTargetsValue { + return true; +} + +export function UpdateFlagSpecificTargetsValueFromJSON(json: any): UpdateFlagSpecificTargetsValue { + return UpdateFlagSpecificTargetsValueFromJSONTyped(json, false); +} + +export function UpdateFlagSpecificTargetsValueFromJSONTyped(json: any, ignoreDiscriminator: boolean): UpdateFlagSpecificTargetsValue { + return json; +} + +export function UpdateFlagSpecificTargetsValueToJSON(json: any): UpdateFlagSpecificTargetsValue { + return UpdateFlagSpecificTargetsValueToJSONTyped(json, false); +} + +export function UpdateFlagSpecificTargetsValueToJSONTyped(value?: UpdateFlagSpecificTargetsValue | null, ignoreDiscriminator: boolean = false): any { + return value; +} + diff --git a/packages/rest-api-sdk/src/generated/models/index.ts b/packages/rest-api-sdk/src/generated/models/index.ts new file mode 100644 index 00000000..0bef5ad9 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/index.ts @@ -0,0 +1,32 @@ +/* tslint:disable */ +/* eslint-disable */ +export * from './App'; +export * from './AppHeader'; +export * from './AppHeaderCollection'; +export * from './BulkUpdateFlagSpecificTargetsSchema'; +export * from './EntityFlag'; +export * from './EntityFlagUpdate'; +export * from './EntityFlagsResponse'; +export * from './Environment'; +export * from './EnvironmentHeader'; +export * from './EnvironmentHeaderCollection'; +export * from './EnvironmentHeaderSortByColumn'; +export * from './EnvironmentSdkAccess'; +export * from './ErrorResponse'; +export * from './ErrorResponseError'; +export * from './FlagHeader'; +export * from './FlagHeaderCollection'; +export * from './FlagKeyFormat'; +export * from './FlagTargeting'; +export * from './FlagTargetingCollection'; +export * from './FlagValue'; +export * from './FlagValueTargeting'; +export * from './OrgHeader'; +export * from './ReflagUserHeader'; +export * from './SegmentHeader'; +export * from './SegmentType'; +export * from './SortOrder'; +export * from './StageHeader'; +export * from './UpdateEntityFlagsBody'; +export * from './UpdateFlagSpecificTargets'; +export * from './UpdateFlagSpecificTargetsValue'; diff --git a/packages/rest-api-sdk/src/generated/runtime.ts b/packages/rest-api-sdk/src/generated/runtime.ts new file mode 100644 index 00000000..357f66d9 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/runtime.ts @@ -0,0 +1,432 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export const BASE_PATH = "https://app.reflag.com/api".replace(/\/+$/, ""); + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string; // parameter for basic security + password?: string; // parameter for basic security + apiKey?: string | Promise | ((name: string) => string | Promise); // parameter for apiKey security + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + constructor(private configuration: ConfigurationParameters = {}) {} + + set config(configuration: Configuration) { + this.configuration = configuration; + } + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): string | undefined { + return this.configuration.username; + } + + get password(): string | undefined { + return this.configuration.password; + } + + get apiKey(): ((name: string) => string | Promise) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export const DefaultConfig = new Configuration(); + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + + private static readonly jsonRegex = new RegExp('^(:?application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', 'i'); + private middleware: Middleware[]; + + constructor(protected configuration = DefaultConfig) { + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + protected isJsonMime(mime: string | null | undefined): boolean { + if (!mime) { + return false; + } + return BaseAPI.jsonRegex.test(mime); + } + + protected async request(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction): Promise { + const { url, init } = await this.createFetchParams(context, initOverrides); + const response = await this.fetchApi(url, init); + if (response && (response.status >= 200 && response.status < 300)) { + return response; + } + throw new ResponseError(response, 'Response returned an error code'); + } + + private async createFetchParams(context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + + const headers = Object.assign({}, this.configuration.headers, context.headers); + Object.keys(headers).forEach(key => headers[key] === undefined ? delete headers[key] : {}); + + const initOverrideFn = + typeof initOverrides === "function" + ? initOverrides + : async () => initOverrides; + + const initParams = { + method: context.method, + headers, + body: context.body, + credentials: this.configuration.credentials, + }; + + const overriddenInit: RequestInit = { + ...initParams, + ...(await initOverrideFn({ + init: initParams, + context, + })) + }; + + let body: any; + if (isFormData(overriddenInit.body) + || (overriddenInit.body instanceof URLSearchParams) + || isBlob(overriddenInit.body)) { + body = overriddenInit.body; + } else if (this.isJsonMime(headers['Content-Type'])) { + body = JSON.stringify(overriddenInit.body); + } else { + body = overriddenInit.body; + } + + const init: RequestInit = { + ...overriddenInit, + body + }; + + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + }) || fetchParams; + } + } + let response: Response | undefined = undefined; + try { + response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + } catch (e) { + for (const middleware of this.middleware) { + if (middleware.onError) { + response = await middleware.onError({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + error: e, + response: response ? response.clone() : undefined, + }) || response; + } + } + if (response === undefined) { + if (e instanceof Error) { + throw new FetchError(e, 'The request failed and the interceptors did not return an alternative response'); + } else { + throw e; + } + } + } + for (const middleware of this.middleware) { + if (middleware.post) { + response = await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + }) || response; + } + } + return response; + } + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as any; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +}; + +function isBlob(value: any): value is Blob { + return typeof Blob !== 'undefined' && value instanceof Blob; +} + +function isFormData(value: any): value is FormData { + return typeof FormData !== "undefined" && value instanceof FormData; +} + +export class ResponseError extends Error { + override name: "ResponseError" = "ResponseError"; + constructor(public response: Response, msg?: string) { + super(msg); + } +} + +export class FetchError extends Error { + override name: "FetchError" = "FetchError"; + constructor(public cause: Error, msg?: string) { + super(msg); + } +} + +export class RequiredError extends Error { + override name: "RequiredError" = "RequiredError"; + constructor(public field: string, msg?: string) { + super(msg); + } +} + +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +export type FetchAPI = WindowOrWorkerGlobalScope['fetch']; + +export type Json = any; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { [key: string]: string | number | null | boolean | Array | Set | HTTPQuery }; +export type HTTPBody = Json | FormData | URLSearchParams; +export type HTTPRequestInit = { headers?: HTTPHeaders; method: HTTPMethod; credentials?: RequestCredentials; body?: HTTPBody }; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export type InitOverrideFunction = (requestContext: { init: HTTPRequestInit, context: RequestOpts }) => Promise + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map(key => querystringSingleKey(key, params[key], prefix)) + .filter(part => part.length > 0) + .join('&'); +} + +function querystringSingleKey(key: string, value: string | number | null | undefined | boolean | Array | Set | HTTPQuery, keyPrefix: string = ''): string { + const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key); + if (value instanceof Array) { + const multiValue = value.map(singleValue => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Set) { + const valueAsArray = Array.from(value); + return querystringSingleKey(key, valueAsArray, keyPrefix); + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; +} + +export function exists(json: any, key: string) { + const value = json[key]; + return value !== null && value !== undefined; +} + +export function mapValues(data: any, fn: (item: any) => any) { + const result: { [key: string]: any } = {}; + for (const key of Object.keys(data)) { + result[key] = fn(data[key]); + } + return result; +} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string; +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface ErrorContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + error: unknown; + response?: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; + onError?(context: ErrorContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export interface ResponseTransformer { + (json: any): T; +} + +export class JSONApiResponse { + constructor(public raw: Response, private transformer: ResponseTransformer = (jsonValue: any) => jsonValue) {} + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.blob(); + }; +} + +export class TextApiResponse { + constructor(public raw: Response) {} + + async value(): Promise { + return await this.raw.text(); + }; +} diff --git a/packages/rest-api-sdk/src/index.ts b/packages/rest-api-sdk/src/index.ts new file mode 100644 index 00000000..1f63fb02 --- /dev/null +++ b/packages/rest-api-sdk/src/index.ts @@ -0,0 +1,3 @@ +export * from "./generated"; +export { Api, ReflagApiError, createAppClient, withAppId } from "./api"; +export type { AppScopedApi } from "./api"; diff --git a/packages/rest-api-sdk/tsconfig.build.json b/packages/rest-api-sdk/tsconfig.build.json new file mode 100644 index 00000000..daa1b256 --- /dev/null +++ b/packages/rest-api-sdk/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noUnusedLocals": false, + "noUnusedParameters": false + }, + "include": ["src"] +} diff --git a/packages/rest-api-sdk/tsconfig.eslint.json b/packages/rest-api-sdk/tsconfig.eslint.json new file mode 100644 index 00000000..66a0c293 --- /dev/null +++ b/packages/rest-api-sdk/tsconfig.eslint.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["./src", "./*.ts"] +} diff --git a/packages/rest-api-sdk/tsconfig.json b/packages/rest-api-sdk/tsconfig.json new file mode 100644 index 00000000..af496590 --- /dev/null +++ b/packages/rest-api-sdk/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@reflag/tsconfig/library", + "compilerOptions": { + "lib": ["ES2020", "DOM"], + "outDir": "./dist/", + "declarationDir": "./dist/types", + "declarationMap": true, + "noUncheckedIndexedAccess": true + }, + "include": ["src"], + "typeRoots": ["./node_modules/@types"] +} diff --git a/reflag-openapi.json b/reflag-openapi.json new file mode 100644 index 00000000..4e9a0129 --- /dev/null +++ b/reflag-openapi.json @@ -0,0 +1 @@ +{"openapi":"3.1.0","info":{"title":"Reflag API","version":"1.3.0","description":"Feature flag management API","contact":{"name":"Reflag Support","url":"https://reflag.com/support"},"license":{"name":"Proprietary","url":"https://reflag.com/"}},"servers":[{"url":"https://app.reflag.com/api","description":"Production server"}],"security":[{"APIKey":[]}],"paths":{"/apps/{appId}":{"get":{"summary":"Get details of an application","description":"Retrieve a specific application by its identifier","operationId":"getApp","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"query","name":"expand","schema":{"default":[],"type":"array","items":{"$ref":"#/components/schemas/appHeaderWithExpansionsOptionalFields"}}}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/appHeaderWithExpansions"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps":{"get":{"summary":"List of applications","description":"Retrieve all accessible applications","operationId":"listApps","parameters":[{"in":"query","name":"orgId","schema":{"$ref":"#/components/schemas/orgId"}},{"in":"query","name":"expand","schema":{"default":[],"type":"array","items":{"$ref":"#/components/schemas/appHeaderWithExpansionsOptionalFields"}}}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/appHeaderWithExpansionsCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/environments":{"get":{"summary":"List environments for application","description":"Retrieve all environments for a specific application","operationId":"listEnvironments","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"query","name":"sortOrder","schema":{"$ref":"#/components/schemas/sortOrder"},"description":"Sort order applied to the sorting column"},{"in":"query","name":"sortBy","schema":{"$ref":"#/components/schemas/environmentHeaderSortByColumn"},"description":"The column to sort by"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/environmentHeaderSortableCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/environments/{envId}":{"get":{"summary":"Get environment details","description":"Retrieve details for a specific environment","operationId":"getEnvironment","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/environmentDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags":{"get":{"summary":"List flags for application","description":"Retrieve all flags for a specific application","operationId":"listFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/{flagKey}/targeting/{envId}":{"get":{"summary":"Get flag targeting for an environment","description":"Retrieve targeting for a flag in an environment","operationId":"getFlagTargeting","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"flagKey","schema":{"$ref":"#/components/schemas/flagKey"},"required":true,"description":"Unique flag key"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagTargeting"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/specific-targets/{envId}":{"patch":{"summary":"Update flag specific targets for an environment","description":"Update specific companies and users for flags in an environment","operationId":"updateBulkFlagSpecificTargets","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/bulkUpdateFlagSpecificTargetsSchema"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagTargetingCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/companies/{companyId}/flags":{"get":{"summary":"Get flags for a company","description":"Retrieve all flags with their targeting status for a specific company","operationId":"getCompanyFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"companyId","schema":{"$ref":"#/components/schemas/companyId"},"required":true,"description":"Company ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Update flag targeting for a company","description":"Update specific targeting for flags for a company in an environment","operationId":"updateCompanyFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"companyId","schema":{"$ref":"#/components/schemas/companyId"},"required":true,"description":"Company ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/updateEntityFlagsBody"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/users/{userId}/flags":{"get":{"summary":"Get flags for a user","description":"Retrieve all flags with their targeting status for a specific user","operationId":"getUserFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"userId","schema":{"$ref":"#/components/schemas/userId"},"required":true,"description":"User ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Update flag targeting for a user","description":"Update specific targeting for flags for a user in an environment","operationId":"updateUserFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"userId","schema":{"$ref":"#/components/schemas/userId"},"required":true,"description":"User ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/updateEntityFlagsBody"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"appId":{"description":"App identifier","type":"string","minLength":1},"appHeaderWithExpansionsOptionalFields":{"description":"Optional fields that can be included when requesting the app basic information","type":"string","enum":["environments","stages","segments"]},"orgId":{"description":"Organization identifier","type":"string","minLength":1},"sortOrder":{"description":"Sort order applied to the sorting column","default":"asc","type":"string","enum":["asc","desc"]},"environmentHeaderSortByColumn":{"description":"The column to sort by","default":"order","type":"string","enum":["name","order"]},"envId":{"description":"Environment identifier","type":"string","minLength":1},"flagKey":{"description":"Unique flag key","type":"string","minLength":1},"bulkUpdateFlagSpecificTargetsSchema":{"description":"Update the explicit value of multiple flags for a given audience","type":"object","properties":{"updates":{"description":"The list of updates to make to the flags' targeting","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/updateFlagSpecificTargets"}},"notifications":{"description":"Whether to send notifications about the change to configured integration (eg. Slack, Linear, etc). Defaults to true.","default":true,"type":"boolean"},"changeDescription":{"description":"The description of the change recorded in the change history","type":"string"}},"required":["updates"]},"updateFlagSpecificTargets":{"description":"Update the explicit value of the flag for a given audience (null means to remove the flag from the audience).","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"value":{"description":"The value of the flag for the given audience (null means to remove the flag from the audience).","anyOf":[{"type":"string","const":"true"},{"type":"boolean","const":true},{"type":"null"}]},"companyId":{"$ref":"#/components/schemas/companyId"},"userId":{"$ref":"#/components/schemas/userId"}},"required":["flagKey","value"]},"companyId":{"description":"Company ID within your application","type":"string","minLength":1},"userId":{"description":"User ID within your application","type":"string","minLength":1},"updateEntityFlagsBody":{"description":"Request body for updating flags for an entity","type":"object","properties":{"updates":{"description":"List of flag updates to apply","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/entityFlagUpdate"}},"changeDescription":{"description":"Description of the change for audit history","type":"string"},"notifications":{"description":"Whether to send notifications about the change (default: true)","default":true,"type":"boolean"}},"required":["updates"]},"entityFlagUpdate":{"description":"Update for a single flag's specific targeting","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"value":{"description":"Set to true to add specific targeting, or null to remove it","anyOf":[{"type":"boolean","const":true},{"type":"null"}]}},"required":["flagKey","value"]},"appHeaderWithExpansions":{"description":"App information with possible additional expansions","type":"object","properties":{"org":{"$ref":"#/components/schemas/orgHeader"},"id":{"$ref":"#/components/schemas/appId"},"name":{"description":"App name","type":"string"},"demo":{"description":"Whether the app is a demo app","type":"boolean"},"flagKeyFormat":{"$ref":"#/components/schemas/flagKeyFormat"},"environments":{"description":"Environments within the app (if expanded)","type":"array","items":{"$ref":"#/components/schemas/environmentHeader"}},"stages":{"description":"Stages within the app (if expanded)","type":"array","items":{"$ref":"#/components/schemas/stageHeader"}},"segments":{"description":"Segments within the app (if expanded)","type":"array","items":{"$ref":"#/components/schemas/segmentHeader"}}},"required":["org","id","name","demo","flagKeyFormat"],"additionalProperties":false},"orgHeader":{"description":"Organization's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/orgId"},"name":{"description":"Organization name","type":"string","minLength":1}},"required":["id","name"],"additionalProperties":false},"flagKeyFormat":{"description":"The enforced key format when creating flags","type":"string","enum":["custom","pascalCase","camelCase","snakeCaseUpper","snakeCaseLower","kebabCaseUpper","kebabCaseLower"]},"environmentHeader":{"description":"Basic environment information","type":"object","properties":{"id":{"$ref":"#/components/schemas/envId"},"name":{"description":"Environment name","type":"string"},"isProduction":{"description":"Whether the environment is a production environment","type":"boolean"},"order":{"description":"Environment order in the app (zero-indexed)","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name","isProduction","order"],"additionalProperties":false},"stageHeader":{"description":"Stage's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/stageId"},"name":{"description":"Stage name","type":"string","minLength":1},"color":{"description":"Stage color (HTML color name or hex code)","type":"string","minLength":1,"maxLength":64},"assignedFlagCount":{"description":"Flags currently in this stage","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"order":{"description":"Stage order","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name","color","assignedFlagCount","order"],"additionalProperties":false},"stageId":{"description":"Stage identifier","type":"string","minLength":1},"segmentHeader":{"description":"Segment's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/segmentId"},"name":{"description":"Segment name","type":"string","minLength":1},"type":{"$ref":"#/components/schemas/segmentType"}},"required":["id","name","type"],"additionalProperties":false},"segmentId":{"description":"Segment identifier","type":"string","minLength":1},"segmentType":{"description":"Segment type","type":"string","enum":["all","custom"]},"ErrorResponse":{"description":"The error response, including individual issues, if applicable","type":"object","properties":{"error":{"description":"The error","type":"object","properties":{"code":{"description":"Error code","type":"string","enum":["invalid_request","not_found","not_possible","not_allowed","not_available","unknown_error","unauthorized","unauthenticated"]},"message":{"description":"Human readable error message","type":"string"}},"required":["code","message"],"additionalProperties":false},"issues":{"description":"Individual validation issues, if applicable","type":"object","propertyNames":{"description":"The field that has the issue (uses dot notation). Empty string if the issue is at the root.","type":"string"},"additionalProperties":{"description":"Error messages for this field","type":"array","items":{"description":"The error message","type":"string"}}}},"required":["error"],"additionalProperties":false},"appHeaderWithExpansionsCollection":{"description":"Collection of App information with possible additional expansions","type":"object","properties":{"data":{"description":"The individual items in the collection","type":"array","items":{"$ref":"#/components/schemas/appHeaderWithExpansions"}}},"required":["data"],"additionalProperties":false},"environmentHeaderSortableCollection":{"description":"Collection of Basic environment information with sorting options","type":"object","properties":{"data":{"description":"The individual items in the collection","type":"array","items":{"$ref":"#/components/schemas/environmentHeader"}},"sortOrder":{"$ref":"#/components/schemas/sortOrder"},"sortBy":{"$ref":"#/components/schemas/environmentHeaderSortByColumn"}},"required":["data","sortOrder","sortBy"],"additionalProperties":false},"environmentDetails":{"description":"Environment details","type":"object","properties":{"id":{"$ref":"#/components/schemas/envId"},"name":{"description":"Environment name","type":"string"},"isProduction":{"description":"Whether the environment is a production environment","type":"boolean"},"order":{"description":"Environment order in the app (zero-indexed)","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sdkAccess":{"description":"SDK access details","type":"object","properties":{"publishableKey":{"description":"Publishable key","type":"string","minLength":1,"maxLength":36},"secretKey":{"description":"Secret key","type":"string","minLength":1,"maxLength":36}},"required":["publishableKey","secretKey"],"additionalProperties":false},"autoFeedbackSurveys":{"description":"Environment-specific auto feedback survey configuration","type":"object","properties":{"snoozePeriodSeconds":{"description":"Snooze period in seconds","default":604800,"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["snoozePeriodSeconds"],"additionalProperties":false},"integrations":{"description":"Environment-specific integration configuration","type":"object","properties":{"slack":{"description":"Default Slack configuration for the environment","type":"object","properties":{"channel":{"$ref":"#/components/schemas/inheritableSlackChannel"}},"required":["channel"],"additionalProperties":false}},"additionalProperties":false}},"required":["id","name","isProduction","order","sdkAccess","autoFeedbackSurveys","integrations"],"additionalProperties":false},"inheritableSlackChannel":{"description":"Slack channel or inherit","anyOf":[{"$ref":"#/components/schemas/slackChannel"},{"type":"string","const":"inherit"}]},"slackChannel":{"description":"Slack channel","type":"object","properties":{"id":{"$ref":"#/components/schemas/slackChannelId"},"name":{"$ref":"#/components/schemas/slackChannelName"}},"required":["id","name"],"additionalProperties":false},"slackChannelId":{"description":"Slack channel identifier","type":"string","minLength":1,"maxLength":128},"slackChannelName":{"description":"Slack channel name","type":"string","minLength":1,"maxLength":128},"flagHeaderCollection":{"description":"Collection response containing flags","type":"object","properties":{"data":{"description":"Page of the collection of flags","type":"array","items":{"$ref":"#/components/schemas/flagHeader"}},"totalCount":{"description":"Total number of flags in collection","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageSize":{"description":"Page size","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageIndex":{"description":"Page index","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sortBy":{"description":"Sort by","type":"string","enum":["name","key","stage","autoFeedbackSurveysEnabled","createdAt","environmentStatus","owner","lastCheck","lastTrack","stale","archivingChecks"]},"sortOrder":{"description":"Sort order","$ref":"#/components/schemas/sortOrder"}},"required":["data","totalCount","pageSize","pageIndex","sortBy","sortOrder"],"additionalProperties":false},"flagHeader":{"description":"Basic flag information","type":"object","properties":{"id":{"description":"Flag ID","type":"string","minLength":1},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"description":{"description":"Flag description","type":"string"},"stage":{"$ref":"#/components/schemas/stageHeader"},"owner":{"$ref":"#/components/schemas/reflagUserHeader"},"archived":{"description":"Whether the flag is archived","type":"boolean"},"stale":{"description":"Whether the flag is stale","type":"boolean"},"permanent":{"description":"Whether the flag is permanent","type":"boolean"},"createdAt":{"description":"Timestamp when the flag was created","type":"string"},"lastCheckAt":{"description":"Timestamp when the flag was last checked","type":"string"},"lastTrackAt":{"description":"Timestamp when the flag was last tracked","type":"string"}},"required":["id","key","name","archived","stale","permanent"],"additionalProperties":false},"reflagUserHeader":{"description":"Reflag user's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/reflagUserId"},"name":{"description":"User's name","type":"string","minLength":1},"email":{"description":"User's email","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"avatarUrl":{"description":"User's avatar URL","type":"string","format":"uri"}},"required":["id","name","email"],"additionalProperties":false},"reflagUserId":{"description":"Reflag user identifier","type":"string","minLength":1},"flagTargeting":{"description":"Flag targeting information and its audience","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"version":{"$ref":"#/components/schemas/flagVersion"},"updatedAt":{"description":"Last time the targeting was updated","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"specificTargets":{"description":"The flag targeting for each value","type":"object","propertyNames":{"$ref":"#/components/schemas/flagValue"},"additionalProperties":{"$ref":"#/components/schemas/flagValueTargeting"}}},"required":["flagKey","version","updatedAt","specificTargets"],"additionalProperties":false},"flagVersion":{"description":"Flag targeting version","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"flagValue":{"description":"The value of the flag served to the audience.","type":"string","const":"true"},"flagValueTargeting":{"description":"Flag targeting value and its audience","type":"object","properties":{"companyIds":{"description":"Companies that were explicitly given the value","type":"array","items":{"$ref":"#/components/schemas/companyId"}},"userIds":{"description":"Users that were explicitly given the value","type":"array","items":{"$ref":"#/components/schemas/userId"}}},"required":["companyIds","userIds"],"additionalProperties":false},"flagTargetingCollection":{"description":"Collection response containing flags' targeting information","type":"object","properties":{"data":{"description":"Collection of flags' targeting information","type":"array","items":{"$ref":"#/components/schemas/flagTargeting"}}},"required":["data"],"additionalProperties":false},"entityFlagsResponse":{"description":"Response containing flags for an entity","type":"object","properties":{"data":{"description":"List of flags with their enabled status","type":"array","items":{"$ref":"#/components/schemas/entityFlag"}},"totalCount":{"description":"Total number of flags","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageSize":{"description":"Page size","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageIndex":{"description":"Page index","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["data","totalCount","pageSize","pageIndex"],"additionalProperties":false},"entityFlag":{"description":"Flag information with enabled status for an entity","type":"object","properties":{"id":{"description":"Flag ID","type":"string","minLength":1},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"value":{"description":"Whether the flag is enabled for this entity","type":"boolean"},"specificallyTargetedValue":{"description":"Value if directly added via specific targets, null if not specifically targeted","anyOf":[{"type":"boolean"},{"type":"null"}]},"firstExposureAt":{"description":"First time the entity was exposed to this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastExposureAt":{"description":"Last time the entity was exposed to this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastCheckAt":{"description":"Last time the flag was checked for this entity","anyOf":[{"type":"string"},{"type":"null"}]},"exposureCount":{"description":"Number of times the entity was exposed to this flag","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"firstTrackAt":{"description":"First time a track event was recorded for this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastTrackAt":{"description":"Last time a track event was recorded for this flag","anyOf":[{"type":"string"},{"type":"null"}]},"trackCount":{"description":"Number of track events for this flag","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","key","name","value","specificallyTargetedValue","firstExposureAt","lastExposureAt","lastCheckAt","exposureCount","firstTrackAt","lastTrackAt","trackCount"],"additionalProperties":false}},"securitySchemes":{"APIKey":{"type":"http","scheme":"bearer","description":"API key authentication, for service access"}}}} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index d5b0fa83..03c7b9ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -671,6 +671,13 @@ __metadata: languageName: node linkType: hard +"@borewit/text-codec@npm:^0.2.1": + version: 0.2.1 + resolution: "@borewit/text-codec@npm:0.2.1" + checksum: 10c0/aabd9c86497197aacc9ddb413857f112a98a9fd4be9ed56a24971a47bbec7c0d5d449efcad830f9895009c1a5914e5c448f972a0c968e97c4ebf99297dea7a6b + languageName: node + linkType: hard + "@bundled-es-modules/cookie@npm:^2.0.0": version: 2.0.0 resolution: "@bundled-es-modules/cookie@npm:2.0.0" @@ -1808,6 +1815,21 @@ __metadata: languageName: node linkType: hard +"@inquirer/external-editor@npm:^1.0.0": + version: 1.0.3 + resolution: "@inquirer/external-editor@npm:1.0.3" + dependencies: + chardet: "npm:^2.1.1" + iconv-lite: "npm:^0.7.0" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/82951cb7f3762dd78cca2ea291396841e3f4adfe26004b5badfed1cec4b6a04bb567dff94d0e41b35c61bdd7957317c64c22f58074d14b238d44e44d9e420019 + languageName: node + linkType: hard + "@inquirer/external-editor@npm:^1.0.2": version: 1.0.2 resolution: "@inquirer/external-editor@npm:1.0.2" @@ -1978,6 +2000,22 @@ __metadata: languageName: node linkType: hard +"@isaacs/balanced-match@npm:^4.0.1": + version: 4.0.1 + resolution: "@isaacs/balanced-match@npm:4.0.1" + checksum: 10c0/7da011805b259ec5c955f01cee903da72ad97c5e6f01ca96197267d3f33103d5b2f8a1af192140f3aa64526c593c8d098ae366c2b11f7f17645d12387c2fd420 + languageName: node + linkType: hard + +"@isaacs/brace-expansion@npm:^5.0.1": + version: 5.0.1 + resolution: "@isaacs/brace-expansion@npm:5.0.1" + dependencies: + "@isaacs/balanced-match": "npm:^4.0.1" + checksum: 10c0/e5d67c7bbf1f17b88132a35bc638af306d48acbb72810d48fa6e6edd8ab375854773108e8bf70f021f7ef6a8273455a6d1f0c3b5aa2aff06ce7894049ab77fb8 + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -2177,6 +2215,13 @@ __metadata: languageName: node linkType: hard +"@lukeed/csprng@npm:^1.0.0": + version: 1.1.0 + resolution: "@lukeed/csprng@npm:1.1.0" + checksum: 10c0/5d6dcf478af732972083ab2889c294b57f1028fa13c2c240d7a4aaa079c2c75df7ef0dcbdda5419147fc6704b4adf96b2de92f1a9a72ac21c6350c4014fffe6c + languageName: node + linkType: hard + "@mdn/browser-compat-data@npm:^5.3.13, @mdn/browser-compat-data@npm:^5.6.19": version: 5.7.6 resolution: "@mdn/browser-compat-data@npm:5.7.6" @@ -2371,6 +2416,68 @@ __metadata: languageName: node linkType: hard +"@nestjs/axios@npm:4.0.1": + version: 4.0.1 + resolution: "@nestjs/axios@npm:4.0.1" + peerDependencies: + "@nestjs/common": ^10.0.0 || ^11.0.0 + axios: ^1.3.1 + rxjs: ^7.0.0 + checksum: 10c0/6290f6ceaab2ea1000ee705dfd81da8cd776eb9fb388287a74a4faecf0afd8a8cdef7f6cc3afe2a9b10e47067df027280ca1ddb75b7885ff365e2faed37b10c7 + languageName: node + linkType: hard + +"@nestjs/common@npm:11.1.12": + version: 11.1.12 + resolution: "@nestjs/common@npm:11.1.12" + dependencies: + file-type: "npm:21.3.0" + iterare: "npm:1.2.1" + load-esm: "npm:1.0.3" + tslib: "npm:2.8.1" + uid: "npm:2.0.2" + peerDependencies: + class-transformer: ">=0.4.1" + class-validator: ">=0.13.2" + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + checksum: 10c0/2ab28286ba7a3cdef74c11f9578f859e24d96828de5c271374cba2abc92c64b923bdcb46aa119d617bcac0557159c85ddf70aa30d773363ef4f57456c7cef100 + languageName: node + linkType: hard + +"@nestjs/core@npm:11.1.12": + version: 11.1.12 + resolution: "@nestjs/core@npm:11.1.12" + dependencies: + "@nuxt/opencollective": "npm:0.4.1" + fast-safe-stringify: "npm:2.1.1" + iterare: "npm:1.2.1" + path-to-regexp: "npm:8.3.0" + tslib: "npm:2.8.1" + uid: "npm:2.0.2" + peerDependencies: + "@nestjs/common": ^11.0.0 + "@nestjs/microservices": ^11.0.0 + "@nestjs/platform-express": ^11.0.0 + "@nestjs/websockets": ^11.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + "@nestjs/microservices": + optional: true + "@nestjs/platform-express": + optional: true + "@nestjs/websockets": + optional: true + checksum: 10c0/a07dd42cfecf88324fa9c029cec118ab54791edd72e00f0a2ccc48b364dc84e348514cabb26d3a18f52640a817a367c4a82b563871d2d2cd7898771cc7fe5ec9 + languageName: node + linkType: hard + "@next/env@npm:14.2.21": version: 14.2.21 resolution: "@next/env@npm:14.2.21" @@ -2385,6 +2492,13 @@ __metadata: languageName: node linkType: hard +"@next/env@npm:14.2.5": + version: 14.2.5 + resolution: "@next/env@npm:14.2.5" + checksum: 10c0/63d8b88ac450b3c37940a9e2119a63a1074aca89908574ade6157a8aa295275dcb3ac5f69e00883fc55d0f12963b73b74e87ba32a5768a489f9609c6be57b699 + languageName: node + linkType: hard + "@next/eslint-plugin-next@npm:14.2.5": version: 14.2.5 resolution: "@next/eslint-plugin-next@npm:14.2.5" @@ -2408,6 +2522,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-darwin-arm64@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-darwin-arm64@npm:14.2.5" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@next/swc-darwin-x64@npm:14.2.21": version: 14.2.21 resolution: "@next/swc-darwin-x64@npm:14.2.21" @@ -2422,6 +2543,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-darwin-x64@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-darwin-x64@npm:14.2.5" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@next/swc-linux-arm64-gnu@npm:14.2.21": version: 14.2.21 resolution: "@next/swc-linux-arm64-gnu@npm:14.2.21" @@ -2436,6 +2564,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-arm64-gnu@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-linux-arm64-gnu@npm:14.2.5" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@next/swc-linux-arm64-musl@npm:14.2.21": version: 14.2.21 resolution: "@next/swc-linux-arm64-musl@npm:14.2.21" @@ -2450,6 +2585,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-arm64-musl@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-linux-arm64-musl@npm:14.2.5" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "@next/swc-linux-x64-gnu@npm:14.2.21": version: 14.2.21 resolution: "@next/swc-linux-x64-gnu@npm:14.2.21" @@ -2464,6 +2606,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-x64-gnu@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-linux-x64-gnu@npm:14.2.5" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@next/swc-linux-x64-musl@npm:14.2.21": version: 14.2.21 resolution: "@next/swc-linux-x64-musl@npm:14.2.21" @@ -2478,6 +2627,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-x64-musl@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-linux-x64-musl@npm:14.2.5" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "@next/swc-win32-arm64-msvc@npm:14.2.21": version: 14.2.21 resolution: "@next/swc-win32-arm64-msvc@npm:14.2.21" @@ -2492,6 +2648,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-arm64-msvc@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-win32-arm64-msvc@npm:14.2.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@next/swc-win32-ia32-msvc@npm:14.2.21": version: 14.2.21 resolution: "@next/swc-win32-ia32-msvc@npm:14.2.21" @@ -2506,6 +2669,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-ia32-msvc@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-win32-ia32-msvc@npm:14.2.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@next/swc-win32-x64-msvc@npm:14.2.21": version: 14.2.21 resolution: "@next/swc-win32-x64-msvc@npm:14.2.21" @@ -2520,6 +2690,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-x64-msvc@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-win32-x64-msvc@npm:14.2.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@nicolo-ribaudo/eslint-scope-5-internals@npm:5.1.1-v1": version: 5.1.1-v1 resolution: "@nicolo-ribaudo/eslint-scope-5-internals@npm:5.1.1-v1" @@ -2698,6 +2875,30 @@ __metadata: languageName: node linkType: hard +"@nuxt/opencollective@npm:0.4.1": + version: 0.4.1 + resolution: "@nuxt/opencollective@npm:0.4.1" + dependencies: + consola: "npm:^3.2.3" + bin: + opencollective: bin/opencollective.js + checksum: 10c0/ef2835d8635d2928152eff8b5a1ec42c145e2ab00cb02ff4bb61f0a6f5528afc9b169c06c32308c783779fe26855ebc67419743046caa80e582e814cff73187d + languageName: node + linkType: hard + +"@nuxtjs/opencollective@npm:0.3.2": + version: 0.3.2 + resolution: "@nuxtjs/opencollective@npm:0.3.2" + dependencies: + chalk: "npm:^4.1.0" + consola: "npm:^2.15.0" + node-fetch: "npm:^2.6.1" + bin: + opencollective: bin/opencollective.js + checksum: 10c0/540268687af3289ff107585484d42201b404cdbb98b3a512487c12a6b180a8f0e1df0d701df47d3d9e0d5c0f6eb3252d80535562aedca9edf52cf7fd17ae4601 + languageName: node + linkType: hard + "@nx/devkit@npm:19.0.4, @nx/devkit@npm:>=17.1.2 < 20": version: 19.0.4 resolution: "@nx/devkit@npm:19.0.4" @@ -2970,6 +3171,33 @@ __metadata: languageName: node linkType: hard +"@openapitools/openapi-generator-cli@npm:^2.13.5": + version: 2.28.0 + resolution: "@openapitools/openapi-generator-cli@npm:2.28.0" + dependencies: + "@nestjs/axios": "npm:4.0.1" + "@nestjs/common": "npm:11.1.12" + "@nestjs/core": "npm:11.1.12" + "@nuxtjs/opencollective": "npm:0.3.2" + axios: "npm:1.13.2" + chalk: "npm:4.1.2" + commander: "npm:8.3.0" + compare-versions: "npm:6.1.1" + concurrently: "npm:9.2.1" + console.table: "npm:0.10.0" + fs-extra: "npm:11.3.3" + glob: "npm:13.0.0" + inquirer: "npm:8.2.7" + proxy-agent: "npm:6.5.0" + reflect-metadata: "npm:0.2.2" + rxjs: "npm:7.8.2" + tslib: "npm:2.8.1" + bin: + openapi-generator-cli: main.js + checksum: 10c0/ff9db846a3ba4e0907a30a652ba34119541847408bfe60248850a6fe9c7fb604f0754bd776bc72c285807ccd7708880d927a2d5b3e53bf53734e156017eaa90e + languageName: node + linkType: hard + "@openfeature/core@npm:1.3.0": version: 1.3.0 resolution: "@openfeature/core@npm:1.3.0" @@ -3244,6 +3472,22 @@ __metadata: languageName: unknown linkType: soft +"@reflag/rest-api-sdk@workspace:*, @reflag/rest-api-sdk@workspace:packages/rest-api-sdk": + version: 0.0.0-use.local + resolution: "@reflag/rest-api-sdk@workspace:packages/rest-api-sdk" + dependencies: + "@openapitools/openapi-generator-cli": "npm:^2.13.5" + "@reflag/eslint-config": "npm:~0.0.2" + "@reflag/tsconfig": "npm:~0.0.2" + "@types/node": "npm:^22.12.0" + "@vitest/coverage-v8": "npm:~1.6.0" + eslint: "npm:^9.21.0" + prettier: "npm:^3.5.2" + typescript: "npm:^5.7.3" + vitest: "npm:~1.6.0" + languageName: unknown + linkType: soft + "@reflag/tsconfig@npm:0.0.2, @reflag/tsconfig@npm:^0.0.2, @reflag/tsconfig@npm:~0.0.2, @reflag/tsconfig@workspace:packages/tsconfig": version: 0.0.0-use.local resolution: "@reflag/tsconfig@workspace:packages/tsconfig" @@ -4053,6 +4297,23 @@ __metadata: languageName: node linkType: hard +"@tokenizer/inflate@npm:^0.4.1": + version: 0.4.1 + resolution: "@tokenizer/inflate@npm:0.4.1" + dependencies: + debug: "npm:^4.4.3" + token-types: "npm:^6.1.1" + checksum: 10c0/9817516efe21d1ce3bdfb80a1f94efc8981064ce3873448ba79f4d81d96c0694c484c289bd042d346ae5536cf77f5aa9a367d39c3df700eb610761b7c306b4de + languageName: node + linkType: hard + +"@tokenizer/token@npm:^0.3.0": + version: 0.3.0 + resolution: "@tokenizer/token@npm:0.3.0" + checksum: 10c0/7ab9a822d4b5ff3f5bca7f7d14d46bdd8432528e028db4a52be7fbf90c7f495cc1af1324691dda2813c6af8dc4b8eb29de3107d4508165f9aa5b53e7d501f155 + languageName: node + linkType: hard + "@tootallnate/once@npm:2": version: 2.0.0 resolution: "@tootallnate/once@npm:2.0.0" @@ -4060,6 +4321,13 @@ __metadata: languageName: node linkType: hard +"@tootallnate/quickjs-emscripten@npm:^0.23.0": + version: 0.23.0 + resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" + checksum: 10c0/2a939b781826fb5fd3edd0f2ec3b321d259d760464cf20611c9877205aaca3ccc0b7304dea68416baa0d568e82cd86b17d29548d1e5139fa3155a4a86a2b4b49 + languageName: node + linkType: hard + "@tsconfig/node10@npm:^1.0.7": version: 1.0.8 resolution: "@tsconfig/node10@npm:1.0.8" @@ -5642,6 +5910,13 @@ __metadata: languageName: node linkType: hard +"agent-base@npm:^7.1.2": + version: 7.1.4 + resolution: "agent-base@npm:7.1.4" + checksum: 10c0/c2c9ab7599692d594b6a161559ada307b7a624fa4c7b03e3afdb5a5e31cd0e53269115b620fcab024c5ac6a6f37fa5eb2e004f076ad30f5f7e6b8b671f7b35fe + languageName: node + linkType: hard + "agentkeepalive@npm:^4.2.1": version: 4.5.0 resolution: "agentkeepalive@npm:4.5.0" @@ -6179,6 +6454,15 @@ __metadata: languageName: node linkType: hard +"ast-types@npm:^0.13.4": + version: 0.13.4 + resolution: "ast-types@npm:0.13.4" + dependencies: + tslib: "npm:^2.0.1" + checksum: 10c0/3a1a409764faa1471601a0ad01b3aa699292991aa9c8a30c7717002cabdf5d98008e7b53ae61f6e058f757fc6ba965e147967a93c13e62692c907d79cfb245f8 + languageName: node + linkType: hard + "async@npm:^2.6.4": version: 2.6.4 resolution: "async@npm:2.6.4" @@ -6243,6 +6527,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:1.13.2": + version: 1.13.2 + resolution: "axios@npm:1.13.2" + dependencies: + follow-redirects: "npm:^1.15.6" + form-data: "npm:^4.0.4" + proxy-from-env: "npm:^1.1.0" + checksum: 10c0/e8a42e37e5568ae9c7a28c348db0e8cf3e43d06fcbef73f0048669edfe4f71219664da7b6cc991b0c0f01c28a48f037c515263cb79be1f1ae8ff034cd813867b + languageName: node + linkType: hard + "axios@npm:^1.6.0": version: 1.8.4 resolution: "axios@npm:1.8.4" @@ -6286,6 +6581,13 @@ __metadata: languageName: node linkType: hard +"basic-ftp@npm:^5.0.2": + version: 5.1.0 + resolution: "basic-ftp@npm:5.1.0" + checksum: 10c0/397d5ed490f4d3b8b2dcd7afdf8e9fcf714a7d1c394a6c66b31704baf49c9fc250f1742f6189136a76b983675edf3d986b7ed93d253dc6477e65a567cf1e04c0 + languageName: node + linkType: hard + "before-after-hook@npm:^2.2.0": version: 2.2.3 resolution: "before-after-hook@npm:2.2.3" @@ -6733,6 +7035,16 @@ __metadata: languageName: node linkType: hard +"chalk@npm:4.1.2, chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: "npm:^4.1.0" + supports-color: "npm:^7.1.0" + checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880 + languageName: node + linkType: hard + "chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -6744,16 +7056,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": - version: 4.1.2 - resolution: "chalk@npm:4.1.2" - dependencies: - ansi-styles: "npm:^4.1.0" - supports-color: "npm:^7.1.0" - checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880 - languageName: node - linkType: hard - "chalk@npm:^5.3.0": version: 5.3.0 resolution: "chalk@npm:5.3.0" @@ -6782,6 +7084,13 @@ __metadata: languageName: node linkType: hard +"chardet@npm:^2.1.1": + version: 2.1.1 + resolution: "chardet@npm:2.1.1" + checksum: 10c0/d8391dd412338442b3de0d3a488aa9327f8bcf74b62b8723d6bd0b85c4084d50b731320e0a7c710edb1d44de75969995d2784b80e4c13b004a6c7a0db4c6e793 + languageName: node + linkType: hard + "check-error@npm:^1.0.3": version: 1.0.3 resolution: "check-error@npm:1.0.3" @@ -7012,6 +7321,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:8.3.0": + version: 8.3.0 + resolution: "commander@npm:8.3.0" + checksum: 10c0/8b043bb8322ea1c39664a1598a95e0495bfe4ca2fad0d84a92d7d1d8d213e2a155b441d2470c8e08de7c4a28cf2bc6e169211c49e1b21d9f7edc6ae4d9356060 + languageName: node + linkType: hard + "commander@npm:^10.0.0, commander@npm:^10.0.1": version: 10.0.1 resolution: "commander@npm:10.0.1" @@ -7063,7 +7379,7 @@ __metadata: languageName: node linkType: hard -"compare-versions@npm:^6.1.1": +"compare-versions@npm:6.1.1, compare-versions@npm:^6.1.1": version: 6.1.1 resolution: "compare-versions@npm:6.1.1" checksum: 10c0/415205c7627f9e4f358f571266422980c9fe2d99086be0c9a48008ef7c771f32b0fbe8e97a441ffedc3910872f917a0675fe0fe3c3b6d331cda6d8690be06338 @@ -7096,6 +7412,23 @@ __metadata: languageName: node linkType: hard +"concurrently@npm:9.2.1": + version: 9.2.1 + resolution: "concurrently@npm:9.2.1" + dependencies: + chalk: "npm:4.1.2" + rxjs: "npm:7.8.2" + shell-quote: "npm:1.8.3" + supports-color: "npm:8.1.1" + tree-kill: "npm:1.2.2" + yargs: "npm:17.7.2" + bin: + conc: dist/bin/concurrently.js + concurrently: dist/bin/concurrently.js + checksum: 10c0/da37f239f82eb7ac24f5ddb56259861e5f1d6da2ade7602b6ea7ad3101b13b5ccec02a77b7001402d1028ff2fdc38eed55644b32853ad5abf30e057002a963aa + languageName: node + linkType: hard + "confbox@npm:^0.1.7": version: 0.1.7 resolution: "confbox@npm:0.1.7" @@ -7127,6 +7460,20 @@ __metadata: languageName: node linkType: hard +"consola@npm:^2.15.0": + version: 2.15.3 + resolution: "consola@npm:2.15.3" + checksum: 10c0/34a337e6b4a1349ee4d7b4c568484344418da8fdb829d7d71bfefcd724f608f273987633b6eef465e8de510929907a092e13cb7a28a5d3acb3be446fcc79fd5e + languageName: node + linkType: hard + +"consola@npm:^3.2.3": + version: 3.4.2 + resolution: "consola@npm:3.4.2" + checksum: 10c0/7cebe57ecf646ba74b300bcce23bff43034ed6fbec9f7e39c27cee1dc00df8a21cd336b466ad32e304ea70fba04ec9e890c200270de9a526ce021ba8a7e4c11a + languageName: node + linkType: hard + "console-control-strings@npm:^1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" @@ -7134,6 +7481,15 @@ __metadata: languageName: node linkType: hard +"console.table@npm:0.10.0": + version: 0.10.0 + resolution: "console.table@npm:0.10.0" + dependencies: + easy-table: "npm:1.1.0" + checksum: 10c0/b1893a06b422c7e82dca03dec000beabebc26415df558a05e1b9778407a76e4caa1db286df40f72e3780ac5c5b5ef5f4b8a3bef2d22020abb86f6408dc357875 + languageName: node + linkType: hard + "content-disposition@npm:0.5.4": version: 0.5.4 resolution: "content-disposition@npm:0.5.4" @@ -7394,6 +7750,17 @@ __metadata: languageName: node linkType: hard +"customer-admin-panel@workspace:packages/rest-api-sdk/examples/customer-admin-panel": + version: 0.0.0-use.local + resolution: "customer-admin-panel@workspace:packages/rest-api-sdk/examples/customer-admin-panel" + dependencies: + "@reflag/rest-api-sdk": "workspace:*" + next: "npm:14.2.5" + react: "npm:18.3.1" + react-dom: "npm:18.3.1" + languageName: unknown + linkType: soft + "damerau-levenshtein@npm:^1.0.8": version: 1.0.8 resolution: "damerau-levenshtein@npm:1.0.8" @@ -7408,6 +7775,13 @@ __metadata: languageName: node linkType: hard +"data-uri-to-buffer@npm:^6.0.2": + version: 6.0.2 + resolution: "data-uri-to-buffer@npm:6.0.2" + checksum: 10c0/f76922bf895b3d7d443059ff278c9cc5efc89d70b8b80cd9de0aa79b3adc6d7a17948eefb8692e30398c43635f70ece1673d6085cc9eba2878dbc6c6da5292ac + languageName: node + linkType: hard + "data-urls@npm:^5.0.0": version: 5.0.0 resolution: "data-urls@npm:5.0.0" @@ -7564,6 +7938,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.4.3": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/d79136ec6c83ecbefd0f6a5593da6a9c91ec4d7ddc4b54c883d6e71ec9accb5f67a1a5e96d00a328196b5b5c86d365e98d8a3a70856aaf16b4e7b1985e67f5a6 + languageName: node + linkType: hard + "decamelize-keys@npm:^1.1.0": version: 1.1.1 resolution: "decamelize-keys@npm:1.1.1" @@ -7727,6 +8113,17 @@ __metadata: languageName: node linkType: hard +"degenerator@npm:^5.0.0": + version: 5.0.1 + resolution: "degenerator@npm:5.0.1" + dependencies: + ast-types: "npm:^0.13.4" + escodegen: "npm:^2.1.0" + esprima: "npm:^4.0.1" + checksum: 10c0/e48d8a651edeb512a648711a09afec269aac6de97d442a4bb9cf121a66877e0eec11b9727100a10252335c0666ae1c84a8bc1e3a3f47788742c975064d2c7b1c + languageName: node + linkType: hard + "delayed-stream@npm:~1.0.0": version: 1.0.0 resolution: "delayed-stream@npm:1.0.0" @@ -7886,6 +8283,18 @@ __metadata: languageName: node linkType: hard +"easy-table@npm:1.1.0": + version: 1.1.0 + resolution: "easy-table@npm:1.1.0" + dependencies: + wcwidth: "npm:>=1.0.1" + dependenciesMeta: + wcwidth: + optional: true + checksum: 10c0/0b7b03723e450c8286bd375bbe7d23247456dbb8f79df055adcfd745bfb91f7604c4e78204ff75d65d5229bec8867cbefca51c57938004f487ff800b587540bb + languageName: node + linkType: hard + "editorconfig@npm:^1.0.4": version: 1.0.4 resolution: "editorconfig@npm:1.0.4" @@ -8623,6 +9032,24 @@ __metadata: languageName: node linkType: hard +"escodegen@npm:^2.1.0": + version: 2.1.0 + resolution: "escodegen@npm:2.1.0" + dependencies: + esprima: "npm:^4.0.1" + estraverse: "npm:^5.2.0" + esutils: "npm:^2.0.2" + source-map: "npm:~0.6.1" + dependenciesMeta: + source-map: + optional: true + bin: + escodegen: bin/escodegen.js + esgenerate: bin/esgenerate.js + checksum: 10c0/e1450a1f75f67d35c061bf0d60888b15f62ab63aef9df1901cffc81cffbbb9e8b3de237c5502cf8613a017c1df3a3003881307c78835a1ab54d8c8d2206e01d3 + languageName: node + linkType: hard + "eslint-config-next@npm:14.2.5": version: 14.2.5 resolution: "eslint-config-next@npm:14.2.5" @@ -9439,6 +9866,13 @@ __metadata: languageName: node linkType: hard +"fast-safe-stringify@npm:2.1.1": + version: 2.1.1 + resolution: "fast-safe-stringify@npm:2.1.1" + checksum: 10c0/d90ec1c963394919828872f21edaa3ad6f1dddd288d2bd4e977027afff09f5db40f94e39536d4646f7e01761d704d72d51dce5af1b93717f3489ef808f5f4e4d + languageName: node + linkType: hard + "fast-uri@npm:^3.0.1": version: 3.0.1 resolution: "fast-uri@npm:3.0.1" @@ -9513,6 +9947,18 @@ __metadata: languageName: node linkType: hard +"file-type@npm:21.3.0": + version: 21.3.0 + resolution: "file-type@npm:21.3.0" + dependencies: + "@tokenizer/inflate": "npm:^0.4.1" + strtok3: "npm:^10.3.4" + token-types: "npm:^6.1.1" + uint8array-extras: "npm:^1.4.0" + checksum: 10c0/1b1fa909e6063044a6da1d2ea348ee4d747ed9286382d3f0d4d6532c11fb2ea9f2e7e67b2bc7d745d1bc937e05dee1aa8cb912c64250933bcb393a3744f4e284 + languageName: node + linkType: hard + "filelist@npm:^1.0.4": version: 1.0.4 resolution: "filelist@npm:1.0.4" @@ -9688,6 +10134,19 @@ __metadata: languageName: node linkType: hard +"form-data@npm:^4.0.4": + version: 4.0.5 + resolution: "form-data@npm:4.0.5" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.2" + mime-types: "npm:^2.1.12" + checksum: 10c0/dd6b767ee0bbd6d84039db12a0fa5a2028160ffbfaba1800695713b46ae974a5f6e08b3356c3195137f8530dcd9dfcb5d5ae1eeff53d0db1e5aad863b619ce3b + languageName: node + linkType: hard + "forwarded@npm:0.2.0": version: 0.2.0 resolution: "forwarded@npm:0.2.0" @@ -9716,6 +10175,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:11.3.3": + version: 11.3.3 + resolution: "fs-extra@npm:11.3.3" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10c0/984924ff4104e3e9f351b658a864bf3b354b2c90429f57aec0acd12d92c4e6b762cbacacdffb4e745b280adce882e1f980c485d9f02c453f769ab4e7fc646ce3 + languageName: node + linkType: hard + "fs-extra@npm:^11.1.0, fs-extra@npm:^11.1.1": version: 11.2.0 resolution: "fs-extra@npm:11.2.0" @@ -10060,6 +10530,17 @@ __metadata: languageName: node linkType: hard +"get-uri@npm:^6.0.1": + version: 6.0.5 + resolution: "get-uri@npm:6.0.5" + dependencies: + basic-ftp: "npm:^5.0.2" + data-uri-to-buffer: "npm:^6.0.2" + debug: "npm:^4.3.4" + checksum: 10c0/c7ff5d5d55de53d23ecce7c5108cc3ed0db1174db43c9aa15506d640283d36ee0956fd8ba1fc50b06a718466cc85794ae9d8860193f91318afe846e3e7010f3a + languageName: node + linkType: hard + "git-raw-commits@npm:^3.0.0": version: 3.0.0 resolution: "git-raw-commits@npm:3.0.0" @@ -10163,6 +10644,17 @@ __metadata: languageName: node linkType: hard +"glob@npm:13.0.0": + version: 13.0.0 + resolution: "glob@npm:13.0.0" + dependencies: + minimatch: "npm:^10.1.1" + minipass: "npm:^7.1.2" + path-scurry: "npm:^2.0.0" + checksum: 10c0/8e2f5821f3f7c312dd102e23a15b80c79e0837a9872784293ba2e15ec73b3f3749a49a42a31bfcb4e52c84820a474e92331c2eebf18819d20308f5c33876630a + languageName: node + linkType: hard + "glob@npm:^10.2.2, glob@npm:^10.3.10": version: 10.3.14 resolution: "glob@npm:10.3.14" @@ -10664,7 +11156,7 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^7.0.2": +"http-proxy-agent@npm:^7.0.1, http-proxy-agent@npm:^7.0.2": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" dependencies: @@ -10728,6 +11220,16 @@ __metadata: languageName: node linkType: hard +"https-proxy-agent@npm:^7.0.6": + version: 7.0.6 + resolution: "https-proxy-agent@npm:7.0.6" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:4" + checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac + languageName: node + linkType: hard + "human-signals@npm:^2.1.0": version: 2.1.0 resolution: "human-signals@npm:2.1.0" @@ -10778,7 +11280,7 @@ __metadata: languageName: node linkType: hard -"ieee754@npm:^1.1.13": +"ieee754@npm:^1.1.13, ieee754@npm:^1.2.1": version: 1.2.1 resolution: "ieee754@npm:1.2.1" checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb @@ -10899,6 +11401,29 @@ __metadata: languageName: node linkType: hard +"inquirer@npm:8.2.7": + version: 8.2.7 + resolution: "inquirer@npm:8.2.7" + dependencies: + "@inquirer/external-editor": "npm:^1.0.0" + ansi-escapes: "npm:^4.2.1" + chalk: "npm:^4.1.1" + cli-cursor: "npm:^3.1.0" + cli-width: "npm:^3.0.0" + figures: "npm:^3.0.0" + lodash: "npm:^4.17.21" + mute-stream: "npm:0.0.8" + ora: "npm:^5.4.1" + run-async: "npm:^2.4.0" + rxjs: "npm:^7.5.5" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + through: "npm:^2.3.6" + wrap-ansi: "npm:^6.0.1" + checksum: 10c0/75aa594231769d292102615da3199320359bfb566e96dae0f89a5773a18e21c676709d9f5a9fb1372f7d2cf25c551a4efe53691ff436d941f95336931777c15d + languageName: node + linkType: hard + "inquirer@npm:^8.2.4": version: 8.2.6 resolution: "inquirer@npm:8.2.6" @@ -10969,6 +11494,13 @@ __metadata: languageName: node linkType: hard +"ip-address@npm:^10.0.1": + version: 10.1.0 + resolution: "ip-address@npm:10.1.0" + checksum: 10c0/0103516cfa93f6433b3bd7333fa876eb21263912329bfa47010af5e16934eeeff86f3d2ae700a3744a137839ddfad62b900c7a445607884a49b5d1e32a3d7566 + languageName: node + linkType: hard + "ip-address@npm:^9.0.5": version: 9.0.5 resolution: "ip-address@npm:9.0.5" @@ -11719,6 +12251,13 @@ __metadata: languageName: node linkType: hard +"iterare@npm:1.2.1": + version: 1.2.1 + resolution: "iterare@npm:1.2.1" + checksum: 10c0/02667d486e3e83ead028ba8484d927498c2ceab7e8c6a69dd881fd02abc4114f00b13abb36b592252fbb578b6e6f99ca1dfc2835408b9158c9a112a9964f453f + languageName: node + linkType: hard + "iterator.prototype@npm:^1.1.2": version: 1.1.2 resolution: "iterator.prototype@npm:1.1.2" @@ -12282,6 +12821,13 @@ __metadata: languageName: node linkType: hard +"load-esm@npm:1.0.3": + version: 1.0.3 + resolution: "load-esm@npm:1.0.3" + checksum: 10c0/285a3666a29c11f7b466bb70ee3582af32893d03ed91b68be939c656a15afd27f3683f5f8d56b52834ce2911ecf1c84e515e6048248fb5268a89b724a8ddbf65 + languageName: node + linkType: hard + "load-json-file@npm:6.2.0": version: 6.2.0 resolution: "load-json-file@npm:6.2.0" @@ -12476,6 +13022,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^11.0.0": + version: 11.2.5 + resolution: "lru-cache@npm:11.2.5" + checksum: 10c0/cc98958d25dddf1c8a8cbdc49588bd3b24450e8dfa78f32168fd188a20d4a0331c7406d0f3250c86a46619ee288056fd7a1195e8df56dc8a9592397f4fbd8e1d + languageName: node + linkType: hard + "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -12494,7 +13047,7 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^7.5.1, lru-cache@npm:^7.7.1": +"lru-cache@npm:^7.14.1, lru-cache@npm:^7.5.1, lru-cache@npm:^7.7.1": version: 7.18.3 resolution: "lru-cache@npm:7.18.3" checksum: 10c0/b3a452b491433db885beed95041eb104c157ef7794b9c9b4d647be503be91769d11206bb573849a16b4cc0d03cbd15ffd22df7960997788b74c1d399ac7a4fed @@ -12837,6 +13390,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.1.1": + version: 10.1.2 + resolution: "minimatch@npm:10.1.2" + dependencies: + "@isaacs/brace-expansion": "npm:^5.0.1" + checksum: 10c0/0cccef3622201703de6ecf9d772c0be1d5513dcc038ed9feb866c20cf798243e678ac35605dac3f1a054650c28037486713fe9e9a34b184b9097959114daf086 + languageName: node + linkType: hard + "minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -13260,6 +13822,13 @@ __metadata: languageName: node linkType: hard +"netmask@npm:^2.0.2": + version: 2.0.2 + resolution: "netmask@npm:2.0.2" + checksum: 10c0/cafd28388e698e1138ace947929f842944d0f1c0b87d3fa2601a61b38dc89397d33c0ce2c8e7b99e968584b91d15f6810b91bef3f3826adf71b1833b61d4bf4f + languageName: node + linkType: hard + "next@npm:14.2.21": version: 14.2.21 resolution: "next@npm:14.2.21" @@ -13376,6 +13945,64 @@ __metadata: languageName: node linkType: hard +"next@npm:14.2.5": + version: 14.2.5 + resolution: "next@npm:14.2.5" + dependencies: + "@next/env": "npm:14.2.5" + "@next/swc-darwin-arm64": "npm:14.2.5" + "@next/swc-darwin-x64": "npm:14.2.5" + "@next/swc-linux-arm64-gnu": "npm:14.2.5" + "@next/swc-linux-arm64-musl": "npm:14.2.5" + "@next/swc-linux-x64-gnu": "npm:14.2.5" + "@next/swc-linux-x64-musl": "npm:14.2.5" + "@next/swc-win32-arm64-msvc": "npm:14.2.5" + "@next/swc-win32-ia32-msvc": "npm:14.2.5" + "@next/swc-win32-x64-msvc": "npm:14.2.5" + "@swc/helpers": "npm:0.5.5" + busboy: "npm:1.6.0" + caniuse-lite: "npm:^1.0.30001579" + graceful-fs: "npm:^4.2.11" + postcss: "npm:8.4.31" + styled-jsx: "npm:5.1.1" + peerDependencies: + "@opentelemetry/api": ^1.1.0 + "@playwright/test": ^1.41.2 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-ia32-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + "@playwright/test": + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: 10c0/8df7d8ccc1a5bab03fa50dd6656c8a6f3750e81ef0b087dc329fea9346847c3094a933a890a8e87151dc32f0bc55020b8f6386d4565856d83bcc10895d29ec08 + languageName: node + linkType: hard + "nextjs-bootstrap-demo@workspace:packages/react-sdk/dev/nextjs-bootstrap-demo": version: 0.0.0-use.local resolution: "nextjs-bootstrap-demo@workspace:packages/react-sdk/dev/nextjs-bootstrap-demo" @@ -13462,7 +14089,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.7": +"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" dependencies: @@ -14388,6 +15015,32 @@ __metadata: languageName: node linkType: hard +"pac-proxy-agent@npm:^7.1.0": + version: 7.2.0 + resolution: "pac-proxy-agent@npm:7.2.0" + dependencies: + "@tootallnate/quickjs-emscripten": "npm:^0.23.0" + agent-base: "npm:^7.1.2" + debug: "npm:^4.3.4" + get-uri: "npm:^6.0.1" + http-proxy-agent: "npm:^7.0.0" + https-proxy-agent: "npm:^7.0.6" + pac-resolver: "npm:^7.0.1" + socks-proxy-agent: "npm:^8.0.5" + checksum: 10c0/0265c17c9401c2ea735697931a6553a0c6d8b20c4d7d4e3b3a0506080ba69a8d5ad656e2a6be875411212e2b6ed7a4d9526dd3997e08581fdfb1cbcad454c296 + languageName: node + linkType: hard + +"pac-resolver@npm:^7.0.1": + version: 7.0.1 + resolution: "pac-resolver@npm:7.0.1" + dependencies: + degenerator: "npm:^5.0.0" + netmask: "npm:^2.0.2" + checksum: 10c0/5f3edd1dd10fded31e7d1f95776442c3ee51aa098c28b74ede4927d9677ebe7cebb2636750c24e945f5b84445e41ae39093d3a1014a994e5ceb9f0b1b88ebff5 + languageName: node + linkType: hard + "package-json-from-dist@npm:^1.0.0": version: 1.0.0 resolution: "package-json-from-dist@npm:1.0.0" @@ -14564,6 +15217,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^2.0.0": + version: 2.0.1 + resolution: "path-scurry@npm:2.0.1" + dependencies: + lru-cache: "npm:^11.0.0" + minipass: "npm:^7.1.2" + checksum: 10c0/2a16ed0e81fbc43513e245aa5763354e25e787dab0d539581a6c3f0f967461a159ed6236b2559de23aa5b88e7dc32b469b6c47568833dd142a4b24b4f5cd2620 + languageName: node + linkType: hard + "path-to-regexp@npm:0.1.12": version: 0.1.12 resolution: "path-to-regexp@npm:0.1.12" @@ -14571,6 +15234,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:8.3.0": + version: 8.3.0 + resolution: "path-to-regexp@npm:8.3.0" + checksum: 10c0/ee1544a73a3f294a97a4c663b0ce71bbf1621d732d80c9c9ed201b3e911a86cb628ebad691b9d40f40a3742fe22011e5a059d8eed2cf63ec2cb94f6fb4efe67c + languageName: node + linkType: hard + "path-to-regexp@npm:^6.2.0": version: 6.3.0 resolution: "path-to-regexp@npm:6.3.0" @@ -15426,6 +16096,22 @@ __metadata: languageName: node linkType: hard +"proxy-agent@npm:6.5.0": + version: 6.5.0 + resolution: "proxy-agent@npm:6.5.0" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:^4.3.4" + http-proxy-agent: "npm:^7.0.1" + https-proxy-agent: "npm:^7.0.6" + lru-cache: "npm:^7.14.1" + pac-proxy-agent: "npm:^7.1.0" + proxy-from-env: "npm:^1.1.0" + socks-proxy-agent: "npm:^8.0.5" + checksum: 10c0/7fd4e6f36bf17098a686d4aee3b8394abfc0b0537c2174ce96b0a4223198b9fafb16576c90108a3fcfc2af0168bd7747152bfa1f58e8fee91d3780e79aab7fd8 + languageName: node + linkType: hard + "proxy-from-env@npm:^1.1.0": version: 1.1.0 resolution: "proxy-from-env@npm:1.1.0" @@ -15542,7 +16228,7 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:*, react-dom@npm:^18": +"react-dom@npm:*, react-dom@npm:18.3.1, react-dom@npm:^18": version: 18.3.1 resolution: "react-dom@npm:18.3.1" dependencies: @@ -15575,7 +16261,7 @@ __metadata: languageName: node linkType: hard -"react@npm:*, react@npm:^18": +"react@npm:*, react@npm:18.3.1, react@npm:^18": version: 18.3.1 resolution: "react@npm:18.3.1" dependencies: @@ -15759,6 +16445,13 @@ __metadata: languageName: node linkType: hard +"reflect-metadata@npm:0.2.2": + version: 0.2.2 + resolution: "reflect-metadata@npm:0.2.2" + checksum: 10c0/1cd93a15ea291e420204955544637c264c216e7aac527470e393d54b4bb075f10a17e60d8168ec96600c7e0b9fcc0cb0bb6e91c3fbf5b0d8c9056f04e6ac1ec2 + languageName: node + linkType: hard + "reflect.getprototypeof@npm:^1.0.4": version: 1.0.6 resolution: "reflect.getprototypeof@npm:1.0.6" @@ -16330,6 +17023,15 @@ __metadata: languageName: node linkType: hard +"rxjs@npm:7.8.2": + version: 7.8.2 + resolution: "rxjs@npm:7.8.2" + dependencies: + tslib: "npm:^2.1.0" + checksum: 10c0/1fcd33d2066ada98ba8f21fcbbcaee9f0b271de1d38dc7f4e256bfbc6ffcdde68c8bfb69093de7eeb46f24b1fb820620bf0223706cff26b4ab99a7ff7b2e2c45 + languageName: node + linkType: hard + "rxjs@npm:^7.5.5": version: 7.8.1 resolution: "rxjs@npm:7.8.1" @@ -16684,6 +17386,13 @@ __metadata: languageName: node linkType: hard +"shell-quote@npm:1.8.3": + version: 1.8.3 + resolution: "shell-quote@npm:1.8.3" + checksum: 10c0/bee87c34e1e986cfb4c30846b8e6327d18874f10b535699866f368ade11ea4ee45433d97bf5eada22c4320c27df79c3a6a7eb1bf3ecfc47f2c997d9e5e2672fd + languageName: node + linkType: hard + "shelljs@npm:^0.8.5": version: 0.8.5 resolution: "shelljs@npm:0.8.5" @@ -16875,6 +17584,17 @@ __metadata: languageName: node linkType: hard +"socks-proxy-agent@npm:^8.0.5": + version: 8.0.5 + resolution: "socks-proxy-agent@npm:8.0.5" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:^4.3.4" + socks: "npm:^2.8.3" + checksum: 10c0/5d2c6cecba6821389aabf18728325730504bf9bb1d9e342e7987a5d13badd7a98838cc9a55b8ed3cb866ad37cc23e1086f09c4d72d93105ce9dfe76330e9d2a6 + languageName: node + linkType: hard + "socks@npm:^2.6.2, socks@npm:^2.7.1": version: 2.8.3 resolution: "socks@npm:2.8.3" @@ -16885,6 +17605,16 @@ __metadata: languageName: node linkType: hard +"socks@npm:^2.8.3": + version: 2.8.7 + resolution: "socks@npm:2.8.7" + dependencies: + ip-address: "npm:^10.0.1" + smart-buffer: "npm:^4.2.0" + checksum: 10c0/2805a43a1c4bcf9ebf6e018268d87b32b32b06fbbc1f9282573583acc155860dc361500f89c73bfbb157caa1b4ac78059eac0ef15d1811eb0ca75e0bdadbc9d2 + languageName: node + linkType: hard + "sort-keys@npm:^2.0.0": version: 2.0.0 resolution: "sort-keys@npm:2.0.0" @@ -17378,6 +18108,15 @@ __metadata: languageName: node linkType: hard +"strtok3@npm:^10.3.4": + version: 10.3.4 + resolution: "strtok3@npm:10.3.4" + dependencies: + "@tokenizer/token": "npm:^0.3.0" + checksum: 10c0/277ab69e417f4545e364ffaf9d560c991f531045dbace32d77b5c822cccd76a608b782785a2c60595274288d4d32dced184a5c21dc20348791da697127dc69a8 + languageName: node + linkType: hard + "styled-jsx@npm:5.1.1": version: 5.1.1 resolution: "styled-jsx@npm:5.1.1" @@ -17412,6 +18151,15 @@ __metadata: languageName: node linkType: hard +"supports-color@npm:8.1.1, supports-color@npm:^8.0.0, supports-color@npm:~8.1.1": + version: 8.1.1 + resolution: "supports-color@npm:8.1.1" + dependencies: + has-flag: "npm:^4.0.0" + checksum: 10c0/ea1d3c275dd604c974670f63943ed9bd83623edc102430c05adb8efc56ba492746b6e95386e7831b872ec3807fd89dd8eb43f735195f37b5ec343e4234cc7e89 + languageName: node + linkType: hard + "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -17430,15 +18178,6 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^8.0.0, supports-color@npm:~8.1.1": - version: 8.1.1 - resolution: "supports-color@npm:8.1.1" - dependencies: - has-flag: "npm:^4.0.0" - checksum: 10c0/ea1d3c275dd604c974670f63943ed9bd83623edc102430c05adb8efc56ba492746b6e95386e7831b872ec3807fd89dd8eb43f735195f37b5ec343e4234cc7e89 - languageName: node - linkType: hard - "supports-preserve-symlinks-flag@npm:^1.0.0": version: 1.0.0 resolution: "supports-preserve-symlinks-flag@npm:1.0.0" @@ -17756,6 +18495,17 @@ __metadata: languageName: node linkType: hard +"token-types@npm:^6.1.1": + version: 6.1.2 + resolution: "token-types@npm:6.1.2" + dependencies: + "@borewit/text-codec": "npm:^0.2.1" + "@tokenizer/token": "npm:^0.3.0" + ieee754: "npm:^1.2.1" + checksum: 10c0/8786e28e3cb65b9e890bc3c38def98e6dfe4565538237f8c0e47dbe549ed8f5f00de8dc464717868308abb4729f1958f78f69e1c4c3deebbb685729113a6fee8 + languageName: node + linkType: hard + "tough-cookie@npm:^4.1.4": version: 4.1.4 resolution: "tough-cookie@npm:4.1.4" @@ -17784,6 +18534,15 @@ __metadata: languageName: node linkType: hard +"tree-kill@npm:1.2.2": + version: 1.2.2 + resolution: "tree-kill@npm:1.2.2" + bin: + tree-kill: cli.js + checksum: 10c0/7b1b7c7f17608a8f8d20a162e7957ac1ef6cd1636db1aba92f4e072dc31818c2ff0efac1e3d91064ede67ed5dc57c565420531a8134090a12ac10cf792ab14d2 + languageName: node + linkType: hard + "trim-newlines@npm:^3.0.0": version: 3.0.1 resolution: "trim-newlines@npm:3.0.1" @@ -17877,6 +18636,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:2.8.1, tslib@npm:^2.0.1": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 + languageName: node + linkType: hard + "tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": version: 2.6.2 resolution: "tslib@npm:2.6.2" @@ -18323,6 +19089,22 @@ __metadata: languageName: node linkType: hard +"uid@npm:2.0.2": + version: 2.0.2 + resolution: "uid@npm:2.0.2" + dependencies: + "@lukeed/csprng": "npm:^1.0.0" + checksum: 10c0/e9d02d0562c74e74b5a2519e586db9d7f8204978e476cddd191ee1a9efb85efafdbab2dbf3fc3dde0f5da01fd9da161f37d604dabf513447fd2c03d008f1324c + languageName: node + linkType: hard + +"uint8array-extras@npm:^1.4.0": + version: 1.5.0 + resolution: "uint8array-extras@npm:1.5.0" + checksum: 10c0/0e74641ac7dadb02eadefc1ccdadba6010e007757bda824960de3c72bbe2b04e6d3af75648441f412148c4103261d54fcb60be45a2863beb76643a55fddba3bd + languageName: node + linkType: hard + "unbox-primitive@npm:^1.0.2": version: 1.0.2 resolution: "unbox-primitive@npm:1.0.2" @@ -19074,7 +19856,7 @@ __metadata: languageName: node linkType: hard -"wcwidth@npm:^1.0.0, wcwidth@npm:^1.0.1": +"wcwidth@npm:>=1.0.1, wcwidth@npm:^1.0.0, wcwidth@npm:^1.0.1": version: 1.0.1 resolution: "wcwidth@npm:1.0.1" dependencies: From 506d07a994b153f9c2199288a9dc8583e0385d92 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Mon, 9 Feb 2026 09:55:31 +0100 Subject: [PATCH 02/26] Document SDK return shapes --- .claude/settings.local.json | 17 ---- packages/rest-api-sdk/README.md | 162 +++++++++++++++++++++++++------- 2 files changed, 126 insertions(+), 53 deletions(-) delete mode 100644 .claude/settings.local.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 789e1b62..00000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "permissions": { - "allow": [ - "WebFetch(domain:raw.githubusercontent.com)", - "Bash(yarn install)", - "Bash(yarn generate)", - "WebFetch(domain:heyapi.dev)", - "Bash(yarn build)", - "Bash(yarn tsc:*)", - "Bash(yarn format:*)", - "Bash(yarn lint)", - "Bash(yarn prettier)", - "Bash(yarn prettier:*)", - "Bash(NODE_TLS_REJECT_UNAUTHORIZED=0 yarn build:*)" - ] - } -} diff --git a/packages/rest-api-sdk/README.md b/packages/rest-api-sdk/README.md index 0a8b4792..3a496a87 100644 --- a/packages/rest-api-sdk/README.md +++ b/packages/rest-api-sdk/README.md @@ -12,6 +12,8 @@ yarn add @reflag/rest-api-sdk ## Quick Start +All API requests require a Bearer token. Get your API key from the Reflag dashboard. + ```typescript import { Api } from "@reflag/rest-api-sdk"; @@ -20,9 +22,14 @@ const api = new Api({ }); const apps = await api.listApps(); +// { +// data: [{ id, name, demo, flagKeyFormat, org: { id, name } }] +// } console.log(apps.data); ``` +Most methods take an `appId`, or you can create an app-scoped client to avoid passing it in: + ```typescript import { createAppClient } from "@reflag/rest-api-sdk"; @@ -35,21 +42,14 @@ const environments = await appApi.listEnvironments({ sortBy: "order", sortOrder: "asc", }); +// { +// data: [{ id, name, isProduction, order }], +// sortBy, +// sortOrder +// } console.log(environments.data); ``` -## Authentication - -All API requests require a Bearer token. Get your API key from the Reflag dashboard. - -```typescript -import { Api } from "@reflag/rest-api-sdk"; - -const api = new Api({ - accessToken: "your-api-key", -}); -``` - ## API Methods ### Applications @@ -61,14 +61,28 @@ const api = new Api(); // List all apps const apps = await api.listApps(); +// { +// data: [{ id, name, demo, flagKeyFormat, org: { id, name } }] +// } // Filter by organization const appsByOrg = await api.listApps({ orgId: "org-123" }); +// same return shape as listApps() // Get a single app const app = await api.getApp({ appId: "app-123", }); +// { +// id, +// name, +// demo, +// flagKeyFormat, +// environments: [{ id, name, isProduction, order, sdkAccess }], +// stages: [{ id, name, color, assignedFlagCount, order }], +// segments: [{ id, name, type }], +// org: { id, name } +// } ``` ### Environments @@ -83,37 +97,66 @@ const environments = await appApi.listEnvironments({ sortBy: "order", sortOrder: "asc", }); +// { +// data: [{ id, name, isProduction, order }], +// sortBy, +// sortOrder +// } const environment = await appApi.getEnvironment({ envId: "env-456", }); -``` - -You can also pass `appId` with every call instead of using `createAppClient`: - -```typescript -const environments = await api.listEnvironments({ - appId: "app-123", - sortBy: "order", - sortOrder: "asc", -}); - -const environment = await api.getEnvironment({ - appId: "app-123", - envId: "env-456", -}); +// { +// id, +// name, +// isProduction, +// order, +// sdkAccess: { publishableKey, secretKey } +// } ``` ### Flags ```typescript const flags = await api.listFlags({ appId: "app-123" }); +// { +// data: [ +// { +// id, +// key, +// name, +// description?, +// stage?, +// owner?, +// archived, +// stale, +// permanent, +// createdAt?, +// lastCheckAt?, +// lastTrackAt? +// } +// ], +// totalCount, +// pageSize, +// pageIndex, +// sortBy, +// sortOrder +// } const targeting = await api.getFlagTargeting({ appId: "app-123", flagKey: "my-feature-flag", envId: "env-456", }); +// { +// flagKey, +// version, +// updatedAt, +// specificTargets: { +// // Currently only "true" exists because this API supports the true variant. +// "true": { companyIds, userIds } +// } +// } const updated = await api.updateBulkFlagSpecificTargets({ appId: "app-123", @@ -128,6 +171,9 @@ const updated = await api.updateBulkFlagSpecificTargets({ changeDescription: "Enabling new feature for beta testers", }, }); +// { +// data: [{ flagKey, version, updatedAt, specificTargets }] +// } ``` ### Company Flags @@ -138,18 +184,40 @@ const companyFlags = await api.getCompanyFlags({ companyId: "company-1", envId: "env-456", }); +// { +// data: [ +// { +// id, +// key, +// name, +// value, +// specificallyTargetedValue, +// firstExposureAt, +// lastExposureAt, +// lastCheckAt, +// exposureCount, +// firstTrackAt, +// lastTrackAt, +// trackCount +// } +// ], +// totalCount, +// pageSize, +// pageIndex +// } const updatedCompanyFlags = await api.updateCompanyFlags({ appId: "app-123", companyId: "company-1", envId: "env-456", updateEntityFlagsBody: { - flags: { - "feature-flag": true, - "another-flag": false, - }, + updates: [ + { flagKey: "feature-flag", value: true }, + { flagKey: "another-flag", value: null }, + ], }, }); +// same return shape as getCompanyFlags() ``` ### User Flags @@ -160,18 +228,40 @@ const userFlags = await api.getUserFlags({ userId: "user-1", envId: "env-456", }); +// { +// data: [ +// { +// id, +// key, +// name, +// value, +// specificallyTargetedValue, +// firstExposureAt, +// lastExposureAt, +// lastCheckAt, +// exposureCount, +// firstTrackAt, +// lastTrackAt, +// trackCount +// } +// ], +// totalCount, +// pageSize, +// pageIndex +// } const updatedUserFlags = await api.updateUserFlags({ appId: "app-123", userId: "user-1", envId: "env-456", updateEntityFlagsBody: { - flags: { - "feature-flag": true, - "beta-feature": true, - }, + updates: [ + { flagKey: "feature-flag", value: true }, + { flagKey: "beta-feature", value: true }, + ], }, }); +// same return shape as getUserFlags() ``` ## Error Handling @@ -198,7 +288,7 @@ All generated types are exported for use in your application: import type { AppHeader, EnvironmentHeader, - EnvironmentDetails, + Environment, FlagHeader, FlagTargeting, ErrorResponse, From 3fb71b597a210636ade315653bc4e1f50f73daf0 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Sat, 21 Feb 2026 22:55:59 +0100 Subject: [PATCH 03/26] Simplify rest-api-sdk flag update API surface --- packages/rest-api-sdk/README.md | 292 +++++------------- .../customer-admin-panel/app/flags/actions.ts | 8 +- packages/rest-api-sdk/src/api.ts | 140 ++++++++- packages/rest-api-sdk/src/index.ts | 8 +- 4 files changed, 211 insertions(+), 237 deletions(-) diff --git a/packages/rest-api-sdk/README.md b/packages/rest-api-sdk/README.md index 3a496a87..b17916de 100644 --- a/packages/rest-api-sdk/README.md +++ b/packages/rest-api-sdk/README.md @@ -1,6 +1,6 @@ # @reflag/rest-api-sdk -Type-safe REST API client for the Reflag API. +Typed Node/browser SDK for Reflag's REST API. ## Installation @@ -10,30 +10,47 @@ npm install @reflag/rest-api-sdk yarn add @reflag/rest-api-sdk ``` -## Quick Start +## Create a client -All API requests require a Bearer token. Get your API key from the Reflag dashboard. +All requests require a Reflag API key. ```typescript import { Api } from "@reflag/rest-api-sdk"; const api = new Api({ accessToken: process.env.REFLAG_API_KEY, + // Optional when using non-default host: + // basePath: "https://app.reflag.com/api", }); +``` + +## Quick start +```typescript const apps = await api.listApps(); -// { -// data: [{ id, name, demo, flagKeyFormat, org: { id, name } }] -// } console.log(apps.data); + +const app = apps.data[0]; +const appId = app?.id; + +if (appId) { + const environments = await api.listEnvironments({ + appId, + sortBy: "order", + sortOrder: "asc", + }); + + console.log(environments.data); +} ``` -Most methods take an `appId`, or you can create an app-scoped client to avoid passing it in: +## App-scoped client + +If most calls are for one app, use `createAppClient` to avoid repeating `appId`. ```typescript import { createAppClient } from "@reflag/rest-api-sdk"; -// App-scoped client keeps appId out of each call. const appApi = createAppClient("app-123", { accessToken: process.env.REFLAG_API_KEY, }); @@ -42,268 +59,111 @@ const environments = await appApi.listEnvironments({ sortBy: "order", sortOrder: "asc", }); -// { -// data: [{ id, name, isProduction, order }], -// sortBy, -// sortOrder -// } -console.log(environments.data); -``` - -## API Methods - -### Applications - -```typescript -import { Api } from "@reflag/rest-api-sdk"; - -const api = new Api(); - -// List all apps -const apps = await api.listApps(); -// { -// data: [{ id, name, demo, flagKeyFormat, org: { id, name } }] -// } - -// Filter by organization -const appsByOrg = await api.listApps({ orgId: "org-123" }); -// same return shape as listApps() -// Get a single app -const app = await api.getApp({ - appId: "app-123", -}); -// { -// id, -// name, -// demo, -// flagKeyFormat, -// environments: [{ id, name, isProduction, order, sdkAccess }], -// stages: [{ id, name, color, assignedFlagCount, order }], -// segments: [{ id, name, type }], -// org: { id, name } -// } +const flags = await appApi.listFlags({}); ``` -### Environments +## Common workflows -```typescript -import { createAppClient } from "@reflag/rest-api-sdk"; +### Read user flags for an environment -const appApi = createAppClient("app-123"); - -// App-scoped client (appId is implicit) -const environments = await appApi.listEnvironments({ - sortBy: "order", - sortOrder: "asc", -}); -// { -// data: [{ id, name, isProduction, order }], -// sortBy, -// sortOrder -// } +`getUserFlags` evaluates flag results for one user in one environment and returns +the user’s current values plus exposure/check metadata for each flag. -const environment = await appApi.getEnvironment({ +```typescript +const userFlags = await api.getUserFlags({ + appId: "app-123", envId: "env-456", + userId: "user-1", }); -// { -// id, -// name, -// isProduction, -// order, -// sdkAccess: { publishableKey, secretKey } -// } + +console.log(userFlags.data); ``` -### Flags +### Toggle a user flag + +Use `true` to explicitly target on, and `null` to remove specific targeting. ```typescript -const flags = await api.listFlags({ appId: "app-123" }); -// { -// data: [ -// { -// id, -// key, -// name, -// description?, -// stage?, -// owner?, -// archived, -// stale, -// permanent, -// createdAt?, -// lastCheckAt?, -// lastTrackAt? -// } -// ], -// totalCount, -// pageSize, -// pageIndex, -// sortBy, -// sortOrder -// } - -const targeting = await api.getFlagTargeting({ - appId: "app-123", - flagKey: "my-feature-flag", - envId: "env-456", -}); -// { -// flagKey, -// version, -// updatedAt, -// specificTargets: { -// // Currently only "true" exists because this API supports the true variant. -// "true": { companyIds, userIds } -// } -// } - -const updated = await api.updateBulkFlagSpecificTargets({ +await api.updateUserFlags({ appId: "app-123", envId: "env-456", - bulkUpdateFlagSpecificTargetsSchema: { - updates: [ - { flagKey: "new-feature", value: true, companyId: "company-1" }, - { flagKey: "new-feature", value: true, userId: "user-1" }, - { flagKey: "old-feature", value: null, companyId: "company-1" }, - ], - notifications: true, - changeDescription: "Enabling new feature for beta testers", - }, + userId: "user-1", + updates: [{ flagKey: "new-checkout", value: true }], }); -// { -// data: [{ flagKey, version, updatedAt, specificTargets }] -// } ``` -### Company Flags +### Read and update company flags ```typescript const companyFlags = await api.getCompanyFlags({ appId: "app-123", - companyId: "company-1", envId: "env-456", + companyId: "company-1", }); -// { -// data: [ -// { -// id, -// key, -// name, -// value, -// specificallyTargetedValue, -// firstExposureAt, -// lastExposureAt, -// lastCheckAt, -// exposureCount, -// firstTrackAt, -// lastTrackAt, -// trackCount -// } -// ], -// totalCount, -// pageSize, -// pageIndex -// } - -const updatedCompanyFlags = await api.updateCompanyFlags({ + +await api.updateCompanyFlags({ appId: "app-123", - companyId: "company-1", envId: "env-456", - updateEntityFlagsBody: { - updates: [ - { flagKey: "feature-flag", value: true }, - { flagKey: "another-flag", value: null }, - ], - }, + companyId: "company-1", + updates: [{ flagKey: "new-checkout", value: null }], }); -// same return shape as getCompanyFlags() ``` -### User Flags +### Bulk update specific targets for multiple flags ```typescript -const userFlags = await api.getUserFlags({ - appId: "app-123", - userId: "user-1", - envId: "env-456", -}); -// { -// data: [ -// { -// id, -// key, -// name, -// value, -// specificallyTargetedValue, -// firstExposureAt, -// lastExposureAt, -// lastCheckAt, -// exposureCount, -// firstTrackAt, -// lastTrackAt, -// trackCount -// } -// ], -// totalCount, -// pageSize, -// pageIndex -// } - -const updatedUserFlags = await api.updateUserFlags({ +await api.updateBulkFlagSpecificTargets({ appId: "app-123", - userId: "user-1", envId: "env-456", - updateEntityFlagsBody: { + bulkUpdateFlagSpecificTargetsSchema: { updates: [ - { flagKey: "feature-flag", value: true }, - { flagKey: "beta-feature", value: true }, + { flagKey: "new-checkout", value: true, companyId: "company-1" }, + { flagKey: "new-checkout", value: true, userId: "user-1" }, + { flagKey: "legacy-checkout", value: null, userId: "user-1" }, ], + notifications: true, + changeDescription: "Rolling out new checkout to pilot accounts", }, }); -// same return shape as getUserFlags() ``` -## Error Handling +## Error handling -Methods throw on non-2xx responses. Catch errors and inspect the response when needed. +The SDK throws `ReflagApiError` for non-2xx API responses. ```typescript +import { ReflagApiError } from "@reflag/rest-api-sdk"; + try { - const apps = await api.listApps(); - console.log(apps.data); + await api.listApps(); } catch (error) { - if (error instanceof Error) { - console.error("Request failed", error.message); + if (error instanceof ReflagApiError) { + console.error(error.status, error.code, error.message, error.details); } throw error; } ``` -## Types +## API surface -All generated types are exported for use in your application: +Main exports: -```typescript -import type { - AppHeader, - EnvironmentHeader, - Environment, - FlagHeader, - FlagTargeting, - ErrorResponse, -} from "@reflag/rest-api-sdk"; -``` +- `Api`: base client +- `createAppClient(appId, config)`: app-scoped client +- `ReflagApiError`: normalized API error type +- Generated request/response types and models from `@reflag/rest-api-sdk` -## Regenerating the SDK +Core method groups: -To regenerate the SDK: +- Applications: `listApps`, `getApp` +- Environments: `listEnvironments`, `getEnvironment` +- Flags: `listFlags`, `getFlagTargeting`, `updateBulkFlagSpecificTargets` +- User/company evaluation: `getUserFlags`, `updateUserFlags`, `getCompanyFlags`, `updateCompanyFlags` -```bash -yarn generate -``` +## Example app -The schema source lives at `https://app.reflag.com/openapi.json`. +See `packages/rest-api-sdk/examples/customer-admin-panel/README.md` for a small Next.js app using this SDK in server actions. ## License diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts index 184d6701..da5dc670 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts @@ -86,9 +86,7 @@ export async function toggleUserFlag( await client.updateUserFlags({ envId, userId, - updateEntityFlagsBody: { - updates: [{ flagKey, value: enabled ? true : null }], - }, + updates: [{ flagKey, value: enabled ? true : null }], }); return await fetchUserFlags(appId, envId, userId); } @@ -128,9 +126,7 @@ export async function toggleCompanyFlag( await client.updateCompanyFlags({ envId, companyId, - updateEntityFlagsBody: { - updates: [{ flagKey, value: enabled ? true : null }], - }, + updates: [{ flagKey, value: enabled ? true : null }], }); return await fetchCompanyFlags(appId, envId, companyId); } diff --git a/packages/rest-api-sdk/src/api.ts b/packages/rest-api-sdk/src/api.ts index 0f1c734f..45533aba 100644 --- a/packages/rest-api-sdk/src/api.ts +++ b/packages/rest-api-sdk/src/api.ts @@ -1,10 +1,22 @@ -import { Configuration, DefaultApi } from "./generated"; -import type { ConfigurationParameters, RequestOpts, InitOverrideFunction } from "./generated/runtime"; +import type { + ConfigurationParameters, + InitOverrideFunction, + RequestOpts, +} from "./generated/runtime"; import { ResponseError } from "./generated/runtime"; +import { + Configuration, + DefaultApi, + type EntityFlagsResponse, + type UpdateCompanyFlagsRequest, + type UpdateEntityFlagsBody, + type UpdateUserFlagsRequest, +} from "./generated"; -type FirstArg = F extends (arg1: infer A, ...rest: infer R) => any ? [A, R] : never; - -type OmitAppIdParam = F extends (arg1: infer A, ...rest: infer R) => infer Ret +type OmitAppIdParam = F extends ( + arg1: infer A, + ...rest: infer R +) => infer Ret ? A extends { appId: string } ? (arg1: Omit, ...rest: R) => Ret : F @@ -14,12 +26,39 @@ export type AppScopedApi = { [K in keyof T]: OmitAppIdParam; }; +type EntityFlagUpdatesPayload = { + updates: UpdateEntityFlagsBody["updates"]; + changeDescription?: UpdateEntityFlagsBody["changeDescription"]; + notifications?: UpdateEntityFlagsBody["notifications"]; +}; + +type FlattenEntityFlagsBody< + T extends { updateEntityFlagsBody?: UpdateEntityFlagsBody }, +> = Omit & EntityFlagUpdatesPayload; + +type UpdateCompanyFlagsFriendlyParams = + FlattenEntityFlagsBody; +type UpdateUserFlagsFriendlyParams = + FlattenEntityFlagsBody; + +export type UpdateCompanyFlagsParams = + | UpdateCompanyFlagsRequest + | UpdateCompanyFlagsFriendlyParams; +export type UpdateUserFlagsParams = + | UpdateUserFlagsRequest + | UpdateUserFlagsFriendlyParams; + export class ReflagApiError extends Error { status: number; code?: string; details?: unknown; - constructor(status: number, message: string, code?: string, details?: unknown) { + constructor( + status: number, + message: string, + code?: string, + details?: unknown, + ) { super(message); this.name = "ReflagApiError"; this.status = status; @@ -38,7 +77,9 @@ async function buildApiError(response: Response) { if (contentType.includes("application/json")) { try { details = await response.clone().json(); - const maybeError = (details as { error?: { message?: string; code?: string } })?.error; + const maybeError = ( + details as { error?: { message?: string; code?: string } } + )?.error; if (maybeError?.message) { message = maybeError.message; } @@ -63,6 +104,44 @@ async function buildApiError(response: Response) { return new ReflagApiError(status, message, code, details); } +function normalizeUpdateCompanyFlagsRequest( + requestParameters: UpdateCompanyFlagsParams, +): UpdateCompanyFlagsRequest { + if ("updateEntityFlagsBody" in requestParameters) { + return requestParameters; + } + + const { updates, changeDescription, notifications, ...rest } = + requestParameters as UpdateCompanyFlagsFriendlyParams; + return { + ...rest, + updateEntityFlagsBody: { + updates, + changeDescription, + notifications, + }, + }; +} + +function normalizeUpdateUserFlagsRequest( + requestParameters: UpdateUserFlagsParams, +): UpdateUserFlagsRequest { + if ("updateEntityFlagsBody" in requestParameters) { + return requestParameters; + } + + const { updates, changeDescription, notifications, ...rest } = + requestParameters as UpdateUserFlagsFriendlyParams; + return { + ...rest, + updateEntityFlagsBody: { + updates, + changeDescription, + notifications, + }, + }; +} + export class Api extends DefaultApi { constructor(config?: ConfigurationParameters) { super(new Configuration(config)); @@ -81,24 +160,59 @@ export class Api extends DefaultApi { throw error; } } + + async updateCompanyFlags( + requestParameters: UpdateCompanyFlagsParams, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + return await super.updateCompanyFlags( + normalizeUpdateCompanyFlagsRequest(requestParameters), + initOverrides, + ); + } + + async updateUserFlags( + requestParameters: UpdateUserFlagsParams, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + return await super.updateUserFlags( + normalizeUpdateUserFlagsRequest(requestParameters), + initOverrides, + ); + } } -export function withAppId(api: T, appId: string): AppScopedApi { +function scopeApiToAppId( + api: T, + appId: string, +): AppScopedApi { return new Proxy(api, { get(target, prop, receiver) { const value = Reflect.get(target, prop, receiver); if (typeof value !== "function") return value; return (arg1: unknown, ...rest: unknown[]) => { if (arg1 && typeof arg1 === "object" && !Array.isArray(arg1)) { - const args = "appId" in (arg1 as object) ? arg1 : { ...(arg1 as object), appId }; - return (value as (...args: unknown[]) => unknown).call(target, args, ...rest); + const args = + "appId" in (arg1 as object) ? arg1 : { ...(arg1 as object), appId }; + return (value as (...args: unknown[]) => unknown).call( + target, + args, + ...rest, + ); } - return (value as (...args: unknown[]) => unknown).call(target, arg1, ...rest); + return (value as (...args: unknown[]) => unknown).call( + target, + arg1, + ...rest, + ); }; }, }) as AppScopedApi; } -export function createAppClient(appId: string, config?: ConfigurationParameters) { - return withAppId(new Api(config), appId); +export function createAppClient( + appId: string, + config?: ConfigurationParameters, +) { + return scopeApiToAppId(new Api(config), appId); } diff --git a/packages/rest-api-sdk/src/index.ts b/packages/rest-api-sdk/src/index.ts index 1f63fb02..03f94843 100644 --- a/packages/rest-api-sdk/src/index.ts +++ b/packages/rest-api-sdk/src/index.ts @@ -1,3 +1,7 @@ +export type { + AppScopedApi, + UpdateCompanyFlagsParams, + UpdateUserFlagsParams, +} from "./api"; +export { Api, createAppClient,ReflagApiError } from "./api"; export * from "./generated"; -export { Api, ReflagApiError, createAppClient, withAppId } from "./api"; -export type { AppScopedApi } from "./api"; From 3abf580599469742db978dde3da897dc2b80ab7f Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Sat, 21 Feb 2026 23:26:57 +0100 Subject: [PATCH 04/26] Customize generator to flatten SDK request body params --- packages/rest-api-sdk/README.md | 16 +- .../openapi-generator.config.yaml | 1 + .../openapi-templates/apis.mustache | 505 ++++++++++++++++++ packages/rest-api-sdk/src/api.ts | 89 +-- .../src/generated/.openapi-generator/FILES | 1 - .../src/generated/apis/DefaultApi.ts | 87 ++- packages/rest-api-sdk/src/index.ts | 8 +- 7 files changed, 594 insertions(+), 113 deletions(-) create mode 100644 packages/rest-api-sdk/openapi-templates/apis.mustache diff --git a/packages/rest-api-sdk/README.md b/packages/rest-api-sdk/README.md index b17916de..96034f8d 100644 --- a/packages/rest-api-sdk/README.md +++ b/packages/rest-api-sdk/README.md @@ -116,15 +116,13 @@ await api.updateCompanyFlags({ await api.updateBulkFlagSpecificTargets({ appId: "app-123", envId: "env-456", - bulkUpdateFlagSpecificTargetsSchema: { - updates: [ - { flagKey: "new-checkout", value: true, companyId: "company-1" }, - { flagKey: "new-checkout", value: true, userId: "user-1" }, - { flagKey: "legacy-checkout", value: null, userId: "user-1" }, - ], - notifications: true, - changeDescription: "Rolling out new checkout to pilot accounts", - }, + updates: [ + { flagKey: "new-checkout", value: true, companyId: "company-1" }, + { flagKey: "new-checkout", value: true, userId: "user-1" }, + { flagKey: "legacy-checkout", value: null, userId: "user-1" }, + ], + notifications: true, + changeDescription: "Rolling out new checkout to pilot accounts", }); ``` diff --git a/packages/rest-api-sdk/openapi-generator.config.yaml b/packages/rest-api-sdk/openapi-generator.config.yaml index 9ab6ecf9..7d269197 100644 --- a/packages/rest-api-sdk/openapi-generator.config.yaml +++ b/packages/rest-api-sdk/openapi-generator.config.yaml @@ -1,6 +1,7 @@ generatorName: typescript-fetch inputSpec: ./openapi.json outputDir: src/generated +templateDir: ./openapi-templates additionalProperties: supportsES6: true typescriptThreePlus: true diff --git a/packages/rest-api-sdk/openapi-templates/apis.mustache b/packages/rest-api-sdk/openapi-templates/apis.mustache new file mode 100644 index 00000000..09a30896 --- /dev/null +++ b/packages/rest-api-sdk/openapi-templates/apis.mustache @@ -0,0 +1,505 @@ +/* tslint:disable */ +/* eslint-disable */ +{{>licenseInfo}} + + +import * as runtime from '../runtime{{importFileExtension}}'; +{{#imports.0}} +import type { + {{#imports}} + {{className}}, + {{/imports}} +} from '../models/index{{importFileExtension}}'; +{{^withoutRuntimeChecks}} +import { + {{#imports}} + {{className}}FromJSON, + {{className}}ToJSON, + {{/imports}} +} from '../models/index{{importFileExtension}}'; +{{/withoutRuntimeChecks}} +{{/imports.0}} + +{{#operations}} +{{#operation}} +{{#allParams.0}} +export interface {{#prefixParameterInterfaces}}{{classname}}{{/prefixParameterInterfaces}}{{operationIdCamelCase}}Request { +{{#allParams}} + {{#isBodyParam}} + /** + * List of flag updates to apply + * @type {{=<% %>=}}<%&dataType%>['updates']<%={{ }}=%> + * @memberof {{{dataType}}} + */ + updates: {{=<% %>=}}<%&dataType%>['updates']<%={{ }}=%>; + /** + * Description of the change for audit history + * @type {string} + * @memberof {{{dataType}}} + */ + changeDescription?: {{=<% %>=}}<%&dataType%>['changeDescription']<%={{ }}=%>; + /** + * Whether to send notifications about the change (default: true) + * @type {boolean} + * @memberof {{{dataType}}} + */ + notifications?: {{=<% %>=}}<%&dataType%>['notifications']<%={{ }}=%>; + {{/isBodyParam}} + {{^isBodyParam}} + {{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{#hasReadOnly}}Omit<{{{dataType}}}, {{#readOnlyVars}}'{{baseName}}'{{^-last}}|{{/-last}}{{/readOnlyVars}}>{{/hasReadOnly}}{{^hasReadOnly}}{{{dataType}}}{{/hasReadOnly}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}; + {{/isBodyParam}} +{{/allParams}} +} + +{{/allParams.0}} +{{/operation}} +{{/operations}} +{{#withInterfaces}} +{{#operations}} +/** + * {{classname}} - interface + * {{#lambda.indented_1}}{{{unescapedDescription}}}{{/lambda.indented_1}} + * @export + * @interface {{classname}}Interface + */ +export interface {{classname}}Interface { +{{#operation}} + /** + * {{¬es}} + {{#summary}} + * @summary {{&summary}} + {{/summary}} + {{#allParams}} + {{#isBodyParam}} + * @param {{=<% %>=}}{<%&dataType%>['updates']}<%={{ }}=%> updates List of flag updates to apply + * @param {{=<% %>=}}{<%&dataType%>['changeDescription']}<%={{ }}=%> [changeDescription] Description of the change for audit history + * @param {{=<% %>=}}{<%&dataType%>['notifications']}<%={{ }}=%> [notifications] Whether to send notifications about the change (default: true) + {{/isBodyParam}} + {{^isBodyParam}} + * @param {{=<% %>=}}{<%&dataType%>}<%={{ }}=%> {{^required}}[{{/required}}{{paramName}}{{^required}}]{{/required}} {{description}} + {{/isBodyParam}} + {{/allParams}} + * @param {*} [options] Override http request option. + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + * @throws {RequiredError} + * @memberof {{classname}}Interface + */ + {{nickname}}Raw({{#allParams.0}}requestParameters: {{#prefixParameterInterfaces}}{{classname}}{{/prefixParameterInterfaces}}{{operationIdCamelCase}}Request, {{/allParams.0}}initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + */ + {{^useSingleRequestParameter}} + {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}, {{/allParams}}initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<{{{returnType}}}{{#returnType}}{{#isResponseOptional}} | null | undefined {{/isResponseOptional}}{{/returnType}}{{^returnType}}void{{/returnType}}>; + {{/useSingleRequestParameter}} + {{#useSingleRequestParameter}} + {{nickname}}({{#allParams.0}}requestParameters: {{#prefixParameterInterfaces}}{{classname}}{{/prefixParameterInterfaces}}{{operationIdCamelCase}}Request, {{/allParams.0}}initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<{{{returnType}}}{{#returnType}}{{#isResponseOptional}} | null | undefined {{/isResponseOptional}}{{/returnType}}{{^returnType}}void{{/returnType}}>; + {{/useSingleRequestParameter}} + +{{/operation}} +} + +{{/operations}} +{{/withInterfaces}} +{{#operations}} +/** + * {{#lambda.indented_star_1}}{{{unescapedDescription}}}{{/lambda.indented_star_1}} + */ +{{#withInterfaces}} +export class {{classname}} extends runtime.BaseAPI implements {{classname}}Interface { +{{/withInterfaces}} +{{^withInterfaces}} +export class {{classname}} extends runtime.BaseAPI { +{{/withInterfaces}} + + {{#operation}} + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + */ + async {{nickname}}Raw({{#allParams.0}}requestParameters: {{#prefixParameterInterfaces}}{{classname}}{{/prefixParameterInterfaces}}{{operationIdCamelCase}}Request, {{/allParams.0}}initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + {{#allParams}} + {{#required}} + if (requestParameters['{{paramName}}'] == null) { + throw new runtime.RequiredError( + '{{paramName}}', + 'Required parameter "{{paramName}}" was null or undefined when calling {{nickname}}().' + ); + } + + {{/required}} + {{/allParams}} + const queryParameters: any = {}; + + {{#queryParams}} + {{#isArray}} + if (requestParameters['{{paramName}}'] != null) { + {{#isCollectionFormatMulti}} + queryParameters['{{baseName}}'] = requestParameters['{{paramName}}']; + {{/isCollectionFormatMulti}} + {{^isCollectionFormatMulti}} + queryParameters['{{baseName}}'] = {{#uniqueItems}}Array.from({{/uniqueItems}}requestParameters['{{paramName}}']{{#uniqueItems}}){{/uniqueItems}}!.join(runtime.COLLECTION_FORMATS["{{collectionFormat}}"]); + {{/isCollectionFormatMulti}} + } + + {{/isArray}} + {{^isArray}} + if (requestParameters['{{paramName}}'] != null) { + {{#isExplode}} + {{#isContainer}} + for (let key of Object.keys(requestParameters['{{paramName}}'])) { + queryParameters[key] = requestParameters['{{paramName}}'][key]; + } + {{/isContainer}} + {{^isContainer}} +{{>apisAssignQueryParam}} + {{/isContainer}} + {{/isExplode}} + {{^isExplode}} +{{>apisAssignQueryParam}} + {{/isExplode}} + } + + {{/isArray}} + {{/queryParams}} + const headerParameters: runtime.HTTPHeaders = {}; + + {{#bodyParam}} + {{^consumes}} + headerParameters['Content-Type'] = 'application/json'; + + {{/consumes}} + {{#consumes.0}} + headerParameters['Content-Type'] = '{{{mediaType}}}'; + + {{/consumes.0}} + {{/bodyParam}} + {{#headerParams}} + {{#isArray}} + if (requestParameters['{{paramName}}'] != null) { + headerParameters['{{baseName}}'] = {{#uniqueItems}}Array.from({{/uniqueItems}}requestParameters['{{paramName}}']{{#uniqueItems}}){{/uniqueItems}}!.join(runtime.COLLECTION_FORMATS["{{collectionFormat}}"]); + } + + {{/isArray}} + {{^isArray}} + if (requestParameters['{{paramName}}'] != null) { + headerParameters['{{baseName}}'] = String(requestParameters['{{paramName}}']); + } + + {{/isArray}} + {{/headerParams}} + {{#authMethods}} + {{#isBasic}} + {{#isBasicBasic}} + if (this.configuration && (this.configuration.username !== undefined || this.configuration.password !== undefined)) { + headerParameters["Authorization"] = "Basic " + btoa(this.configuration.username + ":" + this.configuration.password); + } + {{/isBasicBasic}} + {{#isBasicBearer}} + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("{{name}}", [{{#scopes}}"{{{scope}}}"{{^-last}}, {{/-last}}{{/scopes}}]); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + {{/isBasicBearer}} + {{/isBasic}} + {{#isApiKey}} + {{#isKeyInHeader}} + if (this.configuration && this.configuration.apiKey) { + headerParameters["{{keyParamName}}"] = await this.configuration.apiKey("{{keyParamName}}"); // {{name}} authentication + } + + {{/isKeyInHeader}} + {{#isKeyInQuery}} + if (this.configuration && this.configuration.apiKey) { + queryParameters["{{keyParamName}}"] = await this.configuration.apiKey("{{keyParamName}}"); // {{name}} authentication + } + + {{/isKeyInQuery}} + {{/isApiKey}} + {{#isOAuth}} + if (this.configuration && this.configuration.accessToken) { + // oauth required + headerParameters["Authorization"] = await this.configuration.accessToken("{{name}}", [{{#scopes}}"{{{scope}}}"{{^-last}}, {{/-last}}{{/scopes}}]); + } + + {{/isOAuth}} + {{/authMethods}} + {{#hasFormParams}} + const consumes: runtime.Consume[] = [ + {{#consumes}} + { contentType: '{{{mediaType}}}' }, + {{/consumes}} + ]; + // @ts-ignore: canConsumeForm may be unused + const canConsumeForm = runtime.canConsumeForm(consumes); + + let formParams: { append(param: string, value: any): any }; + let useForm = false; + {{#formParams}} + {{#isFile}} + // use FormData to transmit files using content-type "multipart/form-data" + useForm = canConsumeForm; + {{/isFile}} + {{/formParams}} + if (useForm) { + formParams = new FormData(); + } else { + formParams = new URLSearchParams(); + } + + {{#formParams}} + {{#isArray}} + if (requestParameters['{{paramName}}'] != null) { + {{#isCollectionFormatMulti}} + requestParameters['{{paramName}}'].forEach((element) => { + formParams.append('{{baseName}}{{#useSquareBracketsInArrayNames}}[]{{/useSquareBracketsInArrayNames}}', element as any); + }) + {{/isCollectionFormatMulti}} + {{^isCollectionFormatMulti}} + formParams.append('{{baseName}}{{#useSquareBracketsInArrayNames}}[]{{/useSquareBracketsInArrayNames}}', {{#uniqueItems}}Array.from({{/uniqueItems}}requestParameters['{{paramName}}']{{#uniqueItems}}){{/uniqueItems}}!.join(runtime.COLLECTION_FORMATS["{{collectionFormat}}"])); + {{/isCollectionFormatMulti}} + } + + {{/isArray}} + {{^isArray}} + if (requestParameters['{{paramName}}'] != null) { + {{#isDateTimeType}} + formParams.append('{{baseName}}', (requestParameters['{{paramName}}'] as any).toISOString()); + {{/isDateTimeType}} + {{^isDateTimeType}} + {{#isPrimitiveType}} + formParams.append('{{baseName}}', requestParameters['{{paramName}}'] as any); + {{/isPrimitiveType}} + {{^isPrimitiveType}} + {{#isEnumRef}} + formParams.append('{{baseName}}', requestParameters['{{paramName}}'] as any); + {{/isEnumRef}} + {{^isEnumRef}} + {{^withoutRuntimeChecks}} + formParams.append('{{baseName}}', new Blob([JSON.stringify({{{dataType}}}ToJSON(requestParameters['{{paramName}}']))], { type: "application/json", })); + {{/withoutRuntimeChecks}}{{#withoutRuntimeChecks}} + formParams.append('{{baseName}}', new Blob([JSON.stringify(requestParameters['{{paramName}}'])], { type: "application/json", })); + {{/withoutRuntimeChecks}} + {{/isEnumRef}} + {{/isPrimitiveType}} + {{/isDateTimeType}} + } + + {{/isArray}} + {{/formParams}} + {{/hasFormParams}} + + let urlPath = `{{{path}}}`; + {{#pathParams}} + {{#isDateTimeType}} + if (requestParameters['{{paramName}}'] instanceof Date) { + urlPath = urlPath.replace(`{${"{{baseName}}"}}`, encodeURIComponent(requestParameters['{{paramName}}'].toISOString())); + } else { + urlPath = urlPath.replace(`{${"{{baseName}}"}}`, encodeURIComponent(String(requestParameters['{{paramName}}']))); + } + {{/isDateTimeType}} + {{^isDateTimeType}} + {{#isDateType}} + if (requestParameters['{{paramName}}'] instanceof Date) { + urlPath = urlPath.replace(`{${"{{baseName}}"}}`, encodeURIComponent(requestParameters['{{paramName}}'].toISOString().substring(0,10))); + } else { + urlPath = urlPath.replace(`{${"{{baseName}}"}}`, encodeURIComponent(String(requestParameters['{{paramName}}']))); + } + {{/isDateType}} + {{^isDateType}} + urlPath = urlPath.replace(`{${"{{baseName}}"}}`, encodeURIComponent(String(requestParameters['{{paramName}}']))); + {{/isDateType}} + {{/isDateTimeType}} + {{/pathParams}} + + const response = await this.request({ + path: urlPath, + method: '{{httpMethod}}', + headers: headerParameters, + query: queryParameters, + {{#hasBodyParam}} + {{#bodyParam}} + {{^withoutRuntimeChecks}} + body: {{dataType}}ToJSON({ + updates: requestParameters['updates'], + changeDescription: requestParameters['changeDescription'], + notifications: requestParameters['notifications'], + }), + {{/withoutRuntimeChecks}} + {{#withoutRuntimeChecks}} + body: { + updates: requestParameters['updates'], + changeDescription: requestParameters['changeDescription'], + notifications: requestParameters['notifications'], + }, + {{/withoutRuntimeChecks}} + {{/bodyParam}} + {{/hasBodyParam}} + {{#hasFormParams}} + body: formParams, + {{/hasFormParams}} + }, initOverrides); + + {{#returnType}} + {{#isResponseFile}} + return new runtime.BlobApiResponse(response); + {{/isResponseFile}} + {{^isResponseFile}} + {{#returnTypeIsPrimitive}} + {{#isMap}} + return new runtime.JSONApiResponse(response); + {{/isMap}} + {{#isArray}} + return new runtime.JSONApiResponse(response); + {{/isArray}} + {{#returnSimpleType}} + if (this.isJsonMime(response.headers.get('content-type'))) { + return new runtime.JSONApiResponse<{{returnType}}>(response); + } else { + return new runtime.TextApiResponse(response) as any; + } + {{/returnSimpleType}} + {{/returnTypeIsPrimitive}} + {{^returnTypeIsPrimitive}} + {{#isArray}} + return new runtime.JSONApiResponse(response{{^withoutRuntimeChecks}}, (jsonValue) => {{#uniqueItems}}new Set({{/uniqueItems}}jsonValue.map({{returnBaseType}}FromJSON){{/withoutRuntimeChecks}}){{#uniqueItems}}){{/uniqueItems}}; + {{/isArray}} + {{^isArray}} + {{#isMap}} + return new runtime.JSONApiResponse(response{{^withoutRuntimeChecks}}, (jsonValue) => runtime.mapValues(jsonValue, {{returnBaseType}}FromJSON){{/withoutRuntimeChecks}}); + {{/isMap}} + {{^isMap}} + return new runtime.JSONApiResponse(response{{^withoutRuntimeChecks}}, (jsonValue) => {{returnBaseType}}FromJSON(jsonValue){{/withoutRuntimeChecks}}); + {{/isMap}} + {{/isArray}} + {{/returnTypeIsPrimitive}} + {{/isResponseFile}} + {{/returnType}} + {{^returnType}} + return new runtime.VoidApiResponse(response); + {{/returnType}} + } + + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + {{#isDeprecated}} + * @deprecated + {{/isDeprecated}} + */ + {{^useSingleRequestParameter}} + async {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}, {{/allParams}}initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<{{{returnType}}}{{#returnType}}{{#isResponseOptional}} | null | undefined {{/isResponseOptional}}{{/returnType}}{{^returnType}}void{{/returnType}}> { + {{#returnType}} + const response = await this.{{nickname}}Raw({{#allParams.0}}{ {{#allParams}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/allParams}} }, {{/allParams.0}}initOverrides); + {{#isResponseOptional}} + switch (response.raw.status) { + {{#responses}} + {{#is2xx}} + case {{code}}: + return {{#dataType}}await response.value(){{/dataType}}{{^dataType}}null{{/dataType}}; + {{/is2xx}} + {{/responses}} + default: + return await response.value(); + } + {{/isResponseOptional}} + {{^isResponseOptional}} + return await response.value(); + {{/isResponseOptional}} + {{/returnType}} + {{^returnType}} + await this.{{nickname}}Raw({{#allParams.0}}{ {{#allParams}}{{paramName}}: {{paramName}}{{^-last}}, {{/-last}}{{/allParams}} }, {{/allParams.0}}initOverrides); + {{/returnType}} + } + {{/useSingleRequestParameter}} + {{#useSingleRequestParameter}} + async {{nickname}}({{#allParams.0}}requestParameters: {{#prefixParameterInterfaces}}{{classname}}{{/prefixParameterInterfaces}}{{operationIdCamelCase}}Request{{^hasRequiredParams}} = {}{{/hasRequiredParams}}, {{/allParams.0}}initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<{{{returnType}}}{{#returnType}}{{#isResponseOptional}} | null | undefined {{/isResponseOptional}}{{/returnType}}{{^returnType}}void{{/returnType}}> { + {{#returnType}} + const response = await this.{{nickname}}Raw({{#allParams.0}}requestParameters, {{/allParams.0}}initOverrides); + {{#isResponseOptional}} + switch (response.raw.status) { + {{#responses}} + {{#is2xx}} + case {{code}}: + return {{#dataType}}await response.value(){{/dataType}}{{^dataType}}null{{/dataType}}; + {{/is2xx}} + {{/responses}} + default: + return await response.value(); + } + {{/isResponseOptional}} + {{^isResponseOptional}} + return await response.value(); + {{/isResponseOptional}} + {{/returnType}} + {{^returnType}} + await this.{{nickname}}Raw({{#allParams.0}}requestParameters, {{/allParams.0}}initOverrides); + {{/returnType}} + } + {{/useSingleRequestParameter}} + + {{/operation}} +} +{{/operations}} +{{#hasEnums}} + +{{#operations}} +{{#operation}} +{{#allParams}} +{{#isEnum}} +{{#stringEnums}} +/** + * @export + * @enum {string} + */ +export enum {{operationIdCamelCase}}{{enumName}} { +{{#allowableValues}} + {{#enumVars}} + {{{name}}} = {{{value}}}{{^-last}},{{/-last}} + {{/enumVars}} +{{/allowableValues}} +} +{{/stringEnums}} +{{^stringEnums}} +/** + * @export + */ +export const {{operationIdCamelCase}}{{enumName}} = { +{{#allowableValues}} + {{#enumVars}} + {{{name}}}: {{{value}}}{{^-last}},{{/-last}} + {{/enumVars}} +{{/allowableValues}} +} as const; +export type {{operationIdCamelCase}}{{enumName}} = typeof {{operationIdCamelCase}}{{enumName}}[keyof typeof {{operationIdCamelCase}}{{enumName}}]; +{{/stringEnums}} +{{/isEnum}} +{{/allParams}} +{{/operation}} +{{/operations}} +{{/hasEnums}} diff --git a/packages/rest-api-sdk/src/api.ts b/packages/rest-api-sdk/src/api.ts index 45533aba..07de67ca 100644 --- a/packages/rest-api-sdk/src/api.ts +++ b/packages/rest-api-sdk/src/api.ts @@ -4,14 +4,7 @@ import type { RequestOpts, } from "./generated/runtime"; import { ResponseError } from "./generated/runtime"; -import { - Configuration, - DefaultApi, - type EntityFlagsResponse, - type UpdateCompanyFlagsRequest, - type UpdateEntityFlagsBody, - type UpdateUserFlagsRequest, -} from "./generated"; +import { Configuration, DefaultApi } from "./generated"; type OmitAppIdParam = F extends ( arg1: infer A, @@ -26,28 +19,6 @@ export type AppScopedApi = { [K in keyof T]: OmitAppIdParam; }; -type EntityFlagUpdatesPayload = { - updates: UpdateEntityFlagsBody["updates"]; - changeDescription?: UpdateEntityFlagsBody["changeDescription"]; - notifications?: UpdateEntityFlagsBody["notifications"]; -}; - -type FlattenEntityFlagsBody< - T extends { updateEntityFlagsBody?: UpdateEntityFlagsBody }, -> = Omit & EntityFlagUpdatesPayload; - -type UpdateCompanyFlagsFriendlyParams = - FlattenEntityFlagsBody; -type UpdateUserFlagsFriendlyParams = - FlattenEntityFlagsBody; - -export type UpdateCompanyFlagsParams = - | UpdateCompanyFlagsRequest - | UpdateCompanyFlagsFriendlyParams; -export type UpdateUserFlagsParams = - | UpdateUserFlagsRequest - | UpdateUserFlagsFriendlyParams; - export class ReflagApiError extends Error { status: number; code?: string; @@ -104,44 +75,6 @@ async function buildApiError(response: Response) { return new ReflagApiError(status, message, code, details); } -function normalizeUpdateCompanyFlagsRequest( - requestParameters: UpdateCompanyFlagsParams, -): UpdateCompanyFlagsRequest { - if ("updateEntityFlagsBody" in requestParameters) { - return requestParameters; - } - - const { updates, changeDescription, notifications, ...rest } = - requestParameters as UpdateCompanyFlagsFriendlyParams; - return { - ...rest, - updateEntityFlagsBody: { - updates, - changeDescription, - notifications, - }, - }; -} - -function normalizeUpdateUserFlagsRequest( - requestParameters: UpdateUserFlagsParams, -): UpdateUserFlagsRequest { - if ("updateEntityFlagsBody" in requestParameters) { - return requestParameters; - } - - const { updates, changeDescription, notifications, ...rest } = - requestParameters as UpdateUserFlagsFriendlyParams; - return { - ...rest, - updateEntityFlagsBody: { - updates, - changeDescription, - notifications, - }, - }; -} - export class Api extends DefaultApi { constructor(config?: ConfigurationParameters) { super(new Configuration(config)); @@ -160,26 +93,6 @@ export class Api extends DefaultApi { throw error; } } - - async updateCompanyFlags( - requestParameters: UpdateCompanyFlagsParams, - initOverrides?: RequestInit | InitOverrideFunction, - ): Promise { - return await super.updateCompanyFlags( - normalizeUpdateCompanyFlagsRequest(requestParameters), - initOverrides, - ); - } - - async updateUserFlags( - requestParameters: UpdateUserFlagsParams, - initOverrides?: RequestInit | InitOverrideFunction, - ): Promise { - return await super.updateUserFlags( - normalizeUpdateUserFlagsRequest(requestParameters), - initOverrides, - ); - } } function scopeApiToAppId( diff --git a/packages/rest-api-sdk/src/generated/.openapi-generator/FILES b/packages/rest-api-sdk/src/generated/.openapi-generator/FILES index 3bb71efe..6e7ea149 100644 --- a/packages/rest-api-sdk/src/generated/.openapi-generator/FILES +++ b/packages/rest-api-sdk/src/generated/.openapi-generator/FILES @@ -1,4 +1,3 @@ -.openapi-generator-ignore apis/DefaultApi.ts apis/index.ts index.ts diff --git a/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts b/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts index 5a0de6ec..dae57829 100644 --- a/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts +++ b/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts @@ -102,21 +102,72 @@ export interface ListFlagsRequest { export interface UpdateBulkFlagSpecificTargetsRequest { appId: string; envId: string; - bulkUpdateFlagSpecificTargetsSchema?: BulkUpdateFlagSpecificTargetsSchema; + /** + * List of flag updates to apply + * @type BulkUpdateFlagSpecificTargetsSchema['updates'] + * @memberof BulkUpdateFlagSpecificTargetsSchema + */ + updates: BulkUpdateFlagSpecificTargetsSchema['updates']; + /** + * Description of the change for audit history + * @type {string} + * @memberof BulkUpdateFlagSpecificTargetsSchema + */ + changeDescription?: BulkUpdateFlagSpecificTargetsSchema['changeDescription']; + /** + * Whether to send notifications about the change (default: true) + * @type {boolean} + * @memberof BulkUpdateFlagSpecificTargetsSchema + */ + notifications?: BulkUpdateFlagSpecificTargetsSchema['notifications']; } export interface UpdateCompanyFlagsRequest { appId: string; companyId: string; envId: string; - updateEntityFlagsBody?: UpdateEntityFlagsBody; + /** + * List of flag updates to apply + * @type UpdateEntityFlagsBody['updates'] + * @memberof UpdateEntityFlagsBody + */ + updates: UpdateEntityFlagsBody['updates']; + /** + * Description of the change for audit history + * @type {string} + * @memberof UpdateEntityFlagsBody + */ + changeDescription?: UpdateEntityFlagsBody['changeDescription']; + /** + * Whether to send notifications about the change (default: true) + * @type {boolean} + * @memberof UpdateEntityFlagsBody + */ + notifications?: UpdateEntityFlagsBody['notifications']; } export interface UpdateUserFlagsRequest { appId: string; userId: string; envId: string; - updateEntityFlagsBody?: UpdateEntityFlagsBody; + /** + * List of flag updates to apply + * @type UpdateEntityFlagsBody['updates'] + * @memberof UpdateEntityFlagsBody + */ + updates: UpdateEntityFlagsBody['updates']; + /** + * Description of the change for audit history + * @type {string} + * @memberof UpdateEntityFlagsBody + */ + changeDescription?: UpdateEntityFlagsBody['changeDescription']; + /** + * Whether to send notifications about the change (default: true) + * @type {boolean} + * @memberof UpdateEntityFlagsBody + */ + notifications?: UpdateEntityFlagsBody['notifications']; } /** @@ -268,7 +319,9 @@ export interface DefaultApiInterface { * @summary Update flag specific targets for an environment * @param {string} appId App identifier * @param {string} envId Environment identifier - * @param {BulkUpdateFlagSpecificTargetsSchema} [bulkUpdateFlagSpecificTargetsSchema] + * @param {BulkUpdateFlagSpecificTargetsSchema['updates']} updates List of flag updates to apply + * @param {BulkUpdateFlagSpecificTargetsSchema['changeDescription']} [changeDescription] Description of the change for audit history + * @param {BulkUpdateFlagSpecificTargetsSchema['notifications']} [notifications] Whether to send notifications about the change (default: true) * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DefaultApiInterface @@ -287,7 +340,9 @@ export interface DefaultApiInterface { * @param {string} appId App identifier * @param {string} companyId Company ID within your application * @param {string} envId Environment ID to evaluate targeting for - * @param {UpdateEntityFlagsBody} [updateEntityFlagsBody] + * @param {UpdateEntityFlagsBody['updates']} updates List of flag updates to apply + * @param {UpdateEntityFlagsBody['changeDescription']} [changeDescription] Description of the change for audit history + * @param {UpdateEntityFlagsBody['notifications']} [notifications] Whether to send notifications about the change (default: true) * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DefaultApiInterface @@ -306,7 +361,9 @@ export interface DefaultApiInterface { * @param {string} appId App identifier * @param {string} userId User ID within your application * @param {string} envId Environment ID to evaluate targeting for - * @param {UpdateEntityFlagsBody} [updateEntityFlagsBody] + * @param {UpdateEntityFlagsBody['updates']} updates List of flag updates to apply + * @param {UpdateEntityFlagsBody['changeDescription']} [changeDescription] Description of the change for audit history + * @param {UpdateEntityFlagsBody['notifications']} [notifications] Whether to send notifications about the change (default: true) * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DefaultApiInterface @@ -811,7 +868,11 @@ export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { method: 'PATCH', headers: headerParameters, query: queryParameters, - body: BulkUpdateFlagSpecificTargetsSchemaToJSON(requestParameters['bulkUpdateFlagSpecificTargetsSchema']), + body: BulkUpdateFlagSpecificTargetsSchemaToJSON({ + updates: requestParameters['updates'], + changeDescription: requestParameters['changeDescription'], + notifications: requestParameters['notifications'], + }), }, initOverrides); return new runtime.JSONApiResponse(response, (jsonValue) => FlagTargetingCollectionFromJSON(jsonValue)); @@ -880,7 +941,11 @@ export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { method: 'PATCH', headers: headerParameters, query: queryParameters, - body: UpdateEntityFlagsBodyToJSON(requestParameters['updateEntityFlagsBody']), + body: UpdateEntityFlagsBodyToJSON({ + updates: requestParameters['updates'], + changeDescription: requestParameters['changeDescription'], + notifications: requestParameters['notifications'], + }), }, initOverrides); return new runtime.JSONApiResponse(response, (jsonValue) => EntityFlagsResponseFromJSON(jsonValue)); @@ -949,7 +1014,11 @@ export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { method: 'PATCH', headers: headerParameters, query: queryParameters, - body: UpdateEntityFlagsBodyToJSON(requestParameters['updateEntityFlagsBody']), + body: UpdateEntityFlagsBodyToJSON({ + updates: requestParameters['updates'], + changeDescription: requestParameters['changeDescription'], + notifications: requestParameters['notifications'], + }), }, initOverrides); return new runtime.JSONApiResponse(response, (jsonValue) => EntityFlagsResponseFromJSON(jsonValue)); diff --git a/packages/rest-api-sdk/src/index.ts b/packages/rest-api-sdk/src/index.ts index 03f94843..af774940 100644 --- a/packages/rest-api-sdk/src/index.ts +++ b/packages/rest-api-sdk/src/index.ts @@ -1,7 +1,3 @@ -export type { - AppScopedApi, - UpdateCompanyFlagsParams, - UpdateUserFlagsParams, -} from "./api"; -export { Api, createAppClient,ReflagApiError } from "./api"; +export type { AppScopedApi } from "./api"; +export { Api, createAppClient, ReflagApiError } from "./api"; export * from "./generated"; From 7d80b188757966193b5d8b222fcee0738ac836f0 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Sat, 21 Feb 2026 23:54:18 +0100 Subject: [PATCH 05/26] Update rest-api-sdk README --- packages/rest-api-sdk/README.md | 48 +++++++++++---------------------- 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/packages/rest-api-sdk/README.md b/packages/rest-api-sdk/README.md index 96034f8d..029d8f00 100644 --- a/packages/rest-api-sdk/README.md +++ b/packages/rest-api-sdk/README.md @@ -24,6 +24,22 @@ const api = new Api({ }); ``` +## API surface + +Main exports: + +- `Api`: base client +- `createAppClient(appId, config)`: app-scoped client +- `ReflagApiError`: normalized API error type +- Generated request/response types and models from `@reflag/rest-api-sdk` + +Core method groups: + +- Applications: `listApps`, `getApp` +- Environments: `listEnvironments`, `getEnvironment` +- Flags: `listFlags`, `getFlagTargeting` +- User/company evaluation: `getUserFlags`, `updateUserFlags`, `getCompanyFlags`, `updateCompanyFlags` + ## Quick start ```typescript @@ -110,22 +126,6 @@ await api.updateCompanyFlags({ }); ``` -### Bulk update specific targets for multiple flags - -```typescript -await api.updateBulkFlagSpecificTargets({ - appId: "app-123", - envId: "env-456", - updates: [ - { flagKey: "new-checkout", value: true, companyId: "company-1" }, - { flagKey: "new-checkout", value: true, userId: "user-1" }, - { flagKey: "legacy-checkout", value: null, userId: "user-1" }, - ], - notifications: true, - changeDescription: "Rolling out new checkout to pilot accounts", -}); -``` - ## Error handling The SDK throws `ReflagApiError` for non-2xx API responses. @@ -143,22 +143,6 @@ try { } ``` -## API surface - -Main exports: - -- `Api`: base client -- `createAppClient(appId, config)`: app-scoped client -- `ReflagApiError`: normalized API error type -- Generated request/response types and models from `@reflag/rest-api-sdk` - -Core method groups: - -- Applications: `listApps`, `getApp` -- Environments: `listEnvironments`, `getEnvironment` -- Flags: `listFlags`, `getFlagTargeting`, `updateBulkFlagSpecificTargets` -- User/company evaluation: `getUserFlags`, `updateUserFlags`, `getCompanyFlags`, `updateCompanyFlags` - ## Example app See `packages/rest-api-sdk/examples/customer-admin-panel/README.md` for a small Next.js app using this SDK in server actions. From 61e6cbf82393d9a0dd70003cdfe1bab3bb4cf416 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Fri, 27 Feb 2026 14:45:07 +0100 Subject: [PATCH 06/26] Flatten all SDK request body params and regenerate from production spec - Update Mustache template to always flatten body params using dynamic {{#vars}} iteration instead of hardcoded field names - Remove x-sdk-flatten-body-fields vendor extension dependency - Point inputSpec to https://app.reflag.com/openapi.json - Fix generate script to use npx @openapitools/openapi-generator-cli - Remove deprecated updateBulkFlagSpecificTargets endpoint - Add createFlag/updateFlag support with flattened params - Update README examples to match flattened API surface --- packages/rest-api-sdk/README.md | 25 +- .../openapi-generator.config.yaml | 2 +- .../openapi-templates/apis.mustache | 39 +- packages/rest-api-sdk/openapi.json | 1 - packages/rest-api-sdk/package.json | 2 +- .../src/generated/.openapi-generator/FILES | 9 +- .../src/generated/apis/DefaultApi.ts | 386 ++++++++++-------- .../rest-api-sdk/src/generated/models/App.ts | 2 +- .../src/generated/models/AppHeader.ts | 18 +- .../generated/models/AppHeaderCollection.ts | 2 +- .../BulkUpdateFlagSpecificTargetsSchema.ts | 90 ---- .../generated/models/CreateFlag200Response.ts | 74 ++++ .../models/CreateFlag200ResponseFlag.ts | 190 +++++++++ .../src/generated/models/CreateFlagRequest.ts | 115 ++++++ .../src/generated/models/EntityFlag.ts | 19 +- .../src/generated/models/EntityFlagUpdate.ts | 16 +- .../generated/models/EntityFlagsResponse.ts | 2 +- .../src/generated/models/Environment.ts | 2 +- .../src/generated/models/EnvironmentHeader.ts | 2 +- .../models/EnvironmentHeaderCollection.ts | 2 +- .../models/EnvironmentHeaderSortByColumn.ts | 2 +- .../generated/models/EnvironmentSdkAccess.ts | 2 +- .../src/generated/models/ErrorResponse.ts | 2 +- .../generated/models/ErrorResponseError.ts | 2 +- .../src/generated/models/FlagHeader.ts | 2 +- .../generated/models/FlagHeaderCollection.ts | 2 +- .../src/generated/models/FlagKeyFormat.ts | 2 +- .../src/generated/models/FlagTargeting.ts | 2 +- .../models/FlagTargetingCollection.ts | 74 ---- .../src/generated/models/FlagValue.ts | 2 +- .../generated/models/FlagValueTargeting.ts | 2 +- .../src/generated/models/OrgHeader.ts | 2 +- .../src/generated/models/ReflagUserHeader.ts | 2 +- .../src/generated/models/SegmentHeader.ts | 2 +- .../src/generated/models/SegmentType.ts | 2 +- .../src/generated/models/SortOrder.ts | 2 +- .../src/generated/models/StageHeader.ts | 11 +- .../generated/models/UpdateEntityFlagsBody.ts | 2 +- .../src/generated/models/UpdateFlagRequest.ts | 113 +++++ .../models/UpdateFlagSpecificTargets.ts | 99 ----- .../models/UpdateFlagSpecificTargetsValue.ts | 46 --- .../src/generated/models/index.ts | 8 +- .../rest-api-sdk/src/generated/runtime.ts | 2 +- reflag-openapi.json | 2 +- 44 files changed, 821 insertions(+), 564 deletions(-) delete mode 100644 packages/rest-api-sdk/src/generated/models/BulkUpdateFlagSpecificTargetsSchema.ts create mode 100644 packages/rest-api-sdk/src/generated/models/CreateFlag200Response.ts create mode 100644 packages/rest-api-sdk/src/generated/models/CreateFlag200ResponseFlag.ts create mode 100644 packages/rest-api-sdk/src/generated/models/CreateFlagRequest.ts delete mode 100644 packages/rest-api-sdk/src/generated/models/FlagTargetingCollection.ts create mode 100644 packages/rest-api-sdk/src/generated/models/UpdateFlagRequest.ts delete mode 100644 packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargets.ts delete mode 100644 packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargetsValue.ts diff --git a/packages/rest-api-sdk/README.md b/packages/rest-api-sdk/README.md index 029d8f00..8a6815a7 100644 --- a/packages/rest-api-sdk/README.md +++ b/packages/rest-api-sdk/README.md @@ -37,7 +37,7 @@ Core method groups: - Applications: `listApps`, `getApp` - Environments: `listEnvironments`, `getEnvironment` -- Flags: `listFlags`, `getFlagTargeting` +- Flags: `listFlags`, `createFlag`, `updateFlag`, `getFlagTargeting` - User/company evaluation: `getUserFlags`, `updateUserFlags`, `getCompanyFlags`, `updateCompanyFlags` ## Quick start @@ -81,6 +81,29 @@ const flags = await appApi.listFlags({}); ## Common workflows +### Create and update a flag + +`createFlag` and `updateFlag` return `{ flag }` with the latest flag details. + +Use `null` to clear nullable fields like `description` or `ownerUserId` on update. + +```typescript +const created = await api.createFlag({ + appId: "app-123", + key: "new-checkout", + name: "New checkout", + description: "Rollout for redesigned checkout flow", + secret: false, +}); + +await api.updateFlag({ + appId: "app-123", + flagId: created.flag.id, + name: "New checkout experience", + ownerUserId: null, +}); +``` + ### Read user flags for an environment `getUserFlags` evaluates flag results for one user in one environment and returns diff --git a/packages/rest-api-sdk/openapi-generator.config.yaml b/packages/rest-api-sdk/openapi-generator.config.yaml index 7d269197..d0adc698 100644 --- a/packages/rest-api-sdk/openapi-generator.config.yaml +++ b/packages/rest-api-sdk/openapi-generator.config.yaml @@ -1,5 +1,5 @@ generatorName: typescript-fetch -inputSpec: ./openapi.json +inputSpec: https://app.reflag.com/openapi.json outputDir: src/generated templateDir: ./openapi-templates additionalProperties: diff --git a/packages/rest-api-sdk/openapi-templates/apis.mustache b/packages/rest-api-sdk/openapi-templates/apis.mustache index 09a30896..1c1c7a24 100644 --- a/packages/rest-api-sdk/openapi-templates/apis.mustache +++ b/packages/rest-api-sdk/openapi-templates/apis.mustache @@ -26,24 +26,9 @@ import { export interface {{#prefixParameterInterfaces}}{{classname}}{{/prefixParameterInterfaces}}{{operationIdCamelCase}}Request { {{#allParams}} {{#isBodyParam}} - /** - * List of flag updates to apply - * @type {{=<% %>=}}<%&dataType%>['updates']<%={{ }}=%> - * @memberof {{{dataType}}} - */ - updates: {{=<% %>=}}<%&dataType%>['updates']<%={{ }}=%>; - /** - * Description of the change for audit history - * @type {string} - * @memberof {{{dataType}}} - */ - changeDescription?: {{=<% %>=}}<%&dataType%>['changeDescription']<%={{ }}=%>; - /** - * Whether to send notifications about the change (default: true) - * @type {boolean} - * @memberof {{{dataType}}} - */ - notifications?: {{=<% %>=}}<%&dataType%>['notifications']<%={{ }}=%>; + {{#vars}} + {{name}}{{^required}}?{{/required}}: {{{dataType}}}{{#isNullable}} | null{{/isNullable}}; + {{/vars}} {{/isBodyParam}} {{^isBodyParam}} {{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{#hasReadOnly}}Omit<{{{dataType}}}, {{#readOnlyVars}}'{{baseName}}'{{^-last}}|{{/-last}}{{/readOnlyVars}}>{{/hasReadOnly}}{{^hasReadOnly}}{{{dataType}}}{{/hasReadOnly}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}; @@ -71,9 +56,9 @@ export interface {{classname}}Interface { {{/summary}} {{#allParams}} {{#isBodyParam}} - * @param {{=<% %>=}}{<%&dataType%>['updates']}<%={{ }}=%> updates List of flag updates to apply - * @param {{=<% %>=}}{<%&dataType%>['changeDescription']}<%={{ }}=%> [changeDescription] Description of the change for audit history - * @param {{=<% %>=}}{<%&dataType%>['notifications']}<%={{ }}=%> [notifications] Whether to send notifications about the change (default: true) + {{#vars}} + * @param {{=<% %>=}}{<%&dataType%>}<%={{ }}=%> {{^required}}[{{/required}}{{name}}{{^required}}]{{/required}} {{description}} + {{/vars}} {{/isBodyParam}} {{^isBodyParam}} * @param {{=<% %>=}}{<%&dataType%>}<%={{ }}=%> {{^required}}[{{/required}}{{paramName}}{{^required}}]{{/required}} {{description}} @@ -342,16 +327,16 @@ export class {{classname}} extends runtime.BaseAPI { {{#bodyParam}} {{^withoutRuntimeChecks}} body: {{dataType}}ToJSON({ - updates: requestParameters['updates'], - changeDescription: requestParameters['changeDescription'], - notifications: requestParameters['notifications'], + {{#vars}} + {{name}}: requestParameters['{{name}}'], + {{/vars}} }), {{/withoutRuntimeChecks}} {{#withoutRuntimeChecks}} body: { - updates: requestParameters['updates'], - changeDescription: requestParameters['changeDescription'], - notifications: requestParameters['notifications'], + {{#vars}} + {{name}}: requestParameters['{{name}}'], + {{/vars}} }, {{/withoutRuntimeChecks}} {{/bodyParam}} diff --git a/packages/rest-api-sdk/openapi.json b/packages/rest-api-sdk/openapi.json index 25f8eff9..e69de29b 100644 --- a/packages/rest-api-sdk/openapi.json +++ b/packages/rest-api-sdk/openapi.json @@ -1 +0,0 @@ -{"openapi":"3.1.0","info":{"title":"Reflag API","version":"2.0.0","description":"Feature flag management API","contact":{"name":"Reflag Support","url":"https://reflag.com/support"},"license":{"name":"Proprietary","url":"https://reflag.com/"}},"servers":[{"url":"https://app.reflag.com/api","description":"Production server"}],"security":[{"APIKey":[]}],"paths":{"/apps/{appId}":{"get":{"summary":"Get details of an application","description":"Retrieve a specific application by its identifier","operationId":"getApp","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps":{"get":{"summary":"List of applications","description":"Retrieve all accessible applications","operationId":"listApps","parameters":[{"in":"query","name":"orgId","schema":{"$ref":"#/components/schemas/orgId"}}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/appHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/environments":{"get":{"summary":"List environments for application","description":"Retrieve all environments for a specific application","operationId":"listEnvironments","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"query","name":"sortOrder","schema":{"$ref":"#/components/schemas/sortOrder"},"description":"Sort order applied to the sorting column"},{"in":"query","name":"sortBy","schema":{"$ref":"#/components/schemas/environmentHeaderSortByColumn"},"description":"The column to sort by"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/environmentHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/environments/{envId}":{"get":{"summary":"Get environment details","description":"Retrieve details for a specific environment","operationId":"getEnvironment","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/environment"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags":{"get":{"summary":"List flags for application","description":"Retrieve all flags for a specific application","operationId":"listFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/{flagKey}/targeting/{envId}":{"get":{"summary":"Get flag targeting for an environment","description":"Retrieve targeting for a flag in an environment","operationId":"getFlagTargeting","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"flagKey","schema":{"$ref":"#/components/schemas/flagKey"},"required":true,"description":"Unique flag key"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagTargeting"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/specific-targets/{envId}":{"patch":{"summary":"Update flag specific targets for an environment","description":"Update specific companies and users for flags in an environment","operationId":"updateBulkFlagSpecificTargets","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/bulkUpdateFlagSpecificTargetsSchema"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagTargetingCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/companies/{companyId}/flags":{"get":{"summary":"Get flags for a company","description":"Retrieve all flags with their targeting status for a specific company","operationId":"getCompanyFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"companyId","schema":{"$ref":"#/components/schemas/companyId"},"required":true,"description":"Company ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Update flag targeting for a company","description":"Update specific targeting for flags for a company in an environment","operationId":"updateCompanyFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"companyId","schema":{"$ref":"#/components/schemas/companyId"},"required":true,"description":"Company ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/updateEntityFlagsBody"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/users/{userId}/flags":{"get":{"summary":"Get flags for a user","description":"Retrieve all flags with their targeting status for a specific user","operationId":"getUserFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"userId","schema":{"$ref":"#/components/schemas/userId"},"required":true,"description":"User ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Update flag targeting for a user","description":"Update specific targeting for flags for a user in an environment","operationId":"updateUserFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"userId","schema":{"$ref":"#/components/schemas/userId"},"required":true,"description":"User ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/updateEntityFlagsBody"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"appId":{"description":"App identifier","type":"string","minLength":1},"orgId":{"description":"Organization identifier","type":"string","minLength":1},"sortOrder":{"description":"Sort order applied to the sorting column","default":"asc","type":"string","enum":["asc","desc"]},"environmentHeaderSortByColumn":{"description":"The column to sort by","default":"order","type":"string","enum":["name","order"]},"envId":{"description":"Environment identifier","type":"string","minLength":1},"flagKey":{"description":"Unique flag key","type":"string","minLength":1},"bulkUpdateFlagSpecificTargetsSchema":{"description":"Update the explicit value of multiple flags for a given audience","type":"object","properties":{"updates":{"description":"The list of updates to make to the flags' targeting","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/updateFlagSpecificTargets"}},"notifications":{"description":"Whether to send notifications about the change to configured integration (eg. Slack, Linear, etc). Defaults to true.","default":true,"type":"boolean"},"changeDescription":{"description":"The description of the change recorded in the change history","type":"string"}},"required":["updates"]},"updateFlagSpecificTargets":{"description":"Update the explicit value of the flag for a given audience (null means to remove the flag from the audience).","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"value":{"description":"The value of the flag for the given audience (null means to remove the flag from the audience).","anyOf":[{"type":"string","const":"true"},{"type":"boolean","const":true},{"type":"null"}]},"companyId":{"$ref":"#/components/schemas/companyId"},"userId":{"$ref":"#/components/schemas/userId"}},"required":["flagKey","value"]},"companyId":{"description":"Company ID within your application","type":"string","minLength":1},"userId":{"description":"User ID within your application","type":"string","minLength":1},"updateEntityFlagsBody":{"description":"Request body for updating flags for an entity","type":"object","properties":{"updates":{"description":"List of flag updates to apply","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/entityFlagUpdate"}},"changeDescription":{"description":"Description of the change for audit history","type":"string"},"notifications":{"description":"Whether to send notifications about the change (default: true)","default":true,"type":"boolean"}},"required":["updates"]},"entityFlagUpdate":{"description":"Update for a single flag's specific targeting","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"value":{"description":"Set to true to add specific targeting, or null to remove it","anyOf":[{"type":"boolean","const":true},{"type":"null"}]}},"required":["flagKey","value"]},"app":{"description":"App information with related collections","type":"object","properties":{"org":{"$ref":"#/components/schemas/orgHeader"},"id":{"$ref":"#/components/schemas/appId"},"name":{"description":"App name","type":"string"},"demo":{"description":"Whether the app is a demo app","type":"boolean"},"flagKeyFormat":{"$ref":"#/components/schemas/flagKeyFormat"},"environments":{"description":"Environments within the app","type":"array","items":{"$ref":"#/components/schemas/environment"}},"stages":{"description":"Stages within the app","type":"array","items":{"$ref":"#/components/schemas/stageHeader"}},"segments":{"description":"Segments within the app","type":"array","items":{"$ref":"#/components/schemas/segmentHeader"}}},"required":["org","id","name","demo","flagKeyFormat","environments","stages","segments"],"additionalProperties":false},"orgHeader":{"description":"Organization's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/orgId"},"name":{"description":"Organization name","type":"string","minLength":1}},"required":["id","name"],"additionalProperties":false},"flagKeyFormat":{"description":"The enforced key format when creating flags","type":"string","enum":["custom","pascalCase","camelCase","snakeCaseUpper","snakeCaseLower","kebabCaseUpper","kebabCaseLower"]},"environment":{"description":"Environment details","type":"object","properties":{"id":{"$ref":"#/components/schemas/envId"},"name":{"description":"Environment name","type":"string"},"isProduction":{"description":"Whether the environment is a production environment","type":"boolean"},"order":{"description":"Environment order in the app (zero-indexed)","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sdkAccess":{"description":"SDK access details","type":"object","properties":{"publishableKey":{"description":"Publishable key","type":"string","minLength":1,"maxLength":36},"secretKey":{"description":"Secret key","type":"string","minLength":1,"maxLength":36}},"required":["publishableKey","secretKey"],"additionalProperties":false}},"required":["id","name","isProduction","order","sdkAccess"],"additionalProperties":false},"stageHeader":{"description":"Stage's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/stageId"},"name":{"description":"Stage name","type":"string","minLength":1},"color":{"description":"Stage color (HTML color name or hex code)","type":"string","minLength":1,"maxLength":64},"assignedFlagCount":{"description":"Flags currently in this stage","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"order":{"description":"Stage order","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name","color","assignedFlagCount","order"],"additionalProperties":false},"stageId":{"description":"Stage identifier","type":"string","minLength":1},"segmentHeader":{"description":"Segment's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/segmentId"},"name":{"description":"Segment name","type":"string","minLength":1},"type":{"$ref":"#/components/schemas/segmentType"}},"required":["id","name","type"],"additionalProperties":false},"segmentId":{"description":"Segment identifier","type":"string","minLength":1},"segmentType":{"description":"Segment type","type":"string","enum":["all","custom"]},"ErrorResponse":{"description":"The error response, including individual issues, if applicable","type":"object","properties":{"error":{"description":"The error","type":"object","properties":{"code":{"description":"Error code","type":"string","enum":["invalid_request","not_found","not_possible","not_allowed","not_available","unknown_error","unauthorized","unauthenticated"]},"message":{"description":"Human readable error message","type":"string"}},"required":["code","message"],"additionalProperties":false},"issues":{"description":"Individual validation issues, if applicable","type":"object","propertyNames":{"description":"The field that has the issue (uses dot notation). Empty string if the issue is at the root.","type":"string"},"additionalProperties":{"description":"Error messages for this field","type":"array","items":{"description":"The error message","type":"string"}}}},"required":["error"],"additionalProperties":false},"appHeaderCollection":{"description":"Collection of Basic app information","type":"object","properties":{"data":{"description":"The individual items in the collection","type":"array","items":{"$ref":"#/components/schemas/appHeader"}}},"required":["data"],"additionalProperties":false},"appHeader":{"description":"Basic app information","type":"object","properties":{"org":{"$ref":"#/components/schemas/orgHeader"},"id":{"$ref":"#/components/schemas/appId"},"name":{"description":"App name","type":"string"},"demo":{"description":"Whether the app is a demo app","type":"boolean"},"flagKeyFormat":{"$ref":"#/components/schemas/flagKeyFormat"}},"required":["org","id","name","demo","flagKeyFormat"],"additionalProperties":false},"environmentHeaderCollection":{"description":"Collection of Basic environment information","type":"object","properties":{"data":{"description":"The individual items in the collection","type":"array","items":{"$ref":"#/components/schemas/environmentHeader"}},"sortOrder":{"$ref":"#/components/schemas/sortOrder"},"sortBy":{"$ref":"#/components/schemas/environmentHeaderSortByColumn"}},"required":["data","sortOrder","sortBy"],"additionalProperties":false},"environmentHeader":{"description":"Basic environment information","type":"object","properties":{"id":{"$ref":"#/components/schemas/envId"},"name":{"description":"Environment name","type":"string"},"isProduction":{"description":"Whether the environment is a production environment","type":"boolean"},"order":{"description":"Environment order in the app (zero-indexed)","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name","isProduction","order"],"additionalProperties":false},"flagHeaderCollection":{"description":"Collection response containing flags","type":"object","properties":{"data":{"description":"Page of the collection of flags","type":"array","items":{"$ref":"#/components/schemas/flagHeader"}},"totalCount":{"description":"Total number of flags in collection","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageSize":{"description":"Page size","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageIndex":{"description":"Page index","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sortBy":{"description":"Sort by","type":"string","enum":["name","key","stage","autoFeedbackSurveysEnabled","createdAt","environmentStatus","owner","lastCheck","lastTrack","stale","archivingChecks"]},"sortOrder":{"description":"Sort order","$ref":"#/components/schemas/sortOrder"}},"required":["data","totalCount","pageSize","pageIndex","sortBy","sortOrder"],"additionalProperties":false},"flagHeader":{"description":"Basic flag information","type":"object","properties":{"id":{"description":"Flag ID","type":"string","minLength":1},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"description":{"description":"Flag description","type":"string"},"stage":{"$ref":"#/components/schemas/stageHeader"},"owner":{"$ref":"#/components/schemas/reflagUserHeader"},"archived":{"description":"Whether the flag is archived","type":"boolean"},"stale":{"description":"Whether the flag is stale","type":"boolean"},"permanent":{"description":"Whether the flag is permanent","type":"boolean"},"createdAt":{"description":"Timestamp when the flag was created","type":"string"},"lastCheckAt":{"description":"Timestamp when the flag was last checked","type":"string"},"lastTrackAt":{"description":"Timestamp when the flag was last tracked","type":"string"}},"required":["id","key","name","archived","stale","permanent"],"additionalProperties":false},"reflagUserHeader":{"description":"Reflag user's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/reflagUserId"},"name":{"description":"User's name","type":"string","minLength":1},"email":{"description":"User's email","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"avatarUrl":{"description":"User's avatar URL","type":"string","format":"uri"}},"required":["id","name","email"],"additionalProperties":false},"reflagUserId":{"description":"Reflag user identifier","type":"string","minLength":1},"flagTargeting":{"description":"Flag targeting information and its audience","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"version":{"$ref":"#/components/schemas/flagVersion"},"updatedAt":{"description":"Last time the targeting was updated","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"specificTargets":{"description":"The flag targeting for each value","type":"object","propertyNames":{"$ref":"#/components/schemas/flagValue"},"additionalProperties":{"$ref":"#/components/schemas/flagValueTargeting"}}},"required":["flagKey","version","updatedAt","specificTargets"],"additionalProperties":false},"flagVersion":{"description":"Flag targeting version","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"flagValue":{"description":"The value of the flag served to the audience.","type":"string","const":"true"},"flagValueTargeting":{"description":"Flag targeting value and its audience","type":"object","properties":{"companyIds":{"description":"Companies that were explicitly given the value","type":"array","items":{"$ref":"#/components/schemas/companyId"}},"userIds":{"description":"Users that were explicitly given the value","type":"array","items":{"$ref":"#/components/schemas/userId"}}},"required":["companyIds","userIds"],"additionalProperties":false},"flagTargetingCollection":{"description":"Collection response containing flags' targeting information","type":"object","properties":{"data":{"description":"Collection of flags' targeting information","type":"array","items":{"$ref":"#/components/schemas/flagTargeting"}}},"required":["data"],"additionalProperties":false},"entityFlagsResponse":{"description":"Response containing flags for an entity","type":"object","properties":{"data":{"description":"List of flags with their enabled status","type":"array","items":{"$ref":"#/components/schemas/entityFlag"}},"totalCount":{"description":"Total number of flags","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageSize":{"description":"Page size","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageIndex":{"description":"Page index","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["data","totalCount","pageSize","pageIndex"],"additionalProperties":false},"entityFlag":{"description":"Flag information with enabled status for an entity","type":"object","properties":{"id":{"description":"Flag ID","type":"string","minLength":1},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"value":{"description":"Whether the flag is enabled for this entity","type":"boolean"},"specificallyTargetedValue":{"description":"Value if directly added via specific targets, null if not specifically targeted","anyOf":[{"type":"boolean"},{"type":"null"}]},"firstExposureAt":{"description":"First time the entity was exposed to this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastExposureAt":{"description":"Last time the entity was exposed to this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastCheckAt":{"description":"Last time the flag was checked for this entity","anyOf":[{"type":"string"},{"type":"null"}]},"exposureCount":{"description":"Number of times the entity was exposed to this flag","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"firstTrackAt":{"description":"First time a track event was recorded for this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastTrackAt":{"description":"Last time a track event was recorded for this flag","anyOf":[{"type":"string"},{"type":"null"}]},"trackCount":{"description":"Number of track events for this flag","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","key","name","value","specificallyTargetedValue","firstExposureAt","lastExposureAt","lastCheckAt","exposureCount","firstTrackAt","lastTrackAt","trackCount"],"additionalProperties":false}},"securitySchemes":{"APIKey":{"type":"http","scheme":"bearer","description":"API key authentication, for service access"}}}} \ No newline at end of file diff --git a/packages/rest-api-sdk/package.json b/packages/rest-api-sdk/package.json index d2546682..0e46cc96 100644 --- a/packages/rest-api-sdk/package.json +++ b/packages/rest-api-sdk/package.json @@ -8,7 +8,7 @@ "url": "https://github.com/reflagcom/javascript.git" }, "scripts": { - "generate": "rm -rf src/generated && openapi-generator-cli generate -c openapi-generator.config.yaml", + "generate": "rm -rf src/generated && npx @openapitools/openapi-generator-cli generate -c openapi-generator.config.yaml", "build": "yarn generate && tsc --project tsconfig.build.json", "test": "vitest run", "test:watch": "vitest", diff --git a/packages/rest-api-sdk/src/generated/.openapi-generator/FILES b/packages/rest-api-sdk/src/generated/.openapi-generator/FILES index 6e7ea149..f8105a66 100644 --- a/packages/rest-api-sdk/src/generated/.openapi-generator/FILES +++ b/packages/rest-api-sdk/src/generated/.openapi-generator/FILES @@ -1,10 +1,13 @@ +.openapi-generator-ignore apis/DefaultApi.ts apis/index.ts index.ts models/App.ts models/AppHeader.ts models/AppHeaderCollection.ts -models/BulkUpdateFlagSpecificTargetsSchema.ts +models/CreateFlag200Response.ts +models/CreateFlag200ResponseFlag.ts +models/CreateFlagRequest.ts models/EntityFlag.ts models/EntityFlagUpdate.ts models/EntityFlagsResponse.ts @@ -19,7 +22,6 @@ models/FlagHeader.ts models/FlagHeaderCollection.ts models/FlagKeyFormat.ts models/FlagTargeting.ts -models/FlagTargetingCollection.ts models/FlagValue.ts models/FlagValueTargeting.ts models/OrgHeader.ts @@ -29,7 +31,6 @@ models/SegmentType.ts models/SortOrder.ts models/StageHeader.ts models/UpdateEntityFlagsBody.ts -models/UpdateFlagSpecificTargets.ts -models/UpdateFlagSpecificTargetsValue.ts +models/UpdateFlagRequest.ts models/index.ts runtime.ts diff --git a/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts b/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts index dae57829..1e5b49e1 100644 --- a/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts +++ b/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -17,7 +17,8 @@ import * as runtime from '../runtime'; import type { App, AppHeaderCollection, - BulkUpdateFlagSpecificTargetsSchema, + CreateFlag200Response, + CreateFlagRequest, EntityFlagsResponse, Environment, EnvironmentHeaderCollection, @@ -25,17 +26,19 @@ import type { ErrorResponse, FlagHeaderCollection, FlagTargeting, - FlagTargetingCollection, SortOrder, UpdateEntityFlagsBody, + UpdateFlagRequest, } from '../models/index'; import { AppFromJSON, AppToJSON, AppHeaderCollectionFromJSON, AppHeaderCollectionToJSON, - BulkUpdateFlagSpecificTargetsSchemaFromJSON, - BulkUpdateFlagSpecificTargetsSchemaToJSON, + CreateFlag200ResponseFromJSON, + CreateFlag200ResponseToJSON, + CreateFlagRequestFromJSON, + CreateFlagRequestToJSON, EntityFlagsResponseFromJSON, EntityFlagsResponseToJSON, EnvironmentFromJSON, @@ -50,22 +53,33 @@ import { FlagHeaderCollectionToJSON, FlagTargetingFromJSON, FlagTargetingToJSON, - FlagTargetingCollectionFromJSON, - FlagTargetingCollectionToJSON, SortOrderFromJSON, SortOrderToJSON, UpdateEntityFlagsBodyFromJSON, UpdateEntityFlagsBodyToJSON, + UpdateFlagRequestFromJSON, + UpdateFlagRequestToJSON, } from '../models/index'; +export interface CreateFlagOperationRequest { + appId: string; + key: string; + name: string; + description?: string | null; + stageId?: string; + ownerUserId?: string | null; + permanent?: boolean; + secret?: boolean; +} + export interface GetAppRequest { appId: string; } export interface GetCompanyFlagsRequest { appId: string; - companyId: string; envId: string; + companyId: string; } export interface GetEnvironmentRequest { @@ -81,8 +95,8 @@ export interface GetFlagTargetingRequest { export interface GetUserFlagsRequest { appId: string; - userId: string; envId: string; + userId: string; } export interface ListAppsRequest { @@ -99,75 +113,34 @@ export interface ListFlagsRequest { appId: string; } -export interface UpdateBulkFlagSpecificTargetsRequest { +export interface UpdateCompanyFlagsRequest { appId: string; envId: string; - /** - * List of flag updates to apply - * @type BulkUpdateFlagSpecificTargetsSchema['updates'] - * @memberof BulkUpdateFlagSpecificTargetsSchema - */ - updates: BulkUpdateFlagSpecificTargetsSchema['updates']; - /** - * Description of the change for audit history - * @type {string} - * @memberof BulkUpdateFlagSpecificTargetsSchema - */ - changeDescription?: BulkUpdateFlagSpecificTargetsSchema['changeDescription']; - /** - * Whether to send notifications about the change (default: true) - * @type {boolean} - * @memberof BulkUpdateFlagSpecificTargetsSchema - */ - notifications?: BulkUpdateFlagSpecificTargetsSchema['notifications']; + companyId: string; + updates: Array; + changeDescription?: string; + notifications?: boolean; } -export interface UpdateCompanyFlagsRequest { +export interface UpdateFlagOperationRequest { appId: string; - companyId: string; - envId: string; - /** - * List of flag updates to apply - * @type UpdateEntityFlagsBody['updates'] - * @memberof UpdateEntityFlagsBody - */ - updates: UpdateEntityFlagsBody['updates']; - /** - * Description of the change for audit history - * @type {string} - * @memberof UpdateEntityFlagsBody - */ - changeDescription?: UpdateEntityFlagsBody['changeDescription']; - /** - * Whether to send notifications about the change (default: true) - * @type {boolean} - * @memberof UpdateEntityFlagsBody - */ - notifications?: UpdateEntityFlagsBody['notifications']; + flagId: string; + name?: string; + description?: string | null; + ownerUserId?: string | null; + permanent?: boolean; + secret?: boolean; + isArchived?: boolean; + stageId?: string; } export interface UpdateUserFlagsRequest { appId: string; - userId: string; envId: string; - /** - * List of flag updates to apply - * @type UpdateEntityFlagsBody['updates'] - * @memberof UpdateEntityFlagsBody - */ - updates: UpdateEntityFlagsBody['updates']; - /** - * Description of the change for audit history - * @type {string} - * @memberof UpdateEntityFlagsBody - */ - changeDescription?: UpdateEntityFlagsBody['changeDescription']; - /** - * Whether to send notifications about the change (default: true) - * @type {boolean} - * @memberof UpdateEntityFlagsBody - */ - notifications?: UpdateEntityFlagsBody['notifications']; + userId: string; + updates: Array; + changeDescription?: string; + notifications?: boolean; } /** @@ -177,6 +150,29 @@ export interface UpdateUserFlagsRequest { * @interface DefaultApiInterface */ export interface DefaultApiInterface { + /** + * Create a new flag in the application. Returns the created flag details. + * @summary Create a flag + * @param {string} appId App identifier + * @param {string} key Key of the flag + * @param {string} name Name of the flag + * @param {string} [description] + * @param {string} [stageId] Stage ID of the flag + * @param {string} [ownerUserId] + * @param {boolean} [permanent] + * @param {boolean} [secret] Whether the flag is secret + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApiInterface + */ + createFlagRaw(requestParameters: CreateFlagOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + + /** + * Create a new flag in the application. Returns the created flag details. + * Create a flag + */ + createFlag(requestParameters: CreateFlagOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + /** * Retrieve a specific application by its identifier * @summary Get details of an application @@ -197,8 +193,8 @@ export interface DefaultApiInterface { * Retrieve all flags with their targeting status for a specific company * @summary Get flags for a company * @param {string} appId App identifier + * @param {string} envId Environment identifier * @param {string} companyId Company ID within your application - * @param {string} envId Environment ID to evaluate targeting for * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DefaultApiInterface @@ -250,8 +246,8 @@ export interface DefaultApiInterface { * Retrieve all flags with their targeting status for a specific user * @summary Get flags for a user * @param {string} appId App identifier + * @param {string} envId Environment identifier * @param {string} userId User ID within your application - * @param {string} envId Environment ID to evaluate targeting for * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DefaultApiInterface @@ -315,55 +311,59 @@ export interface DefaultApiInterface { listFlags(requestParameters: ListFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; /** - * Update specific companies and users for flags in an environment - * @summary Update flag specific targets for an environment + * Update specific targeting for flags for a company in an environment + * @summary Update flag targeting for a company * @param {string} appId App identifier * @param {string} envId Environment identifier - * @param {BulkUpdateFlagSpecificTargetsSchema['updates']} updates List of flag updates to apply - * @param {BulkUpdateFlagSpecificTargetsSchema['changeDescription']} [changeDescription] Description of the change for audit history - * @param {BulkUpdateFlagSpecificTargetsSchema['notifications']} [notifications] Whether to send notifications about the change (default: true) + * @param {string} companyId Company ID within your application + * @param {Array} updates List of flag updates to apply + * @param {string} [changeDescription] Description of the change for audit history + * @param {boolean} [notifications] Whether to send notifications about the change (default: true) * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DefaultApiInterface */ - updateBulkFlagSpecificTargetsRaw(requestParameters: UpdateBulkFlagSpecificTargetsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + updateCompanyFlagsRaw(requestParameters: UpdateCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; /** - * Update specific companies and users for flags in an environment - * Update flag specific targets for an environment + * Update specific targeting for flags for a company in an environment + * Update flag targeting for a company */ - updateBulkFlagSpecificTargets(requestParameters: UpdateBulkFlagSpecificTargetsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + updateCompanyFlags(requestParameters: UpdateCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; /** - * Update specific targeting for flags for a company in an environment - * @summary Update flag targeting for a company + * Update an existing flag + * @summary Update a flag * @param {string} appId App identifier - * @param {string} companyId Company ID within your application - * @param {string} envId Environment ID to evaluate targeting for - * @param {UpdateEntityFlagsBody['updates']} updates List of flag updates to apply - * @param {UpdateEntityFlagsBody['changeDescription']} [changeDescription] Description of the change for audit history - * @param {UpdateEntityFlagsBody['notifications']} [notifications] Whether to send notifications about the change (default: true) + * @param {string} flagId Flag ID + * @param {string} [name] Name of the flag + * @param {string} [description] + * @param {string} [ownerUserId] + * @param {boolean} [permanent] + * @param {boolean} [secret] Whether the flag is secret + * @param {boolean} [isArchived] + * @param {string} [stageId] Stage ID of the flag * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DefaultApiInterface */ - updateCompanyFlagsRaw(requestParameters: UpdateCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; + updateFlagRaw(requestParameters: UpdateFlagOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise>; /** - * Update specific targeting for flags for a company in an environment - * Update flag targeting for a company + * Update an existing flag + * Update a flag */ - updateCompanyFlags(requestParameters: UpdateCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; + updateFlag(requestParameters: UpdateFlagOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise; /** * Update specific targeting for flags for a user in an environment * @summary Update flag targeting for a user * @param {string} appId App identifier + * @param {string} envId Environment identifier * @param {string} userId User ID within your application - * @param {string} envId Environment ID to evaluate targeting for - * @param {UpdateEntityFlagsBody['updates']} updates List of flag updates to apply - * @param {UpdateEntityFlagsBody['changeDescription']} [changeDescription] Description of the change for audit history - * @param {UpdateEntityFlagsBody['notifications']} [notifications] Whether to send notifications about the change (default: true) + * @param {Array} updates List of flag updates to apply + * @param {string} [changeDescription] Description of the change for audit history + * @param {boolean} [notifications] Whether to send notifications about the change (default: true) * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof DefaultApiInterface @@ -383,6 +383,64 @@ export interface DefaultApiInterface { */ export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { + /** + * Create a new flag in the application. Returns the created flag details. + * Create a flag + */ + async createFlagRaw(requestParameters: CreateFlagOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + if (requestParameters['appId'] == null) { + throw new runtime.RequiredError( + 'appId', + 'Required parameter "appId" was null or undefined when calling createFlag().' + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + headerParameters['Content-Type'] = 'application/json'; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token("APIKey", []); + + if (tokenString) { + headerParameters["Authorization"] = `Bearer ${tokenString}`; + } + } + + let urlPath = `/apps/{appId}/flags`; + urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + + const response = await this.request({ + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: CreateFlagRequestToJSON({ + key: requestParameters['key'], + name: requestParameters['name'], + description: requestParameters['description'], + stageId: requestParameters['stageId'], + ownerUserId: requestParameters['ownerUserId'], + permanent: requestParameters['permanent'], + secret: requestParameters['secret'], + }), + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => CreateFlag200ResponseFromJSON(jsonValue)); + } + + /** + * Create a new flag in the application. Returns the created flag details. + * Create a flag + */ + async createFlag(requestParameters: CreateFlagOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.createFlagRaw(requestParameters, initOverrides); + return await response.value(); + } + /** * Retrieve a specific application by its identifier * Get details of an application @@ -442,26 +500,22 @@ export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { ); } - if (requestParameters['companyId'] == null) { + if (requestParameters['envId'] == null) { throw new runtime.RequiredError( - 'companyId', - 'Required parameter "companyId" was null or undefined when calling getCompanyFlags().' + 'envId', + 'Required parameter "envId" was null or undefined when calling getCompanyFlags().' ); } - if (requestParameters['envId'] == null) { + if (requestParameters['companyId'] == null) { throw new runtime.RequiredError( - 'envId', - 'Required parameter "envId" was null or undefined when calling getCompanyFlags().' + 'companyId', + 'Required parameter "companyId" was null or undefined when calling getCompanyFlags().' ); } const queryParameters: any = {}; - if (requestParameters['envId'] != null) { - queryParameters['envId'] = requestParameters['envId']; - } - const headerParameters: runtime.HTTPHeaders = {}; if (this.configuration && this.configuration.accessToken) { @@ -473,8 +527,9 @@ export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { } } - let urlPath = `/apps/{appId}/companies/{companyId}/flags`; + let urlPath = `/apps/{appId}/envs/{envId}/companies/{companyId}/flags`; urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + urlPath = urlPath.replace(`{${"envId"}}`, encodeURIComponent(String(requestParameters['envId']))); urlPath = urlPath.replace(`{${"companyId"}}`, encodeURIComponent(String(requestParameters['companyId']))); const response = await this.request({ @@ -626,26 +681,22 @@ export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { ); } - if (requestParameters['userId'] == null) { + if (requestParameters['envId'] == null) { throw new runtime.RequiredError( - 'userId', - 'Required parameter "userId" was null or undefined when calling getUserFlags().' + 'envId', + 'Required parameter "envId" was null or undefined when calling getUserFlags().' ); } - if (requestParameters['envId'] == null) { + if (requestParameters['userId'] == null) { throw new runtime.RequiredError( - 'envId', - 'Required parameter "envId" was null or undefined when calling getUserFlags().' + 'userId', + 'Required parameter "userId" was null or undefined when calling getUserFlags().' ); } const queryParameters: any = {}; - if (requestParameters['envId'] != null) { - queryParameters['envId'] = requestParameters['envId']; - } - const headerParameters: runtime.HTTPHeaders = {}; if (this.configuration && this.configuration.accessToken) { @@ -657,8 +708,9 @@ export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { } } - let urlPath = `/apps/{appId}/users/{userId}/flags`; + let urlPath = `/apps/{appId}/envs/{envId}/users/{userId}/flags`; urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + urlPath = urlPath.replace(`{${"envId"}}`, encodeURIComponent(String(requestParameters['envId']))); urlPath = urlPath.replace(`{${"userId"}}`, encodeURIComponent(String(requestParameters['userId']))); const response = await this.request({ @@ -826,21 +878,28 @@ export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { } /** - * Update specific companies and users for flags in an environment - * Update flag specific targets for an environment + * Update specific targeting for flags for a company in an environment + * Update flag targeting for a company */ - async updateBulkFlagSpecificTargetsRaw(requestParameters: UpdateBulkFlagSpecificTargetsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + async updateCompanyFlagsRaw(requestParameters: UpdateCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { if (requestParameters['appId'] == null) { throw new runtime.RequiredError( 'appId', - 'Required parameter "appId" was null or undefined when calling updateBulkFlagSpecificTargets().' + 'Required parameter "appId" was null or undefined when calling updateCompanyFlags().' ); } if (requestParameters['envId'] == null) { throw new runtime.RequiredError( 'envId', - 'Required parameter "envId" was null or undefined when calling updateBulkFlagSpecificTargets().' + 'Required parameter "envId" was null or undefined when calling updateCompanyFlags().' + ); + } + + if (requestParameters['companyId'] == null) { + throw new runtime.RequiredError( + 'companyId', + 'Required parameter "companyId" was null or undefined when calling updateCompanyFlags().' ); } @@ -859,66 +918,56 @@ export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { } } - let urlPath = `/apps/{appId}/flags/specific-targets/{envId}`; + let urlPath = `/apps/{appId}/envs/{envId}/companies/{companyId}/flags`; urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); urlPath = urlPath.replace(`{${"envId"}}`, encodeURIComponent(String(requestParameters['envId']))); + urlPath = urlPath.replace(`{${"companyId"}}`, encodeURIComponent(String(requestParameters['companyId']))); const response = await this.request({ path: urlPath, method: 'PATCH', headers: headerParameters, query: queryParameters, - body: BulkUpdateFlagSpecificTargetsSchemaToJSON({ + body: UpdateEntityFlagsBodyToJSON({ updates: requestParameters['updates'], changeDescription: requestParameters['changeDescription'], notifications: requestParameters['notifications'], }), }, initOverrides); - return new runtime.JSONApiResponse(response, (jsonValue) => FlagTargetingCollectionFromJSON(jsonValue)); + return new runtime.JSONApiResponse(response, (jsonValue) => EntityFlagsResponseFromJSON(jsonValue)); } /** - * Update specific companies and users for flags in an environment - * Update flag specific targets for an environment + * Update specific targeting for flags for a company in an environment + * Update flag targeting for a company */ - async updateBulkFlagSpecificTargets(requestParameters: UpdateBulkFlagSpecificTargetsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { - const response = await this.updateBulkFlagSpecificTargetsRaw(requestParameters, initOverrides); + async updateCompanyFlags(requestParameters: UpdateCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.updateCompanyFlagsRaw(requestParameters, initOverrides); return await response.value(); } /** - * Update specific targeting for flags for a company in an environment - * Update flag targeting for a company + * Update an existing flag + * Update a flag */ - async updateCompanyFlagsRaw(requestParameters: UpdateCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + async updateFlagRaw(requestParameters: UpdateFlagOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { if (requestParameters['appId'] == null) { throw new runtime.RequiredError( 'appId', - 'Required parameter "appId" was null or undefined when calling updateCompanyFlags().' - ); - } - - if (requestParameters['companyId'] == null) { - throw new runtime.RequiredError( - 'companyId', - 'Required parameter "companyId" was null or undefined when calling updateCompanyFlags().' + 'Required parameter "appId" was null or undefined when calling updateFlag().' ); } - if (requestParameters['envId'] == null) { + if (requestParameters['flagId'] == null) { throw new runtime.RequiredError( - 'envId', - 'Required parameter "envId" was null or undefined when calling updateCompanyFlags().' + 'flagId', + 'Required parameter "flagId" was null or undefined when calling updateFlag().' ); } const queryParameters: any = {}; - if (requestParameters['envId'] != null) { - queryParameters['envId'] = requestParameters['envId']; - } - const headerParameters: runtime.HTTPHeaders = {}; headerParameters['Content-Type'] = 'application/json'; @@ -932,31 +981,35 @@ export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { } } - let urlPath = `/apps/{appId}/companies/{companyId}/flags`; + let urlPath = `/apps/{appId}/flags/{flagId}`; urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); - urlPath = urlPath.replace(`{${"companyId"}}`, encodeURIComponent(String(requestParameters['companyId']))); + urlPath = urlPath.replace(`{${"flagId"}}`, encodeURIComponent(String(requestParameters['flagId']))); const response = await this.request({ path: urlPath, method: 'PATCH', headers: headerParameters, query: queryParameters, - body: UpdateEntityFlagsBodyToJSON({ - updates: requestParameters['updates'], - changeDescription: requestParameters['changeDescription'], - notifications: requestParameters['notifications'], + body: UpdateFlagRequestToJSON({ + name: requestParameters['name'], + description: requestParameters['description'], + ownerUserId: requestParameters['ownerUserId'], + permanent: requestParameters['permanent'], + secret: requestParameters['secret'], + isArchived: requestParameters['isArchived'], + stageId: requestParameters['stageId'], }), }, initOverrides); - return new runtime.JSONApiResponse(response, (jsonValue) => EntityFlagsResponseFromJSON(jsonValue)); + return new runtime.JSONApiResponse(response, (jsonValue) => CreateFlag200ResponseFromJSON(jsonValue)); } /** - * Update specific targeting for flags for a company in an environment - * Update flag targeting for a company + * Update an existing flag + * Update a flag */ - async updateCompanyFlags(requestParameters: UpdateCompanyFlagsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { - const response = await this.updateCompanyFlagsRaw(requestParameters, initOverrides); + async updateFlag(requestParameters: UpdateFlagOperationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.updateFlagRaw(requestParameters, initOverrides); return await response.value(); } @@ -972,26 +1025,22 @@ export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { ); } - if (requestParameters['userId'] == null) { + if (requestParameters['envId'] == null) { throw new runtime.RequiredError( - 'userId', - 'Required parameter "userId" was null or undefined when calling updateUserFlags().' + 'envId', + 'Required parameter "envId" was null or undefined when calling updateUserFlags().' ); } - if (requestParameters['envId'] == null) { + if (requestParameters['userId'] == null) { throw new runtime.RequiredError( - 'envId', - 'Required parameter "envId" was null or undefined when calling updateUserFlags().' + 'userId', + 'Required parameter "userId" was null or undefined when calling updateUserFlags().' ); } const queryParameters: any = {}; - if (requestParameters['envId'] != null) { - queryParameters['envId'] = requestParameters['envId']; - } - const headerParameters: runtime.HTTPHeaders = {}; headerParameters['Content-Type'] = 'application/json'; @@ -1005,8 +1054,9 @@ export class DefaultApi extends runtime.BaseAPI implements DefaultApiInterface { } } - let urlPath = `/apps/{appId}/users/{userId}/flags`; + let urlPath = `/apps/{appId}/envs/{envId}/users/{userId}/flags`; urlPath = urlPath.replace(`{${"appId"}}`, encodeURIComponent(String(requestParameters['appId']))); + urlPath = urlPath.replace(`{${"envId"}}`, encodeURIComponent(String(requestParameters['envId']))); urlPath = urlPath.replace(`{${"userId"}}`, encodeURIComponent(String(requestParameters['userId']))); const response = await this.request({ diff --git a/packages/rest-api-sdk/src/generated/models/App.ts b/packages/rest-api-sdk/src/generated/models/App.ts index 12c57732..d928c112 100644 --- a/packages/rest-api-sdk/src/generated/models/App.ts +++ b/packages/rest-api-sdk/src/generated/models/App.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/AppHeader.ts b/packages/rest-api-sdk/src/generated/models/AppHeader.ts index 4306938c..cbff7d45 100644 --- a/packages/rest-api-sdk/src/generated/models/AppHeader.ts +++ b/packages/rest-api-sdk/src/generated/models/AppHeader.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -13,6 +13,13 @@ */ import { mapValues } from '../runtime'; +import type { EnvironmentHeader } from './EnvironmentHeader'; +import { + EnvironmentHeaderFromJSON, + EnvironmentHeaderFromJSONTyped, + EnvironmentHeaderToJSON, + EnvironmentHeaderToJSONTyped, +} from './EnvironmentHeader'; import type { FlagKeyFormat } from './FlagKeyFormat'; import { FlagKeyFormatFromJSON, @@ -64,6 +71,12 @@ export interface AppHeader { * @memberof AppHeader */ flagKeyFormat: FlagKeyFormat; + /** + * Environments within the app + * @type {Array} + * @memberof AppHeader + */ + environments: Array; } @@ -77,6 +90,7 @@ export function instanceOfAppHeader(value: object): value is AppHeader { if (!('name' in value) || value['name'] === undefined) return false; if (!('demo' in value) || value['demo'] === undefined) return false; if (!('flagKeyFormat' in value) || value['flagKeyFormat'] === undefined) return false; + if (!('environments' in value) || value['environments'] === undefined) return false; return true; } @@ -95,6 +109,7 @@ export function AppHeaderFromJSONTyped(json: any, ignoreDiscriminator: boolean): 'name': json['name'], 'demo': json['demo'], 'flagKeyFormat': FlagKeyFormatFromJSON(json['flagKeyFormat']), + 'environments': ((json['environments'] as Array).map(EnvironmentHeaderFromJSON)), }; } @@ -114,6 +129,7 @@ export function AppHeaderToJSONTyped(value?: AppHeader | null, ignoreDiscriminat 'name': value['name'], 'demo': value['demo'], 'flagKeyFormat': FlagKeyFormatToJSON(value['flagKeyFormat']), + 'environments': ((value['environments'] as Array).map(EnvironmentHeaderToJSON)), }; } diff --git a/packages/rest-api-sdk/src/generated/models/AppHeaderCollection.ts b/packages/rest-api-sdk/src/generated/models/AppHeaderCollection.ts index 77066dde..56a0b502 100644 --- a/packages/rest-api-sdk/src/generated/models/AppHeaderCollection.ts +++ b/packages/rest-api-sdk/src/generated/models/AppHeaderCollection.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/BulkUpdateFlagSpecificTargetsSchema.ts b/packages/rest-api-sdk/src/generated/models/BulkUpdateFlagSpecificTargetsSchema.ts deleted file mode 100644 index 20145a14..00000000 --- a/packages/rest-api-sdk/src/generated/models/BulkUpdateFlagSpecificTargetsSchema.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/** - * Reflag API - * Feature flag management API - * - * The version of the OpenAPI document: 2.0.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - -import { mapValues } from '../runtime'; -import type { UpdateFlagSpecificTargets } from './UpdateFlagSpecificTargets'; -import { - UpdateFlagSpecificTargetsFromJSON, - UpdateFlagSpecificTargetsFromJSONTyped, - UpdateFlagSpecificTargetsToJSON, - UpdateFlagSpecificTargetsToJSONTyped, -} from './UpdateFlagSpecificTargets'; - -/** - * Update the explicit value of multiple flags for a given audience - * @export - * @interface BulkUpdateFlagSpecificTargetsSchema - */ -export interface BulkUpdateFlagSpecificTargetsSchema { - /** - * The list of updates to make to the flags' targeting - * @type {Array} - * @memberof BulkUpdateFlagSpecificTargetsSchema - */ - updates: Array; - /** - * Whether to send notifications about the change to configured integration (eg. Slack, Linear, etc). Defaults to true. - * @type {boolean} - * @memberof BulkUpdateFlagSpecificTargetsSchema - */ - notifications?: boolean; - /** - * The description of the change recorded in the change history - * @type {string} - * @memberof BulkUpdateFlagSpecificTargetsSchema - */ - changeDescription?: string; -} - -/** - * Check if a given object implements the BulkUpdateFlagSpecificTargetsSchema interface. - */ -export function instanceOfBulkUpdateFlagSpecificTargetsSchema(value: object): value is BulkUpdateFlagSpecificTargetsSchema { - if (!('updates' in value) || value['updates'] === undefined) return false; - return true; -} - -export function BulkUpdateFlagSpecificTargetsSchemaFromJSON(json: any): BulkUpdateFlagSpecificTargetsSchema { - return BulkUpdateFlagSpecificTargetsSchemaFromJSONTyped(json, false); -} - -export function BulkUpdateFlagSpecificTargetsSchemaFromJSONTyped(json: any, ignoreDiscriminator: boolean): BulkUpdateFlagSpecificTargetsSchema { - if (json == null) { - return json; - } - return { - - 'updates': ((json['updates'] as Array).map(UpdateFlagSpecificTargetsFromJSON)), - 'notifications': json['notifications'] == null ? undefined : json['notifications'], - 'changeDescription': json['changeDescription'] == null ? undefined : json['changeDescription'], - }; -} - -export function BulkUpdateFlagSpecificTargetsSchemaToJSON(json: any): BulkUpdateFlagSpecificTargetsSchema { - return BulkUpdateFlagSpecificTargetsSchemaToJSONTyped(json, false); -} - -export function BulkUpdateFlagSpecificTargetsSchemaToJSONTyped(value?: BulkUpdateFlagSpecificTargetsSchema | null, ignoreDiscriminator: boolean = false): any { - if (value == null) { - return value; - } - - return { - - 'updates': ((value['updates'] as Array).map(UpdateFlagSpecificTargetsToJSON)), - 'notifications': value['notifications'], - 'changeDescription': value['changeDescription'], - }; -} - diff --git a/packages/rest-api-sdk/src/generated/models/CreateFlag200Response.ts b/packages/rest-api-sdk/src/generated/models/CreateFlag200Response.ts new file mode 100644 index 00000000..e0f4f943 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/CreateFlag200Response.ts @@ -0,0 +1,74 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 3.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { CreateFlag200ResponseFlag } from './CreateFlag200ResponseFlag'; +import { + CreateFlag200ResponseFlagFromJSON, + CreateFlag200ResponseFlagFromJSONTyped, + CreateFlag200ResponseFlagToJSON, + CreateFlag200ResponseFlagToJSONTyped, +} from './CreateFlag200ResponseFlag'; + +/** + * + * @export + * @interface CreateFlag200Response + */ +export interface CreateFlag200Response { + /** + * + * @type {CreateFlag200ResponseFlag} + * @memberof CreateFlag200Response + */ + flag: CreateFlag200ResponseFlag; +} + +/** + * Check if a given object implements the CreateFlag200Response interface. + */ +export function instanceOfCreateFlag200Response(value: object): value is CreateFlag200Response { + if (!('flag' in value) || value['flag'] === undefined) return false; + return true; +} + +export function CreateFlag200ResponseFromJSON(json: any): CreateFlag200Response { + return CreateFlag200ResponseFromJSONTyped(json, false); +} + +export function CreateFlag200ResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): CreateFlag200Response { + if (json == null) { + return json; + } + return { + + 'flag': CreateFlag200ResponseFlagFromJSON(json['flag']), + }; +} + +export function CreateFlag200ResponseToJSON(json: any): CreateFlag200Response { + return CreateFlag200ResponseToJSONTyped(json, false); +} + +export function CreateFlag200ResponseToJSONTyped(value?: CreateFlag200Response | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'flag': CreateFlag200ResponseFlagToJSON(value['flag']), + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/CreateFlag200ResponseFlag.ts b/packages/rest-api-sdk/src/generated/models/CreateFlag200ResponseFlag.ts new file mode 100644 index 00000000..5475f725 --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/CreateFlag200ResponseFlag.ts @@ -0,0 +1,190 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 3.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +import type { StageHeader } from './StageHeader'; +import { + StageHeaderFromJSON, + StageHeaderFromJSONTyped, + StageHeaderToJSON, + StageHeaderToJSONTyped, +} from './StageHeader'; +import type { ReflagUserHeader } from './ReflagUserHeader'; +import { + ReflagUserHeaderFromJSON, + ReflagUserHeaderFromJSONTyped, + ReflagUserHeaderToJSON, + ReflagUserHeaderToJSONTyped, +} from './ReflagUserHeader'; + +/** + * + * @export + * @interface CreateFlag200ResponseFlag + */ +export interface CreateFlag200ResponseFlag { + /** + * Flag ID + * @type {string} + * @memberof CreateFlag200ResponseFlag + */ + id: string; + /** + * Unique flag key + * @type {string} + * @memberof CreateFlag200ResponseFlag + */ + key: string; + /** + * Flag name + * @type {string} + * @memberof CreateFlag200ResponseFlag + */ + name: string; + /** + * Flag description + * @type {string} + * @memberof CreateFlag200ResponseFlag + */ + description?: string; + /** + * + * @type {StageHeader} + * @memberof CreateFlag200ResponseFlag + */ + stage?: StageHeader; + /** + * + * @type {ReflagUserHeader} + * @memberof CreateFlag200ResponseFlag + */ + owner?: ReflagUserHeader; + /** + * Whether the flag is archived + * @type {boolean} + * @memberof CreateFlag200ResponseFlag + */ + archived: boolean; + /** + * Whether the flag is stale + * @type {boolean} + * @memberof CreateFlag200ResponseFlag + */ + stale: boolean; + /** + * Whether the flag is permanent + * @type {boolean} + * @memberof CreateFlag200ResponseFlag + */ + permanent: boolean; + /** + * Timestamp when the flag was created + * @type {string} + * @memberof CreateFlag200ResponseFlag + */ + createdAt?: string; + /** + * Timestamp when the flag was last checked + * @type {string} + * @memberof CreateFlag200ResponseFlag + */ + lastCheckAt?: string; + /** + * Timestamp when the flag was last tracked + * @type {string} + * @memberof CreateFlag200ResponseFlag + */ + lastTrackAt?: string; + /** + * Timestamp when the flag was rolled out to everyone + * @type {string} + * @memberof CreateFlag200ResponseFlag + */ + rolledOutToEveryoneAt?: string; + /** + * Parent flag ID + * @type {string} + * @memberof CreateFlag200ResponseFlag + */ + parentFlagId?: string; +} + +/** + * Check if a given object implements the CreateFlag200ResponseFlag interface. + */ +export function instanceOfCreateFlag200ResponseFlag(value: object): value is CreateFlag200ResponseFlag { + if (!('id' in value) || value['id'] === undefined) return false; + if (!('key' in value) || value['key'] === undefined) return false; + if (!('name' in value) || value['name'] === undefined) return false; + if (!('archived' in value) || value['archived'] === undefined) return false; + if (!('stale' in value) || value['stale'] === undefined) return false; + if (!('permanent' in value) || value['permanent'] === undefined) return false; + return true; +} + +export function CreateFlag200ResponseFlagFromJSON(json: any): CreateFlag200ResponseFlag { + return CreateFlag200ResponseFlagFromJSONTyped(json, false); +} + +export function CreateFlag200ResponseFlagFromJSONTyped(json: any, ignoreDiscriminator: boolean): CreateFlag200ResponseFlag { + if (json == null) { + return json; + } + return { + + 'id': json['id'], + 'key': json['key'], + 'name': json['name'], + 'description': json['description'] == null ? undefined : json['description'], + 'stage': json['stage'] == null ? undefined : StageHeaderFromJSON(json['stage']), + 'owner': json['owner'] == null ? undefined : ReflagUserHeaderFromJSON(json['owner']), + 'archived': json['archived'], + 'stale': json['stale'], + 'permanent': json['permanent'], + 'createdAt': json['createdAt'] == null ? undefined : json['createdAt'], + 'lastCheckAt': json['lastCheckAt'] == null ? undefined : json['lastCheckAt'], + 'lastTrackAt': json['lastTrackAt'] == null ? undefined : json['lastTrackAt'], + 'rolledOutToEveryoneAt': json['rolledOutToEveryoneAt'] == null ? undefined : json['rolledOutToEveryoneAt'], + 'parentFlagId': json['parentFlagId'] == null ? undefined : json['parentFlagId'], + }; +} + +export function CreateFlag200ResponseFlagToJSON(json: any): CreateFlag200ResponseFlag { + return CreateFlag200ResponseFlagToJSONTyped(json, false); +} + +export function CreateFlag200ResponseFlagToJSONTyped(value?: CreateFlag200ResponseFlag | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'id': value['id'], + 'key': value['key'], + 'name': value['name'], + 'description': value['description'], + 'stage': StageHeaderToJSON(value['stage']), + 'owner': ReflagUserHeaderToJSON(value['owner']), + 'archived': value['archived'], + 'stale': value['stale'], + 'permanent': value['permanent'], + 'createdAt': value['createdAt'], + 'lastCheckAt': value['lastCheckAt'], + 'lastTrackAt': value['lastTrackAt'], + 'rolledOutToEveryoneAt': value['rolledOutToEveryoneAt'], + 'parentFlagId': value['parentFlagId'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/CreateFlagRequest.ts b/packages/rest-api-sdk/src/generated/models/CreateFlagRequest.ts new file mode 100644 index 00000000..9b58ee5a --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/CreateFlagRequest.ts @@ -0,0 +1,115 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 3.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface CreateFlagRequest + */ +export interface CreateFlagRequest { + /** + * Key of the flag + * @type {string} + * @memberof CreateFlagRequest + */ + key: string; + /** + * Name of the flag + * @type {string} + * @memberof CreateFlagRequest + */ + name: string; + /** + * + * @type {string} + * @memberof CreateFlagRequest + */ + description?: string | null; + /** + * Stage ID of the flag + * @type {string} + * @memberof CreateFlagRequest + */ + stageId?: string; + /** + * + * @type {string} + * @memberof CreateFlagRequest + */ + ownerUserId?: string | null; + /** + * + * @type {boolean} + * @memberof CreateFlagRequest + */ + permanent?: boolean; + /** + * Whether the flag is secret + * @type {boolean} + * @memberof CreateFlagRequest + */ + secret?: boolean; +} + +/** + * Check if a given object implements the CreateFlagRequest interface. + */ +export function instanceOfCreateFlagRequest(value: object): value is CreateFlagRequest { + if (!('key' in value) || value['key'] === undefined) return false; + if (!('name' in value) || value['name'] === undefined) return false; + return true; +} + +export function CreateFlagRequestFromJSON(json: any): CreateFlagRequest { + return CreateFlagRequestFromJSONTyped(json, false); +} + +export function CreateFlagRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): CreateFlagRequest { + if (json == null) { + return json; + } + return { + + 'key': json['key'], + 'name': json['name'], + 'description': json['description'] == null ? undefined : json['description'], + 'stageId': json['stageId'] == null ? undefined : json['stageId'], + 'ownerUserId': json['ownerUserId'] == null ? undefined : json['ownerUserId'], + 'permanent': json['permanent'] == null ? undefined : json['permanent'], + 'secret': json['secret'] == null ? undefined : json['secret'], + }; +} + +export function CreateFlagRequestToJSON(json: any): CreateFlagRequest { + return CreateFlagRequestToJSONTyped(json, false); +} + +export function CreateFlagRequestToJSONTyped(value?: CreateFlagRequest | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'key': value['key'], + 'name': value['name'], + 'description': value['description'], + 'stageId': value['stageId'], + 'ownerUserId': value['ownerUserId'], + 'permanent': value['permanent'], + 'secret': value['secret'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/EntityFlag.ts b/packages/rest-api-sdk/src/generated/models/EntityFlag.ts index 719c3423..fc2aede7 100644 --- a/packages/rest-api-sdk/src/generated/models/EntityFlag.ts +++ b/packages/rest-api-sdk/src/generated/models/EntityFlag.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -37,6 +37,12 @@ export interface EntityFlag { * @memberof EntityFlag */ name: string; + /** + * Timestamp when the flag was created + * @type {string} + * @memberof EntityFlag + */ + createdAt: string; /** * Whether the flag is enabled for this entity * @type {boolean} @@ -48,7 +54,7 @@ export interface EntityFlag { * @type {boolean} * @memberof EntityFlag */ - specificallyTargetedValue: boolean | null; + specificTargetValue: boolean | null; /** * * @type {string} @@ -100,8 +106,9 @@ export function instanceOfEntityFlag(value: object): value is EntityFlag { if (!('id' in value) || value['id'] === undefined) return false; if (!('key' in value) || value['key'] === undefined) return false; if (!('name' in value) || value['name'] === undefined) return false; + if (!('createdAt' in value) || value['createdAt'] === undefined) return false; if (!('value' in value) || value['value'] === undefined) return false; - if (!('specificallyTargetedValue' in value) || value['specificallyTargetedValue'] === undefined) return false; + if (!('specificTargetValue' in value) || value['specificTargetValue'] === undefined) return false; if (!('firstExposureAt' in value) || value['firstExposureAt'] === undefined) return false; if (!('lastExposureAt' in value) || value['lastExposureAt'] === undefined) return false; if (!('lastCheckAt' in value) || value['lastCheckAt'] === undefined) return false; @@ -125,8 +132,9 @@ export function EntityFlagFromJSONTyped(json: any, ignoreDiscriminator: boolean) 'id': json['id'], 'key': json['key'], 'name': json['name'], + 'createdAt': json['createdAt'], 'value': json['value'], - 'specificallyTargetedValue': json['specificallyTargetedValue'], + 'specificTargetValue': json['specificTargetValue'], 'firstExposureAt': json['firstExposureAt'], 'lastExposureAt': json['lastExposureAt'], 'lastCheckAt': json['lastCheckAt'], @@ -151,8 +159,9 @@ export function EntityFlagToJSONTyped(value?: EntityFlag | null, ignoreDiscrimin 'id': value['id'], 'key': value['key'], 'name': value['name'], + 'createdAt': value['createdAt'], 'value': value['value'], - 'specificallyTargetedValue': value['specificallyTargetedValue'], + 'specificTargetValue': value['specificTargetValue'], 'firstExposureAt': value['firstExposureAt'], 'lastExposureAt': value['lastExposureAt'], 'lastCheckAt': value['lastCheckAt'], diff --git a/packages/rest-api-sdk/src/generated/models/EntityFlagUpdate.ts b/packages/rest-api-sdk/src/generated/models/EntityFlagUpdate.ts index 10d9d326..7c84b1d6 100644 --- a/packages/rest-api-sdk/src/generated/models/EntityFlagUpdate.ts +++ b/packages/rest-api-sdk/src/generated/models/EntityFlagUpdate.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -14,7 +14,7 @@ import { mapValues } from '../runtime'; /** - * Update for a single flag's specific targeting + * Update for a single flag's explicit targeting override * @export * @interface EntityFlagUpdate */ @@ -30,17 +30,17 @@ export interface EntityFlagUpdate { * @type {boolean} * @memberof EntityFlagUpdate */ - value: EntityFlagUpdateValueEnum | null; + specificTargetValue: EntityFlagUpdateSpecificTargetValueEnum | null; } /** * @export */ -export const EntityFlagUpdateValueEnum = { +export const EntityFlagUpdateSpecificTargetValueEnum = { True: true } as const; -export type EntityFlagUpdateValueEnum = typeof EntityFlagUpdateValueEnum[keyof typeof EntityFlagUpdateValueEnum]; +export type EntityFlagUpdateSpecificTargetValueEnum = typeof EntityFlagUpdateSpecificTargetValueEnum[keyof typeof EntityFlagUpdateSpecificTargetValueEnum]; /** @@ -48,7 +48,7 @@ export type EntityFlagUpdateValueEnum = typeof EntityFlagUpdateValueEnum[keyof t */ export function instanceOfEntityFlagUpdate(value: object): value is EntityFlagUpdate { if (!('flagKey' in value) || value['flagKey'] === undefined) return false; - if (!('value' in value) || value['value'] === undefined) return false; + if (!('specificTargetValue' in value) || value['specificTargetValue'] === undefined) return false; return true; } @@ -63,7 +63,7 @@ export function EntityFlagUpdateFromJSONTyped(json: any, ignoreDiscriminator: bo return { 'flagKey': json['flagKey'], - 'value': json['value'], + 'specificTargetValue': json['specificTargetValue'], }; } @@ -79,7 +79,7 @@ export function EntityFlagUpdateToJSONTyped(value?: EntityFlagUpdate | null, ign return { 'flagKey': value['flagKey'], - 'value': value['value'], + 'specificTargetValue': value['specificTargetValue'], }; } diff --git a/packages/rest-api-sdk/src/generated/models/EntityFlagsResponse.ts b/packages/rest-api-sdk/src/generated/models/EntityFlagsResponse.ts index af956367..b7c1fd11 100644 --- a/packages/rest-api-sdk/src/generated/models/EntityFlagsResponse.ts +++ b/packages/rest-api-sdk/src/generated/models/EntityFlagsResponse.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/Environment.ts b/packages/rest-api-sdk/src/generated/models/Environment.ts index 1c64a7b2..53f93a66 100644 --- a/packages/rest-api-sdk/src/generated/models/Environment.ts +++ b/packages/rest-api-sdk/src/generated/models/Environment.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/EnvironmentHeader.ts b/packages/rest-api-sdk/src/generated/models/EnvironmentHeader.ts index 2f040fd4..f790394b 100644 --- a/packages/rest-api-sdk/src/generated/models/EnvironmentHeader.ts +++ b/packages/rest-api-sdk/src/generated/models/EnvironmentHeader.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderCollection.ts b/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderCollection.ts index 0540c54f..798fb3a5 100644 --- a/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderCollection.ts +++ b/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderCollection.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderSortByColumn.ts b/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderSortByColumn.ts index 246e4e20..358ec424 100644 --- a/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderSortByColumn.ts +++ b/packages/rest-api-sdk/src/generated/models/EnvironmentHeaderSortByColumn.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/EnvironmentSdkAccess.ts b/packages/rest-api-sdk/src/generated/models/EnvironmentSdkAccess.ts index 687b6364..cb03ea10 100644 --- a/packages/rest-api-sdk/src/generated/models/EnvironmentSdkAccess.ts +++ b/packages/rest-api-sdk/src/generated/models/EnvironmentSdkAccess.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/ErrorResponse.ts b/packages/rest-api-sdk/src/generated/models/ErrorResponse.ts index 9ba1225c..b90d1d0e 100644 --- a/packages/rest-api-sdk/src/generated/models/ErrorResponse.ts +++ b/packages/rest-api-sdk/src/generated/models/ErrorResponse.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/ErrorResponseError.ts b/packages/rest-api-sdk/src/generated/models/ErrorResponseError.ts index 3180bf4e..efd7b93e 100644 --- a/packages/rest-api-sdk/src/generated/models/ErrorResponseError.ts +++ b/packages/rest-api-sdk/src/generated/models/ErrorResponseError.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/FlagHeader.ts b/packages/rest-api-sdk/src/generated/models/FlagHeader.ts index fb2fc853..8c092b05 100644 --- a/packages/rest-api-sdk/src/generated/models/FlagHeader.ts +++ b/packages/rest-api-sdk/src/generated/models/FlagHeader.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/FlagHeaderCollection.ts b/packages/rest-api-sdk/src/generated/models/FlagHeaderCollection.ts index d966999b..a3dbd7eb 100644 --- a/packages/rest-api-sdk/src/generated/models/FlagHeaderCollection.ts +++ b/packages/rest-api-sdk/src/generated/models/FlagHeaderCollection.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/FlagKeyFormat.ts b/packages/rest-api-sdk/src/generated/models/FlagKeyFormat.ts index 10e35969..b0d33716 100644 --- a/packages/rest-api-sdk/src/generated/models/FlagKeyFormat.ts +++ b/packages/rest-api-sdk/src/generated/models/FlagKeyFormat.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/FlagTargeting.ts b/packages/rest-api-sdk/src/generated/models/FlagTargeting.ts index 15fc9d2f..970812a6 100644 --- a/packages/rest-api-sdk/src/generated/models/FlagTargeting.ts +++ b/packages/rest-api-sdk/src/generated/models/FlagTargeting.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/FlagTargetingCollection.ts b/packages/rest-api-sdk/src/generated/models/FlagTargetingCollection.ts deleted file mode 100644 index e00e7977..00000000 --- a/packages/rest-api-sdk/src/generated/models/FlagTargetingCollection.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/** - * Reflag API - * Feature flag management API - * - * The version of the OpenAPI document: 2.0.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - -import { mapValues } from '../runtime'; -import type { FlagTargeting } from './FlagTargeting'; -import { - FlagTargetingFromJSON, - FlagTargetingFromJSONTyped, - FlagTargetingToJSON, - FlagTargetingToJSONTyped, -} from './FlagTargeting'; - -/** - * Collection response containing flags' targeting information - * @export - * @interface FlagTargetingCollection - */ -export interface FlagTargetingCollection { - /** - * Collection of flags' targeting information - * @type {Array} - * @memberof FlagTargetingCollection - */ - data: Array; -} - -/** - * Check if a given object implements the FlagTargetingCollection interface. - */ -export function instanceOfFlagTargetingCollection(value: object): value is FlagTargetingCollection { - if (!('data' in value) || value['data'] === undefined) return false; - return true; -} - -export function FlagTargetingCollectionFromJSON(json: any): FlagTargetingCollection { - return FlagTargetingCollectionFromJSONTyped(json, false); -} - -export function FlagTargetingCollectionFromJSONTyped(json: any, ignoreDiscriminator: boolean): FlagTargetingCollection { - if (json == null) { - return json; - } - return { - - 'data': ((json['data'] as Array).map(FlagTargetingFromJSON)), - }; -} - -export function FlagTargetingCollectionToJSON(json: any): FlagTargetingCollection { - return FlagTargetingCollectionToJSONTyped(json, false); -} - -export function FlagTargetingCollectionToJSONTyped(value?: FlagTargetingCollection | null, ignoreDiscriminator: boolean = false): any { - if (value == null) { - return value; - } - - return { - - 'data': ((value['data'] as Array).map(FlagTargetingToJSON)), - }; -} - diff --git a/packages/rest-api-sdk/src/generated/models/FlagValue.ts b/packages/rest-api-sdk/src/generated/models/FlagValue.ts index 3c6441e9..5495197b 100644 --- a/packages/rest-api-sdk/src/generated/models/FlagValue.ts +++ b/packages/rest-api-sdk/src/generated/models/FlagValue.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/FlagValueTargeting.ts b/packages/rest-api-sdk/src/generated/models/FlagValueTargeting.ts index b0e2c8bb..d16726bc 100644 --- a/packages/rest-api-sdk/src/generated/models/FlagValueTargeting.ts +++ b/packages/rest-api-sdk/src/generated/models/FlagValueTargeting.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/OrgHeader.ts b/packages/rest-api-sdk/src/generated/models/OrgHeader.ts index c4355a93..e24841bb 100644 --- a/packages/rest-api-sdk/src/generated/models/OrgHeader.ts +++ b/packages/rest-api-sdk/src/generated/models/OrgHeader.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/ReflagUserHeader.ts b/packages/rest-api-sdk/src/generated/models/ReflagUserHeader.ts index b8c85a37..2a7da548 100644 --- a/packages/rest-api-sdk/src/generated/models/ReflagUserHeader.ts +++ b/packages/rest-api-sdk/src/generated/models/ReflagUserHeader.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/SegmentHeader.ts b/packages/rest-api-sdk/src/generated/models/SegmentHeader.ts index 1b08c6e7..50529443 100644 --- a/packages/rest-api-sdk/src/generated/models/SegmentHeader.ts +++ b/packages/rest-api-sdk/src/generated/models/SegmentHeader.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/SegmentType.ts b/packages/rest-api-sdk/src/generated/models/SegmentType.ts index e1c57595..da773ae5 100644 --- a/packages/rest-api-sdk/src/generated/models/SegmentType.ts +++ b/packages/rest-api-sdk/src/generated/models/SegmentType.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/SortOrder.ts b/packages/rest-api-sdk/src/generated/models/SortOrder.ts index 1aa9fc0c..034e4f2f 100644 --- a/packages/rest-api-sdk/src/generated/models/SortOrder.ts +++ b/packages/rest-api-sdk/src/generated/models/SortOrder.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/StageHeader.ts b/packages/rest-api-sdk/src/generated/models/StageHeader.ts index c37d6a47..13975a96 100644 --- a/packages/rest-api-sdk/src/generated/models/StageHeader.ts +++ b/packages/rest-api-sdk/src/generated/models/StageHeader.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -37,12 +37,6 @@ export interface StageHeader { * @memberof StageHeader */ color: string; - /** - * Flags currently in this stage - * @type {number} - * @memberof StageHeader - */ - assignedFlagCount: number; /** * Stage order * @type {number} @@ -58,7 +52,6 @@ export function instanceOfStageHeader(value: object): value is StageHeader { if (!('id' in value) || value['id'] === undefined) return false; if (!('name' in value) || value['name'] === undefined) return false; if (!('color' in value) || value['color'] === undefined) return false; - if (!('assignedFlagCount' in value) || value['assignedFlagCount'] === undefined) return false; if (!('order' in value) || value['order'] === undefined) return false; return true; } @@ -76,7 +69,6 @@ export function StageHeaderFromJSONTyped(json: any, ignoreDiscriminator: boolean 'id': json['id'], 'name': json['name'], 'color': json['color'], - 'assignedFlagCount': json['assignedFlagCount'], 'order': json['order'], }; } @@ -95,7 +87,6 @@ export function StageHeaderToJSONTyped(value?: StageHeader | null, ignoreDiscrim 'id': value['id'], 'name': value['name'], 'color': value['color'], - 'assignedFlagCount': value['assignedFlagCount'], 'order': value['order'], }; } diff --git a/packages/rest-api-sdk/src/generated/models/UpdateEntityFlagsBody.ts b/packages/rest-api-sdk/src/generated/models/UpdateEntityFlagsBody.ts index ab8ef8c7..f526cecd 100644 --- a/packages/rest-api-sdk/src/generated/models/UpdateEntityFlagsBody.ts +++ b/packages/rest-api-sdk/src/generated/models/UpdateEntityFlagsBody.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/rest-api-sdk/src/generated/models/UpdateFlagRequest.ts b/packages/rest-api-sdk/src/generated/models/UpdateFlagRequest.ts new file mode 100644 index 00000000..b08e9fdf --- /dev/null +++ b/packages/rest-api-sdk/src/generated/models/UpdateFlagRequest.ts @@ -0,0 +1,113 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Reflag API + * Feature flag management API + * + * The version of the OpenAPI document: 3.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { mapValues } from '../runtime'; +/** + * + * @export + * @interface UpdateFlagRequest + */ +export interface UpdateFlagRequest { + /** + * Name of the flag + * @type {string} + * @memberof UpdateFlagRequest + */ + name?: string; + /** + * + * @type {string} + * @memberof UpdateFlagRequest + */ + description?: string | null; + /** + * + * @type {string} + * @memberof UpdateFlagRequest + */ + ownerUserId?: string | null; + /** + * + * @type {boolean} + * @memberof UpdateFlagRequest + */ + permanent?: boolean; + /** + * Whether the flag is secret + * @type {boolean} + * @memberof UpdateFlagRequest + */ + secret?: boolean; + /** + * + * @type {boolean} + * @memberof UpdateFlagRequest + */ + isArchived?: boolean; + /** + * Stage ID of the flag + * @type {string} + * @memberof UpdateFlagRequest + */ + stageId?: string; +} + +/** + * Check if a given object implements the UpdateFlagRequest interface. + */ +export function instanceOfUpdateFlagRequest(value: object): value is UpdateFlagRequest { + return true; +} + +export function UpdateFlagRequestFromJSON(json: any): UpdateFlagRequest { + return UpdateFlagRequestFromJSONTyped(json, false); +} + +export function UpdateFlagRequestFromJSONTyped(json: any, ignoreDiscriminator: boolean): UpdateFlagRequest { + if (json == null) { + return json; + } + return { + + 'name': json['name'] == null ? undefined : json['name'], + 'description': json['description'] == null ? undefined : json['description'], + 'ownerUserId': json['ownerUserId'] == null ? undefined : json['ownerUserId'], + 'permanent': json['permanent'] == null ? undefined : json['permanent'], + 'secret': json['secret'] == null ? undefined : json['secret'], + 'isArchived': json['isArchived'] == null ? undefined : json['isArchived'], + 'stageId': json['stageId'] == null ? undefined : json['stageId'], + }; +} + +export function UpdateFlagRequestToJSON(json: any): UpdateFlagRequest { + return UpdateFlagRequestToJSONTyped(json, false); +} + +export function UpdateFlagRequestToJSONTyped(value?: UpdateFlagRequest | null, ignoreDiscriminator: boolean = false): any { + if (value == null) { + return value; + } + + return { + + 'name': value['name'], + 'description': value['description'], + 'ownerUserId': value['ownerUserId'], + 'permanent': value['permanent'], + 'secret': value['secret'], + 'isArchived': value['isArchived'], + 'stageId': value['stageId'], + }; +} + diff --git a/packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargets.ts b/packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargets.ts deleted file mode 100644 index 293ffcb8..00000000 --- a/packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargets.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/** - * Reflag API - * Feature flag management API - * - * The version of the OpenAPI document: 2.0.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - -import { mapValues } from '../runtime'; -import type { UpdateFlagSpecificTargetsValue } from './UpdateFlagSpecificTargetsValue'; -import { - UpdateFlagSpecificTargetsValueFromJSON, - UpdateFlagSpecificTargetsValueFromJSONTyped, - UpdateFlagSpecificTargetsValueToJSON, - UpdateFlagSpecificTargetsValueToJSONTyped, -} from './UpdateFlagSpecificTargetsValue'; - -/** - * Update the explicit value of the flag for a given audience (null means to remove the flag from the audience). - * @export - * @interface UpdateFlagSpecificTargets - */ -export interface UpdateFlagSpecificTargets { - /** - * Unique flag key - * @type {string} - * @memberof UpdateFlagSpecificTargets - */ - flagKey: string; - /** - * - * @type {UpdateFlagSpecificTargetsValue} - * @memberof UpdateFlagSpecificTargets - */ - value: UpdateFlagSpecificTargetsValue | null; - /** - * Company ID within your application - * @type {string} - * @memberof UpdateFlagSpecificTargets - */ - companyId?: string; - /** - * User ID within your application - * @type {string} - * @memberof UpdateFlagSpecificTargets - */ - userId?: string; -} - -/** - * Check if a given object implements the UpdateFlagSpecificTargets interface. - */ -export function instanceOfUpdateFlagSpecificTargets(value: object): value is UpdateFlagSpecificTargets { - if (!('flagKey' in value) || value['flagKey'] === undefined) return false; - if (!('value' in value) || value['value'] === undefined) return false; - return true; -} - -export function UpdateFlagSpecificTargetsFromJSON(json: any): UpdateFlagSpecificTargets { - return UpdateFlagSpecificTargetsFromJSONTyped(json, false); -} - -export function UpdateFlagSpecificTargetsFromJSONTyped(json: any, ignoreDiscriminator: boolean): UpdateFlagSpecificTargets { - if (json == null) { - return json; - } - return { - - 'flagKey': json['flagKey'], - 'value': UpdateFlagSpecificTargetsValueFromJSON(json['value']), - 'companyId': json['companyId'] == null ? undefined : json['companyId'], - 'userId': json['userId'] == null ? undefined : json['userId'], - }; -} - -export function UpdateFlagSpecificTargetsToJSON(json: any): UpdateFlagSpecificTargets { - return UpdateFlagSpecificTargetsToJSONTyped(json, false); -} - -export function UpdateFlagSpecificTargetsToJSONTyped(value?: UpdateFlagSpecificTargets | null, ignoreDiscriminator: boolean = false): any { - if (value == null) { - return value; - } - - return { - - 'flagKey': value['flagKey'], - 'value': UpdateFlagSpecificTargetsValueToJSON(value['value']), - 'companyId': value['companyId'], - 'userId': value['userId'], - }; -} - diff --git a/packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargetsValue.ts b/packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargetsValue.ts deleted file mode 100644 index 1cc3158b..00000000 --- a/packages/rest-api-sdk/src/generated/models/UpdateFlagSpecificTargetsValue.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/** - * Reflag API - * Feature flag management API - * - * The version of the OpenAPI document: 2.0.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - -import { mapValues } from '../runtime'; -/** - * The value of the flag for the given audience (null means to remove the flag from the audience). - * @export - * @interface UpdateFlagSpecificTargetsValue - */ -export interface UpdateFlagSpecificTargetsValue { -} - -/** - * Check if a given object implements the UpdateFlagSpecificTargetsValue interface. - */ -export function instanceOfUpdateFlagSpecificTargetsValue(value: object): value is UpdateFlagSpecificTargetsValue { - return true; -} - -export function UpdateFlagSpecificTargetsValueFromJSON(json: any): UpdateFlagSpecificTargetsValue { - return UpdateFlagSpecificTargetsValueFromJSONTyped(json, false); -} - -export function UpdateFlagSpecificTargetsValueFromJSONTyped(json: any, ignoreDiscriminator: boolean): UpdateFlagSpecificTargetsValue { - return json; -} - -export function UpdateFlagSpecificTargetsValueToJSON(json: any): UpdateFlagSpecificTargetsValue { - return UpdateFlagSpecificTargetsValueToJSONTyped(json, false); -} - -export function UpdateFlagSpecificTargetsValueToJSONTyped(value?: UpdateFlagSpecificTargetsValue | null, ignoreDiscriminator: boolean = false): any { - return value; -} - diff --git a/packages/rest-api-sdk/src/generated/models/index.ts b/packages/rest-api-sdk/src/generated/models/index.ts index 0bef5ad9..1c4b59e4 100644 --- a/packages/rest-api-sdk/src/generated/models/index.ts +++ b/packages/rest-api-sdk/src/generated/models/index.ts @@ -3,7 +3,9 @@ export * from './App'; export * from './AppHeader'; export * from './AppHeaderCollection'; -export * from './BulkUpdateFlagSpecificTargetsSchema'; +export * from './CreateFlag200Response'; +export * from './CreateFlag200ResponseFlag'; +export * from './CreateFlagRequest'; export * from './EntityFlag'; export * from './EntityFlagUpdate'; export * from './EntityFlagsResponse'; @@ -18,7 +20,6 @@ export * from './FlagHeader'; export * from './FlagHeaderCollection'; export * from './FlagKeyFormat'; export * from './FlagTargeting'; -export * from './FlagTargetingCollection'; export * from './FlagValue'; export * from './FlagValueTargeting'; export * from './OrgHeader'; @@ -28,5 +29,4 @@ export * from './SegmentType'; export * from './SortOrder'; export * from './StageHeader'; export * from './UpdateEntityFlagsBody'; -export * from './UpdateFlagSpecificTargets'; -export * from './UpdateFlagSpecificTargetsValue'; +export * from './UpdateFlagRequest'; diff --git a/packages/rest-api-sdk/src/generated/runtime.ts b/packages/rest-api-sdk/src/generated/runtime.ts index 357f66d9..b87a849d 100644 --- a/packages/rest-api-sdk/src/generated/runtime.ts +++ b/packages/rest-api-sdk/src/generated/runtime.ts @@ -4,7 +4,7 @@ * Reflag API * Feature flag management API * - * The version of the OpenAPI document: 2.0.0 + * The version of the OpenAPI document: 3.0.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/reflag-openapi.json b/reflag-openapi.json index 4e9a0129..8051a5d8 100644 --- a/reflag-openapi.json +++ b/reflag-openapi.json @@ -1 +1 @@ -{"openapi":"3.1.0","info":{"title":"Reflag API","version":"1.3.0","description":"Feature flag management API","contact":{"name":"Reflag Support","url":"https://reflag.com/support"},"license":{"name":"Proprietary","url":"https://reflag.com/"}},"servers":[{"url":"https://app.reflag.com/api","description":"Production server"}],"security":[{"APIKey":[]}],"paths":{"/apps/{appId}":{"get":{"summary":"Get details of an application","description":"Retrieve a specific application by its identifier","operationId":"getApp","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"query","name":"expand","schema":{"default":[],"type":"array","items":{"$ref":"#/components/schemas/appHeaderWithExpansionsOptionalFields"}}}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/appHeaderWithExpansions"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps":{"get":{"summary":"List of applications","description":"Retrieve all accessible applications","operationId":"listApps","parameters":[{"in":"query","name":"orgId","schema":{"$ref":"#/components/schemas/orgId"}},{"in":"query","name":"expand","schema":{"default":[],"type":"array","items":{"$ref":"#/components/schemas/appHeaderWithExpansionsOptionalFields"}}}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/appHeaderWithExpansionsCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/environments":{"get":{"summary":"List environments for application","description":"Retrieve all environments for a specific application","operationId":"listEnvironments","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"query","name":"sortOrder","schema":{"$ref":"#/components/schemas/sortOrder"},"description":"Sort order applied to the sorting column"},{"in":"query","name":"sortBy","schema":{"$ref":"#/components/schemas/environmentHeaderSortByColumn"},"description":"The column to sort by"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/environmentHeaderSortableCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/environments/{envId}":{"get":{"summary":"Get environment details","description":"Retrieve details for a specific environment","operationId":"getEnvironment","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/environmentDetails"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags":{"get":{"summary":"List flags for application","description":"Retrieve all flags for a specific application","operationId":"listFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/{flagKey}/targeting/{envId}":{"get":{"summary":"Get flag targeting for an environment","description":"Retrieve targeting for a flag in an environment","operationId":"getFlagTargeting","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"flagKey","schema":{"$ref":"#/components/schemas/flagKey"},"required":true,"description":"Unique flag key"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagTargeting"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/specific-targets/{envId}":{"patch":{"summary":"Update flag specific targets for an environment","description":"Update specific companies and users for flags in an environment","operationId":"updateBulkFlagSpecificTargets","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/bulkUpdateFlagSpecificTargetsSchema"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagTargetingCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/companies/{companyId}/flags":{"get":{"summary":"Get flags for a company","description":"Retrieve all flags with their targeting status for a specific company","operationId":"getCompanyFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"companyId","schema":{"$ref":"#/components/schemas/companyId"},"required":true,"description":"Company ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Update flag targeting for a company","description":"Update specific targeting for flags for a company in an environment","operationId":"updateCompanyFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"companyId","schema":{"$ref":"#/components/schemas/companyId"},"required":true,"description":"Company ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/updateEntityFlagsBody"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/users/{userId}/flags":{"get":{"summary":"Get flags for a user","description":"Retrieve all flags with their targeting status for a specific user","operationId":"getUserFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"userId","schema":{"$ref":"#/components/schemas/userId"},"required":true,"description":"User ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Update flag targeting for a user","description":"Update specific targeting for flags for a user in an environment","operationId":"updateUserFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"userId","schema":{"$ref":"#/components/schemas/userId"},"required":true,"description":"User ID within your application"},{"in":"query","name":"envId","schema":{"description":"Environment ID to evaluate targeting for","type":"string","minLength":1},"required":true,"description":"Environment ID to evaluate targeting for"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/updateEntityFlagsBody"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"appId":{"description":"App identifier","type":"string","minLength":1},"appHeaderWithExpansionsOptionalFields":{"description":"Optional fields that can be included when requesting the app basic information","type":"string","enum":["environments","stages","segments"]},"orgId":{"description":"Organization identifier","type":"string","minLength":1},"sortOrder":{"description":"Sort order applied to the sorting column","default":"asc","type":"string","enum":["asc","desc"]},"environmentHeaderSortByColumn":{"description":"The column to sort by","default":"order","type":"string","enum":["name","order"]},"envId":{"description":"Environment identifier","type":"string","minLength":1},"flagKey":{"description":"Unique flag key","type":"string","minLength":1},"bulkUpdateFlagSpecificTargetsSchema":{"description":"Update the explicit value of multiple flags for a given audience","type":"object","properties":{"updates":{"description":"The list of updates to make to the flags' targeting","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/updateFlagSpecificTargets"}},"notifications":{"description":"Whether to send notifications about the change to configured integration (eg. Slack, Linear, etc). Defaults to true.","default":true,"type":"boolean"},"changeDescription":{"description":"The description of the change recorded in the change history","type":"string"}},"required":["updates"]},"updateFlagSpecificTargets":{"description":"Update the explicit value of the flag for a given audience (null means to remove the flag from the audience).","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"value":{"description":"The value of the flag for the given audience (null means to remove the flag from the audience).","anyOf":[{"type":"string","const":"true"},{"type":"boolean","const":true},{"type":"null"}]},"companyId":{"$ref":"#/components/schemas/companyId"},"userId":{"$ref":"#/components/schemas/userId"}},"required":["flagKey","value"]},"companyId":{"description":"Company ID within your application","type":"string","minLength":1},"userId":{"description":"User ID within your application","type":"string","minLength":1},"updateEntityFlagsBody":{"description":"Request body for updating flags for an entity","type":"object","properties":{"updates":{"description":"List of flag updates to apply","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/entityFlagUpdate"}},"changeDescription":{"description":"Description of the change for audit history","type":"string"},"notifications":{"description":"Whether to send notifications about the change (default: true)","default":true,"type":"boolean"}},"required":["updates"]},"entityFlagUpdate":{"description":"Update for a single flag's specific targeting","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"value":{"description":"Set to true to add specific targeting, or null to remove it","anyOf":[{"type":"boolean","const":true},{"type":"null"}]}},"required":["flagKey","value"]},"appHeaderWithExpansions":{"description":"App information with possible additional expansions","type":"object","properties":{"org":{"$ref":"#/components/schemas/orgHeader"},"id":{"$ref":"#/components/schemas/appId"},"name":{"description":"App name","type":"string"},"demo":{"description":"Whether the app is a demo app","type":"boolean"},"flagKeyFormat":{"$ref":"#/components/schemas/flagKeyFormat"},"environments":{"description":"Environments within the app (if expanded)","type":"array","items":{"$ref":"#/components/schemas/environmentHeader"}},"stages":{"description":"Stages within the app (if expanded)","type":"array","items":{"$ref":"#/components/schemas/stageHeader"}},"segments":{"description":"Segments within the app (if expanded)","type":"array","items":{"$ref":"#/components/schemas/segmentHeader"}}},"required":["org","id","name","demo","flagKeyFormat"],"additionalProperties":false},"orgHeader":{"description":"Organization's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/orgId"},"name":{"description":"Organization name","type":"string","minLength":1}},"required":["id","name"],"additionalProperties":false},"flagKeyFormat":{"description":"The enforced key format when creating flags","type":"string","enum":["custom","pascalCase","camelCase","snakeCaseUpper","snakeCaseLower","kebabCaseUpper","kebabCaseLower"]},"environmentHeader":{"description":"Basic environment information","type":"object","properties":{"id":{"$ref":"#/components/schemas/envId"},"name":{"description":"Environment name","type":"string"},"isProduction":{"description":"Whether the environment is a production environment","type":"boolean"},"order":{"description":"Environment order in the app (zero-indexed)","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name","isProduction","order"],"additionalProperties":false},"stageHeader":{"description":"Stage's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/stageId"},"name":{"description":"Stage name","type":"string","minLength":1},"color":{"description":"Stage color (HTML color name or hex code)","type":"string","minLength":1,"maxLength":64},"assignedFlagCount":{"description":"Flags currently in this stage","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"order":{"description":"Stage order","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","name","color","assignedFlagCount","order"],"additionalProperties":false},"stageId":{"description":"Stage identifier","type":"string","minLength":1},"segmentHeader":{"description":"Segment's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/segmentId"},"name":{"description":"Segment name","type":"string","minLength":1},"type":{"$ref":"#/components/schemas/segmentType"}},"required":["id","name","type"],"additionalProperties":false},"segmentId":{"description":"Segment identifier","type":"string","minLength":1},"segmentType":{"description":"Segment type","type":"string","enum":["all","custom"]},"ErrorResponse":{"description":"The error response, including individual issues, if applicable","type":"object","properties":{"error":{"description":"The error","type":"object","properties":{"code":{"description":"Error code","type":"string","enum":["invalid_request","not_found","not_possible","not_allowed","not_available","unknown_error","unauthorized","unauthenticated"]},"message":{"description":"Human readable error message","type":"string"}},"required":["code","message"],"additionalProperties":false},"issues":{"description":"Individual validation issues, if applicable","type":"object","propertyNames":{"description":"The field that has the issue (uses dot notation). Empty string if the issue is at the root.","type":"string"},"additionalProperties":{"description":"Error messages for this field","type":"array","items":{"description":"The error message","type":"string"}}}},"required":["error"],"additionalProperties":false},"appHeaderWithExpansionsCollection":{"description":"Collection of App information with possible additional expansions","type":"object","properties":{"data":{"description":"The individual items in the collection","type":"array","items":{"$ref":"#/components/schemas/appHeaderWithExpansions"}}},"required":["data"],"additionalProperties":false},"environmentHeaderSortableCollection":{"description":"Collection of Basic environment information with sorting options","type":"object","properties":{"data":{"description":"The individual items in the collection","type":"array","items":{"$ref":"#/components/schemas/environmentHeader"}},"sortOrder":{"$ref":"#/components/schemas/sortOrder"},"sortBy":{"$ref":"#/components/schemas/environmentHeaderSortByColumn"}},"required":["data","sortOrder","sortBy"],"additionalProperties":false},"environmentDetails":{"description":"Environment details","type":"object","properties":{"id":{"$ref":"#/components/schemas/envId"},"name":{"description":"Environment name","type":"string"},"isProduction":{"description":"Whether the environment is a production environment","type":"boolean"},"order":{"description":"Environment order in the app (zero-indexed)","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sdkAccess":{"description":"SDK access details","type":"object","properties":{"publishableKey":{"description":"Publishable key","type":"string","minLength":1,"maxLength":36},"secretKey":{"description":"Secret key","type":"string","minLength":1,"maxLength":36}},"required":["publishableKey","secretKey"],"additionalProperties":false},"autoFeedbackSurveys":{"description":"Environment-specific auto feedback survey configuration","type":"object","properties":{"snoozePeriodSeconds":{"description":"Snooze period in seconds","default":604800,"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["snoozePeriodSeconds"],"additionalProperties":false},"integrations":{"description":"Environment-specific integration configuration","type":"object","properties":{"slack":{"description":"Default Slack configuration for the environment","type":"object","properties":{"channel":{"$ref":"#/components/schemas/inheritableSlackChannel"}},"required":["channel"],"additionalProperties":false}},"additionalProperties":false}},"required":["id","name","isProduction","order","sdkAccess","autoFeedbackSurveys","integrations"],"additionalProperties":false},"inheritableSlackChannel":{"description":"Slack channel or inherit","anyOf":[{"$ref":"#/components/schemas/slackChannel"},{"type":"string","const":"inherit"}]},"slackChannel":{"description":"Slack channel","type":"object","properties":{"id":{"$ref":"#/components/schemas/slackChannelId"},"name":{"$ref":"#/components/schemas/slackChannelName"}},"required":["id","name"],"additionalProperties":false},"slackChannelId":{"description":"Slack channel identifier","type":"string","minLength":1,"maxLength":128},"slackChannelName":{"description":"Slack channel name","type":"string","minLength":1,"maxLength":128},"flagHeaderCollection":{"description":"Collection response containing flags","type":"object","properties":{"data":{"description":"Page of the collection of flags","type":"array","items":{"$ref":"#/components/schemas/flagHeader"}},"totalCount":{"description":"Total number of flags in collection","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageSize":{"description":"Page size","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageIndex":{"description":"Page index","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"sortBy":{"description":"Sort by","type":"string","enum":["name","key","stage","autoFeedbackSurveysEnabled","createdAt","environmentStatus","owner","lastCheck","lastTrack","stale","archivingChecks"]},"sortOrder":{"description":"Sort order","$ref":"#/components/schemas/sortOrder"}},"required":["data","totalCount","pageSize","pageIndex","sortBy","sortOrder"],"additionalProperties":false},"flagHeader":{"description":"Basic flag information","type":"object","properties":{"id":{"description":"Flag ID","type":"string","minLength":1},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"description":{"description":"Flag description","type":"string"},"stage":{"$ref":"#/components/schemas/stageHeader"},"owner":{"$ref":"#/components/schemas/reflagUserHeader"},"archived":{"description":"Whether the flag is archived","type":"boolean"},"stale":{"description":"Whether the flag is stale","type":"boolean"},"permanent":{"description":"Whether the flag is permanent","type":"boolean"},"createdAt":{"description":"Timestamp when the flag was created","type":"string"},"lastCheckAt":{"description":"Timestamp when the flag was last checked","type":"string"},"lastTrackAt":{"description":"Timestamp when the flag was last tracked","type":"string"}},"required":["id","key","name","archived","stale","permanent"],"additionalProperties":false},"reflagUserHeader":{"description":"Reflag user's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/reflagUserId"},"name":{"description":"User's name","type":"string","minLength":1},"email":{"description":"User's email","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"avatarUrl":{"description":"User's avatar URL","type":"string","format":"uri"}},"required":["id","name","email"],"additionalProperties":false},"reflagUserId":{"description":"Reflag user identifier","type":"string","minLength":1},"flagTargeting":{"description":"Flag targeting information and its audience","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"version":{"$ref":"#/components/schemas/flagVersion"},"updatedAt":{"description":"Last time the targeting was updated","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"specificTargets":{"description":"The flag targeting for each value","type":"object","propertyNames":{"$ref":"#/components/schemas/flagValue"},"additionalProperties":{"$ref":"#/components/schemas/flagValueTargeting"}}},"required":["flagKey","version","updatedAt","specificTargets"],"additionalProperties":false},"flagVersion":{"description":"Flag targeting version","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"flagValue":{"description":"The value of the flag served to the audience.","type":"string","const":"true"},"flagValueTargeting":{"description":"Flag targeting value and its audience","type":"object","properties":{"companyIds":{"description":"Companies that were explicitly given the value","type":"array","items":{"$ref":"#/components/schemas/companyId"}},"userIds":{"description":"Users that were explicitly given the value","type":"array","items":{"$ref":"#/components/schemas/userId"}}},"required":["companyIds","userIds"],"additionalProperties":false},"flagTargetingCollection":{"description":"Collection response containing flags' targeting information","type":"object","properties":{"data":{"description":"Collection of flags' targeting information","type":"array","items":{"$ref":"#/components/schemas/flagTargeting"}}},"required":["data"],"additionalProperties":false},"entityFlagsResponse":{"description":"Response containing flags for an entity","type":"object","properties":{"data":{"description":"List of flags with their enabled status","type":"array","items":{"$ref":"#/components/schemas/entityFlag"}},"totalCount":{"description":"Total number of flags","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageSize":{"description":"Page size","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pageIndex":{"description":"Page index","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["data","totalCount","pageSize","pageIndex"],"additionalProperties":false},"entityFlag":{"description":"Flag information with enabled status for an entity","type":"object","properties":{"id":{"description":"Flag ID","type":"string","minLength":1},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"value":{"description":"Whether the flag is enabled for this entity","type":"boolean"},"specificallyTargetedValue":{"description":"Value if directly added via specific targets, null if not specifically targeted","anyOf":[{"type":"boolean"},{"type":"null"}]},"firstExposureAt":{"description":"First time the entity was exposed to this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastExposureAt":{"description":"Last time the entity was exposed to this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastCheckAt":{"description":"Last time the flag was checked for this entity","anyOf":[{"type":"string"},{"type":"null"}]},"exposureCount":{"description":"Number of times the entity was exposed to this flag","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"firstTrackAt":{"description":"First time a track event was recorded for this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastTrackAt":{"description":"Last time a track event was recorded for this flag","anyOf":[{"type":"string"},{"type":"null"}]},"trackCount":{"description":"Number of track events for this flag","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["id","key","name","value","specificallyTargetedValue","firstExposureAt","lastExposureAt","lastCheckAt","exposureCount","firstTrackAt","lastTrackAt","trackCount"],"additionalProperties":false}},"securitySchemes":{"APIKey":{"type":"http","scheme":"bearer","description":"API key authentication, for service access"}}}} \ No newline at end of file +{"openapi":"3.1.0","info":{"title":"Reflag API","version":"3.0.0","description":"Feature flag management API","contact":{"name":"Reflag Support","url":"https://reflag.com/support"},"license":{"name":"Proprietary","url":"https://reflag.com/"}},"servers":[{"url":"https://app.reflag.com/api","description":"Production server"}],"security":[{"APIKey":[]}],"paths":{"/apps/{appId}":{"get":{"summary":"Get details of an application","description":"Retrieve a specific application by its identifier","operationId":"getApp","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps":{"get":{"summary":"List of applications","description":"Retrieve all accessible applications","operationId":"listApps","parameters":[{"in":"query","name":"orgId","schema":{"$ref":"#/components/schemas/orgId"}}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/appHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/environments":{"get":{"summary":"List environments for application","description":"Retrieve all environments for a specific application","operationId":"listEnvironments","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"query","name":"sortOrder","schema":{"$ref":"#/components/schemas/sortOrder"},"description":"Sort order applied to the sorting column"},{"in":"query","name":"sortBy","schema":{"$ref":"#/components/schemas/environmentHeaderSortByColumn"},"description":"The column to sort by"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/environmentHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/environments/{envId}":{"get":{"summary":"Get environment details","description":"Retrieve details for a specific environment","operationId":"getEnvironment","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/environment"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags":{"get":{"summary":"List flags for application","description":"Retrieve all flags for a specific application","operationId":"listFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"summary":"Create a flag","description":"Create a new flag in the application. Returns the created flag details.","operationId":"createFlag","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"description":"Key of the flag","type":"string","minLength":1},"name":{"description":"Name of the flag","type":"string","minLength":1},"description":{"description":"Description of the flag","anyOf":[{"type":"string","maxLength":8192},{"type":"null"}]},"stageId":{"description":"Stage ID of the flag","type":"string","minLength":14,"maxLength":14},"ownerUserId":{"anyOf":[{"type":"string"},{"type":"null"}]},"permanent":{"default":false,"type":"boolean"},"secret":{"description":"Whether the flag is secret","type":"boolean"}},"required":["key","name"]}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"flag":{"$ref":"#/components/schemas/flag"}},"required":["flag"],"additionalProperties":false}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/{flagId}":{"patch":{"summary":"Update a flag","description":"Update an existing flag","operationId":"updateFlag","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"flagId","schema":{"$ref":"#/components/schemas/flagId"},"required":true,"description":"Flag ID"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"Name of the flag","type":"string","minLength":1},"description":{"description":"Description of the flag","anyOf":[{"type":"string","maxLength":8192},{"type":"null"}]},"ownerUserId":{"anyOf":[{"type":"string"},{"type":"null"}]},"permanent":{"description":"Whether the flag is permanent","type":"boolean"},"secret":{"description":"Whether the flag is secret","type":"boolean"},"isArchived":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"flag":{"$ref":"#/components/schemas/flag"}},"required":["flag"],"additionalProperties":false}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/{flagKey}/targeting/{envId}":{"get":{"summary":"Get flag targeting for an environment","description":"Retrieve targeting for a flag in an environment","operationId":"getFlagTargeting","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"flagKey","schema":{"$ref":"#/components/schemas/flagKey"},"required":true,"description":"Unique flag key"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagTargeting"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/specific-targets/{envId}":{"patch":{"summary":"Update flag specific targets for an environment","description":"Deprecated. Update specific companies and users for flags in an environment. Prefer /apps/{appId}/envs/{envId}/companies/{companyId}/flags and /apps/{appId}/envs/{envId}/users/{userId}/flags.","operationId":"updateBulkFlagSpecificTargets","deprecated":true,"parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/bulkUpdateFlagSpecificTargetsSchema"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagTargetingCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/envs/{envId}/companies/{companyId}/flags":{"get":{"summary":"Get flags for a company","description":"Retrieve all flags with their targeting status for a specific company","operationId":"getCompanyFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"},{"in":"path","name":"companyId","schema":{"$ref":"#/components/schemas/companyId"},"required":true,"description":"Company ID within your application"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Update flag targeting for a company","description":"Update specific targeting for flags for a company in an environment","operationId":"updateCompanyFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"},{"in":"path","name":"companyId","schema":{"$ref":"#/components/schemas/companyId"},"required":true,"description":"Company ID within your application"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/updateEntityFlagsBody"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/envs/{envId}/users/{userId}/flags":{"get":{"summary":"Get flags for a user","description":"Retrieve all flags with their targeting status for a specific user","operationId":"getUserFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"},{"in":"path","name":"userId","schema":{"$ref":"#/components/schemas/userId"},"required":true,"description":"User ID within your application"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Update flag targeting for a user","description":"Update specific targeting for flags for a user in an environment","operationId":"updateUserFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"},{"in":"path","name":"userId","schema":{"$ref":"#/components/schemas/userId"},"required":true,"description":"User ID within your application"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/updateEntityFlagsBody"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"appId":{"description":"App identifier","type":"string","minLength":1},"orgId":{"description":"Organization identifier","type":"string","minLength":1},"sortOrder":{"description":"Sort order applied to the sorting column","default":"asc","type":"string","enum":["asc","desc"]},"environmentHeaderSortByColumn":{"description":"The column to sort by","default":"order","type":"string","enum":["name","order"]},"envId":{"description":"Environment identifier","type":"string","minLength":1},"flagId":{"description":"Flag ID","type":"string","minLength":1},"flagKey":{"description":"Unique flag key","type":"string","minLength":1},"bulkUpdateFlagSpecificTargetsSchema":{"description":"Update the explicit value of multiple flags for a given audience","type":"object","properties":{"updates":{"description":"The list of updates to make to the flags' targeting","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/updateFlagSpecificTargets"}},"notifications":{"description":"Whether to send notifications about the change to configured integration (eg. Slack, Linear, etc). Defaults to true.","default":true,"type":"boolean"},"changeDescription":{"description":"The description of the change recorded in the change history","type":"string"}},"required":["updates"]},"updateFlagSpecificTargets":{"description":"Update the explicit value of the flag for a given audience (null means to remove the flag from the audience).","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"value":{"description":"The value of the flag for the given audience (null means to remove the flag from the audience).","anyOf":[{"type":"string","const":"true"},{"type":"boolean","const":true},{"type":"null"}]},"companyId":{"$ref":"#/components/schemas/companyId"},"userId":{"$ref":"#/components/schemas/userId"}},"required":["flagKey","value"]},"companyId":{"description":"Company ID within your application","type":"string","minLength":1},"userId":{"description":"User ID within your application","type":"string","minLength":1},"updateEntityFlagsBody":{"description":"Request body for updating flags for an entity","type":"object","properties":{"updates":{"description":"List of flag updates to apply","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/entityFlagUpdate"}},"changeDescription":{"description":"Description of the change for audit history","type":"string"},"notifications":{"description":"Whether to send notifications about the change (default: true)","default":true,"type":"boolean"}},"required":["updates"]},"entityFlagUpdate":{"description":"Update for a single flag's explicit targeting override","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"specificallyTargetedValue":{"description":"Set to true to add a specific-targeting override, or null to remove it","anyOf":[{"type":"boolean","const":true},{"type":"null"}]}},"required":["flagKey","specificallyTargetedValue"]},"app":{"description":"App information with related collections","type":"object","properties":{"org":{"$ref":"#/components/schemas/orgHeader"},"id":{"$ref":"#/components/schemas/appId"},"name":{"description":"App name","type":"string"},"demo":{"description":"Whether the app is a demo app","type":"boolean"},"flagKeyFormat":{"$ref":"#/components/schemas/flagKeyFormat"},"environments":{"description":"Environments within the app","type":"array","items":{"$ref":"#/components/schemas/environment"}},"stages":{"description":"Stages within the app","type":"array","items":{"$ref":"#/components/schemas/stageHeader"}},"segments":{"description":"Segments within the app","type":"array","items":{"$ref":"#/components/schemas/segmentHeader"}}},"required":["org","id","name","demo","flagKeyFormat","environments","stages","segments"],"additionalProperties":false},"orgHeader":{"description":"Organization's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/orgId"},"name":{"description":"Organization name","type":"string","minLength":1}},"required":["id","name"],"additionalProperties":false},"flagKeyFormat":{"description":"The enforced key format when creating flags","type":"string","enum":["custom","pascalCase","camelCase","snakeCaseUpper","snakeCaseLower","kebabCaseUpper","kebabCaseLower"]},"environment":{"description":"Environment details","type":"object","properties":{"id":{"$ref":"#/components/schemas/envId"},"name":{"description":"Environment name","type":"string"},"isProduction":{"description":"Whether the environment is a production environment","type":"boolean"},"order":{"description":"Environment order in the app (zero-indexed)","type":"integer"},"sdkAccess":{"description":"SDK access details","type":"object","properties":{"publishableKey":{"description":"Publishable key","type":"string","minLength":1,"maxLength":36},"secretKey":{"description":"Secret key","type":"string","minLength":1,"maxLength":36}},"required":["publishableKey","secretKey"],"additionalProperties":false}},"required":["id","name","isProduction","order","sdkAccess"],"additionalProperties":false},"stageHeader":{"description":"Stage's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/stageId"},"name":{"description":"Stage name","type":"string","minLength":1},"color":{"description":"Stage color (HTML color name or hex code)","type":"string","minLength":1,"maxLength":64},"order":{"description":"Stage order","type":"integer"}},"required":["id","name","color","order"],"additionalProperties":false},"stageId":{"description":"Stage identifier","type":"string","minLength":1},"segmentHeader":{"description":"Segment's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/segmentId"},"name":{"description":"Segment name","type":"string","minLength":1},"type":{"$ref":"#/components/schemas/segmentType"}},"required":["id","name","type"],"additionalProperties":false},"segmentId":{"description":"Segment identifier","type":"string","minLength":1},"segmentType":{"description":"Segment type","type":"string","enum":["all","custom"]},"ErrorResponse":{"description":"The error response, including individual issues, if applicable","type":"object","properties":{"error":{"description":"The error","type":"object","properties":{"code":{"description":"Error code","type":"string","enum":["invalid_request","not_found","not_possible","not_allowed","not_available","unknown_error","unauthorized","unauthenticated"]},"message":{"description":"Human readable error message","type":"string"}},"required":["code","message"],"additionalProperties":false},"issues":{"description":"Individual validation issues, if applicable","type":"object","propertyNames":{"description":"The field that has the issue (uses dot notation). Empty string if the issue is at the root.","type":"string"},"additionalProperties":{"description":"Error messages for this field","type":"array","items":{"description":"The error message","type":"string"}}}},"required":["error"],"additionalProperties":false},"appHeaderCollection":{"description":"Collection of Basic app information","type":"object","properties":{"data":{"description":"The individual items in the collection","type":"array","items":{"$ref":"#/components/schemas/appHeader"}}},"required":["data"],"additionalProperties":false},"appHeader":{"description":"Basic app information","type":"object","properties":{"org":{"$ref":"#/components/schemas/orgHeader"},"id":{"$ref":"#/components/schemas/appId"},"name":{"description":"App name","type":"string"},"demo":{"description":"Whether the app is a demo app","type":"boolean"},"flagKeyFormat":{"$ref":"#/components/schemas/flagKeyFormat"},"environments":{"description":"Environments within the app","type":"array","items":{"$ref":"#/components/schemas/environmentHeader"}}},"required":["org","id","name","demo","flagKeyFormat","environments"],"additionalProperties":false},"environmentHeader":{"description":"Basic environment information","type":"object","properties":{"id":{"$ref":"#/components/schemas/envId"},"name":{"description":"Environment name","type":"string"},"isProduction":{"description":"Whether the environment is a production environment","type":"boolean"},"order":{"description":"Environment order in the app (zero-indexed)","type":"integer"}},"required":["id","name","isProduction","order"],"additionalProperties":false},"environmentHeaderCollection":{"description":"Collection of Basic environment information","type":"object","properties":{"data":{"description":"The individual items in the collection","type":"array","items":{"$ref":"#/components/schemas/environmentHeader"}},"sortOrder":{"$ref":"#/components/schemas/sortOrder"},"sortBy":{"$ref":"#/components/schemas/environmentHeaderSortByColumn"}},"required":["data","sortOrder","sortBy"],"additionalProperties":false},"flagHeaderCollection":{"description":"Collection response containing flags","type":"object","properties":{"data":{"description":"Page of the collection of flags","type":"array","items":{"$ref":"#/components/schemas/flagHeader"}},"totalCount":{"description":"Total number of flags in collection","type":"integer"},"pageSize":{"description":"Page size","type":"integer"},"pageIndex":{"description":"Page index","type":"integer"},"sortBy":{"description":"Sort by","type":"string","enum":["name","key","stage","autoFeedbackSurveysEnabled","createdAt","environmentStatus","owner","lastCheck","lastTrack","stale","archivingChecks"]},"sortOrder":{"description":"Sort order","$ref":"#/components/schemas/sortOrder"}},"required":["data","totalCount","pageSize","pageIndex","sortBy","sortOrder"],"additionalProperties":false},"flagHeader":{"description":"Basic flag information","type":"object","properties":{"id":{"$ref":"#/components/schemas/flagId"},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"description":{"description":"Flag description","type":"string"},"stage":{"$ref":"#/components/schemas/stageHeader"},"owner":{"$ref":"#/components/schemas/reflagUserHeader"},"archived":{"description":"Whether the flag is archived","type":"boolean"},"stale":{"description":"Whether the flag is stale","type":"boolean"},"permanent":{"description":"Whether the flag is permanent","type":"boolean"},"createdAt":{"description":"Timestamp when the flag was created","type":"string"},"lastCheckAt":{"description":"Timestamp when the flag was last checked","type":"string"},"lastTrackAt":{"description":"Timestamp when the flag was last tracked","type":"string"}},"required":["id","key","name","archived","stale","permanent"],"additionalProperties":false},"reflagUserHeader":{"description":"Reflag user's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/reflagUserId"},"name":{"description":"User's name","type":"string","minLength":1},"email":{"description":"User's email","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"avatarUrl":{"description":"User's avatar URL","type":"string","format":"uri"}},"required":["id","name","email"],"additionalProperties":false},"reflagUserId":{"description":"Reflag user identifier","type":"string","minLength":1},"flag":{"description":"Flag","type":"object","properties":{"id":{"$ref":"#/components/schemas/flagId"},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"description":{"description":"Flag description","type":"string"},"stage":{"$ref":"#/components/schemas/stageHeader"},"owner":{"$ref":"#/components/schemas/reflagUserHeader"},"archived":{"description":"Whether the flag is archived","type":"boolean"},"stale":{"description":"Whether the flag is stale","type":"boolean"},"permanent":{"description":"Whether the flag is permanent","type":"boolean"},"createdAt":{"description":"Timestamp when the flag was created","type":"string"},"lastCheckAt":{"description":"Timestamp when the flag was last checked","type":"string"},"lastTrackAt":{"description":"Timestamp when the flag was last tracked","type":"string"},"rolledOutToEveryoneAt":{"description":"Timestamp when the flag was rolled out to everyone","type":"string"},"automatedSurveysEnabled":{"description":"Whether automated surveys are enabled for this flag","type":"boolean"},"parentFlagId":{"description":"Parent flag ID","type":"string"},"feedbackCount":{"description":"Feedback count for this flag","type":"number"}},"required":["id","key","name","archived","stale","permanent"],"additionalProperties":false},"flagTargeting":{"description":"Flag targeting information and its audience","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"version":{"$ref":"#/components/schemas/flagVersion"},"updatedAt":{"description":"Last time the targeting was updated","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"specificTargets":{"description":"The flag targeting for each value","type":"object","propertyNames":{"$ref":"#/components/schemas/flagValue"},"additionalProperties":{"$ref":"#/components/schemas/flagValueTargeting"}}},"required":["flagKey","version","updatedAt","specificTargets"],"additionalProperties":false},"flagVersion":{"description":"Flag targeting version","type":"integer"},"flagValue":{"description":"The value of the flag served to the audience.","type":"string","const":"true"},"flagValueTargeting":{"description":"Flag targeting value and its audience","type":"object","properties":{"companyIds":{"description":"Companies that were explicitly given the value","type":"array","items":{"$ref":"#/components/schemas/companyId"}},"userIds":{"description":"Users that were explicitly given the value","type":"array","items":{"$ref":"#/components/schemas/userId"}}},"required":["companyIds","userIds"],"additionalProperties":false},"flagTargetingCollection":{"description":"Collection response containing flags' targeting information","type":"object","properties":{"data":{"description":"Collection of flags' targeting information","type":"array","items":{"$ref":"#/components/schemas/flagTargeting"}}},"required":["data"],"additionalProperties":false},"entityFlagsResponse":{"description":"Response containing flags for an entity","type":"object","properties":{"data":{"description":"List of flags with their enabled status","type":"array","items":{"$ref":"#/components/schemas/entityFlag"}},"totalCount":{"description":"Total number of flags","type":"integer"},"pageSize":{"description":"Page size","type":"integer"},"pageIndex":{"description":"Page index","type":"integer"}},"required":["data","totalCount","pageSize","pageIndex"],"additionalProperties":false},"entityFlag":{"description":"Flag information with enabled status for an entity","type":"object","properties":{"id":{"$ref":"#/components/schemas/flagId"},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"value":{"description":"Whether the flag is enabled for this entity","type":"boolean"},"specificallyTargetedValue":{"description":"Value if directly added via specific targets, null if not specifically targeted","anyOf":[{"type":"boolean"},{"type":"null"}]},"firstExposureAt":{"description":"First time the entity was exposed to this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastExposureAt":{"description":"Last time the entity was exposed to this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastCheckAt":{"description":"Last time the flag was checked for this entity","anyOf":[{"type":"string"},{"type":"null"}]},"exposureCount":{"description":"Number of times the entity was exposed to this flag","type":"integer"},"firstTrackAt":{"description":"First time a track event was recorded for this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastTrackAt":{"description":"Last time a track event was recorded for this flag","anyOf":[{"type":"string"},{"type":"null"}]},"trackCount":{"description":"Number of track events for this flag","type":"integer"}},"required":["id","key","name","value","specificallyTargetedValue","firstExposureAt","lastExposureAt","lastCheckAt","exposureCount","firstTrackAt","lastTrackAt","trackCount"],"additionalProperties":false}},"securitySchemes":{"APIKey":{"type":"http","scheme":"bearer","description":"API key authentication, for service access"}}}} \ No newline at end of file From 3debc319789ea005a70fa6adc7bc15a806777b5c Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Mon, 2 Mar 2026 20:26:14 +0100 Subject: [PATCH 07/26] Use extends for flattened body params and update demo app for renamed fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Template uses `extends BodyType` instead of iterating body vars, fixing missing nested type imports (e.g. EntityFlagUpdate) - Update demo app for renamed API fields: value → specificTargetValue, specificallyTargetedValue → specificTargetValue - Remove debug console.log from api.ts --- .../customer-admin-panel/app/flags/actions.ts | 4 +-- .../app/flags/company/page.tsx | 2 +- .../app/flags/user/page.tsx | 2 +- .../openapi-templates/apis.mustache | 7 +---- .../src/generated/apis/DefaultApi.ts | 28 +++---------------- 5 files changed, 9 insertions(+), 34 deletions(-) diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts index da5dc670..53ba9c65 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts @@ -86,7 +86,7 @@ export async function toggleUserFlag( await client.updateUserFlags({ envId, userId, - updates: [{ flagKey, value: enabled ? true : null }], + updates: [{ flagKey, specificTargetValue: enabled ? true : null }], }); return await fetchUserFlags(appId, envId, userId); } @@ -126,7 +126,7 @@ export async function toggleCompanyFlag( await client.updateCompanyFlags({ envId, companyId, - updates: [{ flagKey, value: enabled ? true : null }], + updates: [{ flagKey, specificTargetValue: enabled ? true : null }], }); return await fetchCompanyFlags(appId, envId, companyId); } diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx index 4af0a290..a80e4c17 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx @@ -126,7 +126,7 @@ export default async function CompanyFlagsPage({ searchParams }: PageProps) { {flags.map((flag) => { - const isInherited = flag.value && flag.specificallyTargetedValue === null; + const isInherited = flag.value && flag.specificTargetValue === null; const canToggle = !isInherited; const nextValue = !flag.value; diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx index 1953edce..f8e79974 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx @@ -117,7 +117,7 @@ export default async function UserFlagsPage({ searchParams }: PageProps) { {flags.map((flag) => { - const isInherited = flag.value && flag.specificallyTargetedValue === null; + const isInherited = flag.value && flag.specificTargetValue === null; const canToggle = !isInherited; const nextValue = !flag.value; diff --git a/packages/rest-api-sdk/openapi-templates/apis.mustache b/packages/rest-api-sdk/openapi-templates/apis.mustache index 1c1c7a24..33c8ddf6 100644 --- a/packages/rest-api-sdk/openapi-templates/apis.mustache +++ b/packages/rest-api-sdk/openapi-templates/apis.mustache @@ -23,13 +23,8 @@ import { {{#operations}} {{#operation}} {{#allParams.0}} -export interface {{#prefixParameterInterfaces}}{{classname}}{{/prefixParameterInterfaces}}{{operationIdCamelCase}}Request { +export interface {{#prefixParameterInterfaces}}{{classname}}{{/prefixParameterInterfaces}}{{operationIdCamelCase}}Request {{#bodyParam}}extends {{{dataType}}} {{/bodyParam}}{ {{#allParams}} - {{#isBodyParam}} - {{#vars}} - {{name}}{{^required}}?{{/required}}: {{{dataType}}}{{#isNullable}} | null{{/isNullable}}; - {{/vars}} - {{/isBodyParam}} {{^isBodyParam}} {{paramName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{#hasReadOnly}}Omit<{{{dataType}}}, {{#readOnlyVars}}'{{baseName}}'{{^-last}}|{{/-last}}{{/readOnlyVars}}>{{/hasReadOnly}}{{^hasReadOnly}}{{{dataType}}}{{/hasReadOnly}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}; {{/isBodyParam}} diff --git a/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts b/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts index 1e5b49e1..b2b1452e 100644 --- a/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts +++ b/packages/rest-api-sdk/src/generated/apis/DefaultApi.ts @@ -61,15 +61,8 @@ import { UpdateFlagRequestToJSON, } from '../models/index'; -export interface CreateFlagOperationRequest { +export interface CreateFlagOperationRequest extends CreateFlagRequest { appId: string; - key: string; - name: string; - description?: string | null; - stageId?: string; - ownerUserId?: string | null; - permanent?: boolean; - secret?: boolean; } export interface GetAppRequest { @@ -113,34 +106,21 @@ export interface ListFlagsRequest { appId: string; } -export interface UpdateCompanyFlagsRequest { +export interface UpdateCompanyFlagsRequest extends UpdateEntityFlagsBody { appId: string; envId: string; companyId: string; - updates: Array; - changeDescription?: string; - notifications?: boolean; } -export interface UpdateFlagOperationRequest { +export interface UpdateFlagOperationRequest extends UpdateFlagRequest { appId: string; flagId: string; - name?: string; - description?: string | null; - ownerUserId?: string | null; - permanent?: boolean; - secret?: boolean; - isArchived?: boolean; - stageId?: string; } -export interface UpdateUserFlagsRequest { +export interface UpdateUserFlagsRequest extends UpdateEntityFlagsBody { appId: string; envId: string; userId: string; - updates: Array; - changeDescription?: string; - notifications?: boolean; } /** From 6dac9b8b34eb9c43c4e9dd88bb50a0793ccb01d7 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Mon, 2 Mar 2026 20:35:33 +0100 Subject: [PATCH 08/26] Improve demo app flag toggling UX - Show "Yes"/"Yes (implicitly)"/"No" instead of checkboxes - Hide action button for inherited flags - Add revalidatePath to fix stale data after toggle - Remove redundant re-fetch in toggle actions --- .../customer-admin-panel/app/flags/actions.ts | 6 ++-- .../app/flags/company/page.tsx | 30 +++++++++---------- .../app/flags/user/page.tsx | 30 +++++++++---------- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts index 53ba9c65..70b22ebd 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/actions.ts @@ -75,7 +75,7 @@ export async function toggleUserFlag( userId: string, flagKey: string, enabled: boolean, -): Promise { +): Promise { if (!envId) { throw new Error("envId is required"); } @@ -88,7 +88,6 @@ export async function toggleUserFlag( userId, updates: [{ flagKey, specificTargetValue: enabled ? true : null }], }); - return await fetchUserFlags(appId, envId, userId); } export async function fetchCompanyFlags( @@ -115,7 +114,7 @@ export async function toggleCompanyFlag( companyId: string, flagKey: string, enabled: boolean, -): Promise { +): Promise { if (!envId) { throw new Error("envId is required"); } @@ -128,5 +127,4 @@ export async function toggleCompanyFlag( companyId, updates: [{ flagKey, specificTargetValue: enabled ? true : null }], }); - return await fetchCompanyFlags(appId, envId, companyId); } diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx index a80e4c17..f17668d0 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx @@ -1,5 +1,6 @@ import Link from "next/link"; import { redirect } from "next/navigation"; +import { revalidatePath } from "next/cache"; import { fetchCompanyFlags, listApps, @@ -54,6 +55,7 @@ export default async function CompanyFlagsPage({ searchParams }: PageProps) { await toggleCompanyFlag(appId, envId, nextCompanyId, flagKey, nextValue); const query = new URLSearchParams({ appId, envId, companyId: nextCompanyId }); + revalidatePath("/flags/company"); redirect(`/flags/company?${query.toString()}`); } @@ -120,35 +122,33 @@ export default async function CompanyFlagsPage({ searchParams }: PageProps) { Flag Key Enabled - Status Action {flags.map((flag) => { const isInherited = flag.value && flag.specificTargetValue === null; - const canToggle = !isInherited; - const nextValue = !flag.value; return ( {flag.name} {flag.key} - + {flag.value ? (isInherited ? "Yes (implicitly)" : "Yes") : "No"} - {isInherited ? "Inherited" : "Targeted"} -
- - - - - - -
+ {!isInherited && ( +
+ + + + + + +
+ )} ); diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx index f8e79974..565d14b6 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx @@ -1,5 +1,6 @@ import Link from "next/link"; import { redirect } from "next/navigation"; +import { revalidatePath } from "next/cache"; import { fetchUserFlags, listApps, listEnvironments, toggleUserFlag } from "../actions"; type QueryValue = string | string[] | undefined; @@ -49,6 +50,7 @@ export default async function UserFlagsPage({ searchParams }: PageProps) { await toggleUserFlag(appId, envId, nextUserId, flagKey, nextValue); const query = new URLSearchParams({ appId, envId, userId: nextUserId }); + revalidatePath("/flags/user"); redirect(`/flags/user?${query.toString()}`); } @@ -111,35 +113,33 @@ export default async function UserFlagsPage({ searchParams }: PageProps) { Flag Key Enabled - Status Action {flags.map((flag) => { const isInherited = flag.value && flag.specificTargetValue === null; - const canToggle = !isInherited; - const nextValue = !flag.value; return ( {flag.name} {flag.key} - + {flag.value ? (isInherited ? "Yes (implicitly)" : "Yes") : "No"} - {isInherited ? "Inherited" : "Targeted"} -
- - - - - - -
+ {!isInherited && ( +
+ + + + + + +
+ )} ); From 6400100db070e7712ec0df505dc8a6282aad8857 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Mon, 2 Mar 2026 20:40:43 +0100 Subject: [PATCH 09/26] Update README for renamed specificTargetValue field and remove basePath comment --- packages/rest-api-sdk/README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/rest-api-sdk/README.md b/packages/rest-api-sdk/README.md index 8a6815a7..f2548301 100644 --- a/packages/rest-api-sdk/README.md +++ b/packages/rest-api-sdk/README.md @@ -19,8 +19,6 @@ import { Api } from "@reflag/rest-api-sdk"; const api = new Api({ accessToken: process.env.REFLAG_API_KEY, - // Optional when using non-default host: - // basePath: "https://app.reflag.com/api", }); ``` @@ -128,7 +126,7 @@ await api.updateUserFlags({ appId: "app-123", envId: "env-456", userId: "user-1", - updates: [{ flagKey: "new-checkout", value: true }], + updates: [{ flagKey: "new-checkout", specificTargetValue: true }], }); ``` @@ -145,7 +143,7 @@ await api.updateCompanyFlags({ appId: "app-123", envId: "env-456", companyId: "company-1", - updates: [{ flagKey: "new-checkout", value: null }], + updates: [{ flagKey: "new-checkout", specificTargetValue: null }], }); ``` From db2dbed5303ca6fe12114f4a6dcf064c52ad661e Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 10:49:24 +0100 Subject: [PATCH 10/26] fix rest-api-sdk workflow regressions and app-scoped middleware chaining --- .../customer-admin-panel/package.json | 2 +- packages/rest-api-sdk/package.json | 2 +- packages/rest-api-sdk/src/api.ts | 25 ++++++- .../rest-api-sdk/test/createAppClient.test.ts | 74 +++++++++++++++++++ packages/rest-api-sdk/tsconfig.eslint.json | 2 +- 5 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 packages/rest-api-sdk/test/createAppClient.test.ts diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/package.json b/packages/rest-api-sdk/examples/customer-admin-panel/package.json index 2282b7a7..ccab0eba 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/package.json +++ b/packages/rest-api-sdk/examples/customer-admin-panel/package.json @@ -6,7 +6,7 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "echo 'No lint configured for customer-admin-panel example; skipping.'" }, "dependencies": { "@reflag/rest-api-sdk": "workspace:*", diff --git a/packages/rest-api-sdk/package.json b/packages/rest-api-sdk/package.json index 0e46cc96..6c8f8e28 100644 --- a/packages/rest-api-sdk/package.json +++ b/packages/rest-api-sdk/package.json @@ -10,7 +10,7 @@ "scripts": { "generate": "rm -rf src/generated && npx @openapitools/openapi-generator-cli generate -c openapi-generator.config.yaml", "build": "yarn generate && tsc --project tsconfig.build.json", - "test": "vitest run", + "test": "vitest run --passWithNoTests", "test:watch": "vitest", "test:ci": "yarn test --reporter=default --reporter=junit --outputFile=junit.xml", "lint": "eslint .", diff --git a/packages/rest-api-sdk/src/api.ts b/packages/rest-api-sdk/src/api.ts index 07de67ca..e24e8a87 100644 --- a/packages/rest-api-sdk/src/api.ts +++ b/packages/rest-api-sdk/src/api.ts @@ -98,29 +98,48 @@ export class Api extends DefaultApi { function scopeApiToAppId( api: T, appId: string, + scopedCache: WeakMap = new WeakMap(), ): AppScopedApi { - return new Proxy(api, { + const cached = scopedCache.get(api as object); + if (cached) { + return cached as AppScopedApi; + } + + const scopedApi = new Proxy(api, { get(target, prop, receiver) { const value = Reflect.get(target, prop, receiver); if (typeof value !== "function") return value; + + const wrapResult = (result: unknown) => { + if (result instanceof Api) { + return scopeApiToAppId(result, appId, scopedCache); + } + return result; + }; + return (arg1: unknown, ...rest: unknown[]) => { if (arg1 && typeof arg1 === "object" && !Array.isArray(arg1)) { const args = "appId" in (arg1 as object) ? arg1 : { ...(arg1 as object), appId }; - return (value as (...args: unknown[]) => unknown).call( + const result = (value as (...args: unknown[]) => unknown).call( target, args, ...rest, ); + return wrapResult(result); } - return (value as (...args: unknown[]) => unknown).call( + const result = (value as (...args: unknown[]) => unknown).call( target, arg1, ...rest, ); + return wrapResult(result); }; }, }) as AppScopedApi; + + scopedCache.set(api as object, scopedApi as object); + return scopedApi; } export function createAppClient( diff --git a/packages/rest-api-sdk/test/createAppClient.test.ts b/packages/rest-api-sdk/test/createAppClient.test.ts new file mode 100644 index 00000000..50954f12 --- /dev/null +++ b/packages/rest-api-sdk/test/createAppClient.test.ts @@ -0,0 +1,74 @@ +import { describe, expect, it, vi } from "vitest"; + +import { createAppClient } from "../src/api"; +import type { Middleware } from "../src/generated/runtime"; + +const listFlagsResponse = { + data: [], + totalCount: 0, + pageSize: 0, + pageIndex: 0, + sortBy: "name", + sortOrder: "asc", +}; + +type ScopedListFlagsClient = { + listFlags(args: Record): Promise; +}; + +describe("createAppClient", () => { + it("preserves app scoping after withPreMiddleware", async () => { + const fetchApi = vi.fn( + async (_input: RequestInfo | URL, _init?: RequestInit) => + new Response(JSON.stringify(listFlagsResponse), { + status: 200, + headers: { "Content-Type": "application/json" }, + }), + ); + const client = createAppClient("app-123", { + basePath: "https://example.test", + fetchApi, + }); + + const withPre = client.withPreMiddleware(async ({ url, init }) => ({ + url, + init, + })); + + await expect( + (withPre as unknown as ScopedListFlagsClient).listFlags({}), + ).resolves.toEqual(listFlagsResponse); + expect(fetchApi).toHaveBeenCalledTimes(1); + expect(String(fetchApi.mock.calls[0]?.[0])).toBe( + "https://example.test/apps/app-123/flags", + ); + }); + + it("preserves app scoping after withMiddleware", async () => { + const fetchApi = vi.fn( + async (_input: RequestInfo | URL, _init?: RequestInit) => + new Response(JSON.stringify(listFlagsResponse), { + status: 200, + headers: { "Content-Type": "application/json" }, + }), + ); + const client = createAppClient("app-123", { + basePath: "https://example.test", + fetchApi, + }); + const middleware: Middleware = { + pre: vi.fn(async ({ url, init }) => ({ url, init })), + }; + + const withMiddleware = client.withMiddleware(middleware); + + await expect( + (withMiddleware as unknown as ScopedListFlagsClient).listFlags({}), + ).resolves.toEqual(listFlagsResponse); + expect(middleware.pre).toHaveBeenCalledTimes(1); + expect(fetchApi).toHaveBeenCalledTimes(1); + expect(String(fetchApi.mock.calls[0]?.[0])).toBe( + "https://example.test/apps/app-123/flags", + ); + }); +}); diff --git a/packages/rest-api-sdk/tsconfig.eslint.json b/packages/rest-api-sdk/tsconfig.eslint.json index 66a0c293..8f51c5d7 100644 --- a/packages/rest-api-sdk/tsconfig.eslint.json +++ b/packages/rest-api-sdk/tsconfig.eslint.json @@ -1,4 +1,4 @@ { "extends": "./tsconfig.json", - "include": ["./src", "./*.ts"] + "include": ["./src", "./test", "./*.ts"] } From d85dfd59fded69b3169db8507ca17b404d362478 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 12:44:00 +0100 Subject: [PATCH 11/26] docs: mark REST API SDK as beta --- README.md | 6 ++++++ packages/rest-api-sdk/README.md | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d6485d56..ca1dbbb1 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,12 @@ Use this for Cloudflare Workers as well. [Read the docs](packages/node-sdk/README.md) +## REST API SDK (beta) + +Typed Node/browser SDK for Reflag's REST API. + +[Read the docs](packages/rest-api-sdk/README.md) + ## Reflag CLI CLI to interact with Reflag and generate types diff --git a/packages/rest-api-sdk/README.md b/packages/rest-api-sdk/README.md index f2548301..317ffb19 100644 --- a/packages/rest-api-sdk/README.md +++ b/packages/rest-api-sdk/README.md @@ -1,4 +1,4 @@ -# @reflag/rest-api-sdk +# @reflag/rest-api-sdk (beta) Typed Node/browser SDK for Reflag's REST API. From f8eec77b03913fe0f046d069a84a55456f20a17a Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 12:52:31 +0100 Subject: [PATCH 12/26] chore: remove reflag-openapi spec file --- reflag-openapi.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 reflag-openapi.json diff --git a/reflag-openapi.json b/reflag-openapi.json deleted file mode 100644 index 8051a5d8..00000000 --- a/reflag-openapi.json +++ /dev/null @@ -1 +0,0 @@ -{"openapi":"3.1.0","info":{"title":"Reflag API","version":"3.0.0","description":"Feature flag management API","contact":{"name":"Reflag Support","url":"https://reflag.com/support"},"license":{"name":"Proprietary","url":"https://reflag.com/"}},"servers":[{"url":"https://app.reflag.com/api","description":"Production server"}],"security":[{"APIKey":[]}],"paths":{"/apps/{appId}":{"get":{"summary":"Get details of an application","description":"Retrieve a specific application by its identifier","operationId":"getApp","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps":{"get":{"summary":"List of applications","description":"Retrieve all accessible applications","operationId":"listApps","parameters":[{"in":"query","name":"orgId","schema":{"$ref":"#/components/schemas/orgId"}}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/appHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/environments":{"get":{"summary":"List environments for application","description":"Retrieve all environments for a specific application","operationId":"listEnvironments","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"query","name":"sortOrder","schema":{"$ref":"#/components/schemas/sortOrder"},"description":"Sort order applied to the sorting column"},{"in":"query","name":"sortBy","schema":{"$ref":"#/components/schemas/environmentHeaderSortByColumn"},"description":"The column to sort by"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/environmentHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/environments/{envId}":{"get":{"summary":"Get environment details","description":"Retrieve details for a specific environment","operationId":"getEnvironment","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/environment"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags":{"get":{"summary":"List flags for application","description":"Retrieve all flags for a specific application","operationId":"listFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagHeaderCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"summary":"Create a flag","description":"Create a new flag in the application. Returns the created flag details.","operationId":"createFlag","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"description":"Key of the flag","type":"string","minLength":1},"name":{"description":"Name of the flag","type":"string","minLength":1},"description":{"description":"Description of the flag","anyOf":[{"type":"string","maxLength":8192},{"type":"null"}]},"stageId":{"description":"Stage ID of the flag","type":"string","minLength":14,"maxLength":14},"ownerUserId":{"anyOf":[{"type":"string"},{"type":"null"}]},"permanent":{"default":false,"type":"boolean"},"secret":{"description":"Whether the flag is secret","type":"boolean"}},"required":["key","name"]}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"flag":{"$ref":"#/components/schemas/flag"}},"required":["flag"],"additionalProperties":false}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/{flagId}":{"patch":{"summary":"Update a flag","description":"Update an existing flag","operationId":"updateFlag","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"flagId","schema":{"$ref":"#/components/schemas/flagId"},"required":true,"description":"Flag ID"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"Name of the flag","type":"string","minLength":1},"description":{"description":"Description of the flag","anyOf":[{"type":"string","maxLength":8192},{"type":"null"}]},"ownerUserId":{"anyOf":[{"type":"string"},{"type":"null"}]},"permanent":{"description":"Whether the flag is permanent","type":"boolean"},"secret":{"description":"Whether the flag is secret","type":"boolean"},"isArchived":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"flag":{"$ref":"#/components/schemas/flag"}},"required":["flag"],"additionalProperties":false}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/{flagKey}/targeting/{envId}":{"get":{"summary":"Get flag targeting for an environment","description":"Retrieve targeting for a flag in an environment","operationId":"getFlagTargeting","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"flagKey","schema":{"$ref":"#/components/schemas/flagKey"},"required":true,"description":"Unique flag key"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagTargeting"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/flags/specific-targets/{envId}":{"patch":{"summary":"Update flag specific targets for an environment","description":"Deprecated. Update specific companies and users for flags in an environment. Prefer /apps/{appId}/envs/{envId}/companies/{companyId}/flags and /apps/{appId}/envs/{envId}/users/{userId}/flags.","operationId":"updateBulkFlagSpecificTargets","deprecated":true,"parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/bulkUpdateFlagSpecificTargetsSchema"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/flagTargetingCollection"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/envs/{envId}/companies/{companyId}/flags":{"get":{"summary":"Get flags for a company","description":"Retrieve all flags with their targeting status for a specific company","operationId":"getCompanyFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"},{"in":"path","name":"companyId","schema":{"$ref":"#/components/schemas/companyId"},"required":true,"description":"Company ID within your application"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Update flag targeting for a company","description":"Update specific targeting for flags for a company in an environment","operationId":"updateCompanyFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"},{"in":"path","name":"companyId","schema":{"$ref":"#/components/schemas/companyId"},"required":true,"description":"Company ID within your application"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/updateEntityFlagsBody"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/apps/{appId}/envs/{envId}/users/{userId}/flags":{"get":{"summary":"Get flags for a user","description":"Retrieve all flags with their targeting status for a specific user","operationId":"getUserFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"},{"in":"path","name":"userId","schema":{"$ref":"#/components/schemas/userId"},"required":true,"description":"User ID within your application"}],"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Update flag targeting for a user","description":"Update specific targeting for flags for a user in an environment","operationId":"updateUserFlags","parameters":[{"in":"path","name":"appId","schema":{"$ref":"#/components/schemas/appId"},"required":true,"description":"App identifier"},{"in":"path","name":"envId","schema":{"$ref":"#/components/schemas/envId"},"required":true,"description":"Environment identifier"},{"in":"path","name":"userId","schema":{"$ref":"#/components/schemas/userId"},"required":true,"description":"User ID within your application"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/updateEntityFlagsBody"}}}},"responses":{"200":{"description":"Requested resource retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/entityFlagsResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Requested resource, or its parent, not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"appId":{"description":"App identifier","type":"string","minLength":1},"orgId":{"description":"Organization identifier","type":"string","minLength":1},"sortOrder":{"description":"Sort order applied to the sorting column","default":"asc","type":"string","enum":["asc","desc"]},"environmentHeaderSortByColumn":{"description":"The column to sort by","default":"order","type":"string","enum":["name","order"]},"envId":{"description":"Environment identifier","type":"string","minLength":1},"flagId":{"description":"Flag ID","type":"string","minLength":1},"flagKey":{"description":"Unique flag key","type":"string","minLength":1},"bulkUpdateFlagSpecificTargetsSchema":{"description":"Update the explicit value of multiple flags for a given audience","type":"object","properties":{"updates":{"description":"The list of updates to make to the flags' targeting","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/updateFlagSpecificTargets"}},"notifications":{"description":"Whether to send notifications about the change to configured integration (eg. Slack, Linear, etc). Defaults to true.","default":true,"type":"boolean"},"changeDescription":{"description":"The description of the change recorded in the change history","type":"string"}},"required":["updates"]},"updateFlagSpecificTargets":{"description":"Update the explicit value of the flag for a given audience (null means to remove the flag from the audience).","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"value":{"description":"The value of the flag for the given audience (null means to remove the flag from the audience).","anyOf":[{"type":"string","const":"true"},{"type":"boolean","const":true},{"type":"null"}]},"companyId":{"$ref":"#/components/schemas/companyId"},"userId":{"$ref":"#/components/schemas/userId"}},"required":["flagKey","value"]},"companyId":{"description":"Company ID within your application","type":"string","minLength":1},"userId":{"description":"User ID within your application","type":"string","minLength":1},"updateEntityFlagsBody":{"description":"Request body for updating flags for an entity","type":"object","properties":{"updates":{"description":"List of flag updates to apply","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/entityFlagUpdate"}},"changeDescription":{"description":"Description of the change for audit history","type":"string"},"notifications":{"description":"Whether to send notifications about the change (default: true)","default":true,"type":"boolean"}},"required":["updates"]},"entityFlagUpdate":{"description":"Update for a single flag's explicit targeting override","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"specificallyTargetedValue":{"description":"Set to true to add a specific-targeting override, or null to remove it","anyOf":[{"type":"boolean","const":true},{"type":"null"}]}},"required":["flagKey","specificallyTargetedValue"]},"app":{"description":"App information with related collections","type":"object","properties":{"org":{"$ref":"#/components/schemas/orgHeader"},"id":{"$ref":"#/components/schemas/appId"},"name":{"description":"App name","type":"string"},"demo":{"description":"Whether the app is a demo app","type":"boolean"},"flagKeyFormat":{"$ref":"#/components/schemas/flagKeyFormat"},"environments":{"description":"Environments within the app","type":"array","items":{"$ref":"#/components/schemas/environment"}},"stages":{"description":"Stages within the app","type":"array","items":{"$ref":"#/components/schemas/stageHeader"}},"segments":{"description":"Segments within the app","type":"array","items":{"$ref":"#/components/schemas/segmentHeader"}}},"required":["org","id","name","demo","flagKeyFormat","environments","stages","segments"],"additionalProperties":false},"orgHeader":{"description":"Organization's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/orgId"},"name":{"description":"Organization name","type":"string","minLength":1}},"required":["id","name"],"additionalProperties":false},"flagKeyFormat":{"description":"The enforced key format when creating flags","type":"string","enum":["custom","pascalCase","camelCase","snakeCaseUpper","snakeCaseLower","kebabCaseUpper","kebabCaseLower"]},"environment":{"description":"Environment details","type":"object","properties":{"id":{"$ref":"#/components/schemas/envId"},"name":{"description":"Environment name","type":"string"},"isProduction":{"description":"Whether the environment is a production environment","type":"boolean"},"order":{"description":"Environment order in the app (zero-indexed)","type":"integer"},"sdkAccess":{"description":"SDK access details","type":"object","properties":{"publishableKey":{"description":"Publishable key","type":"string","minLength":1,"maxLength":36},"secretKey":{"description":"Secret key","type":"string","minLength":1,"maxLength":36}},"required":["publishableKey","secretKey"],"additionalProperties":false}},"required":["id","name","isProduction","order","sdkAccess"],"additionalProperties":false},"stageHeader":{"description":"Stage's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/stageId"},"name":{"description":"Stage name","type":"string","minLength":1},"color":{"description":"Stage color (HTML color name or hex code)","type":"string","minLength":1,"maxLength":64},"order":{"description":"Stage order","type":"integer"}},"required":["id","name","color","order"],"additionalProperties":false},"stageId":{"description":"Stage identifier","type":"string","minLength":1},"segmentHeader":{"description":"Segment's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/segmentId"},"name":{"description":"Segment name","type":"string","minLength":1},"type":{"$ref":"#/components/schemas/segmentType"}},"required":["id","name","type"],"additionalProperties":false},"segmentId":{"description":"Segment identifier","type":"string","minLength":1},"segmentType":{"description":"Segment type","type":"string","enum":["all","custom"]},"ErrorResponse":{"description":"The error response, including individual issues, if applicable","type":"object","properties":{"error":{"description":"The error","type":"object","properties":{"code":{"description":"Error code","type":"string","enum":["invalid_request","not_found","not_possible","not_allowed","not_available","unknown_error","unauthorized","unauthenticated"]},"message":{"description":"Human readable error message","type":"string"}},"required":["code","message"],"additionalProperties":false},"issues":{"description":"Individual validation issues, if applicable","type":"object","propertyNames":{"description":"The field that has the issue (uses dot notation). Empty string if the issue is at the root.","type":"string"},"additionalProperties":{"description":"Error messages for this field","type":"array","items":{"description":"The error message","type":"string"}}}},"required":["error"],"additionalProperties":false},"appHeaderCollection":{"description":"Collection of Basic app information","type":"object","properties":{"data":{"description":"The individual items in the collection","type":"array","items":{"$ref":"#/components/schemas/appHeader"}}},"required":["data"],"additionalProperties":false},"appHeader":{"description":"Basic app information","type":"object","properties":{"org":{"$ref":"#/components/schemas/orgHeader"},"id":{"$ref":"#/components/schemas/appId"},"name":{"description":"App name","type":"string"},"demo":{"description":"Whether the app is a demo app","type":"boolean"},"flagKeyFormat":{"$ref":"#/components/schemas/flagKeyFormat"},"environments":{"description":"Environments within the app","type":"array","items":{"$ref":"#/components/schemas/environmentHeader"}}},"required":["org","id","name","demo","flagKeyFormat","environments"],"additionalProperties":false},"environmentHeader":{"description":"Basic environment information","type":"object","properties":{"id":{"$ref":"#/components/schemas/envId"},"name":{"description":"Environment name","type":"string"},"isProduction":{"description":"Whether the environment is a production environment","type":"boolean"},"order":{"description":"Environment order in the app (zero-indexed)","type":"integer"}},"required":["id","name","isProduction","order"],"additionalProperties":false},"environmentHeaderCollection":{"description":"Collection of Basic environment information","type":"object","properties":{"data":{"description":"The individual items in the collection","type":"array","items":{"$ref":"#/components/schemas/environmentHeader"}},"sortOrder":{"$ref":"#/components/schemas/sortOrder"},"sortBy":{"$ref":"#/components/schemas/environmentHeaderSortByColumn"}},"required":["data","sortOrder","sortBy"],"additionalProperties":false},"flagHeaderCollection":{"description":"Collection response containing flags","type":"object","properties":{"data":{"description":"Page of the collection of flags","type":"array","items":{"$ref":"#/components/schemas/flagHeader"}},"totalCount":{"description":"Total number of flags in collection","type":"integer"},"pageSize":{"description":"Page size","type":"integer"},"pageIndex":{"description":"Page index","type":"integer"},"sortBy":{"description":"Sort by","type":"string","enum":["name","key","stage","autoFeedbackSurveysEnabled","createdAt","environmentStatus","owner","lastCheck","lastTrack","stale","archivingChecks"]},"sortOrder":{"description":"Sort order","$ref":"#/components/schemas/sortOrder"}},"required":["data","totalCount","pageSize","pageIndex","sortBy","sortOrder"],"additionalProperties":false},"flagHeader":{"description":"Basic flag information","type":"object","properties":{"id":{"$ref":"#/components/schemas/flagId"},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"description":{"description":"Flag description","type":"string"},"stage":{"$ref":"#/components/schemas/stageHeader"},"owner":{"$ref":"#/components/schemas/reflagUserHeader"},"archived":{"description":"Whether the flag is archived","type":"boolean"},"stale":{"description":"Whether the flag is stale","type":"boolean"},"permanent":{"description":"Whether the flag is permanent","type":"boolean"},"createdAt":{"description":"Timestamp when the flag was created","type":"string"},"lastCheckAt":{"description":"Timestamp when the flag was last checked","type":"string"},"lastTrackAt":{"description":"Timestamp when the flag was last tracked","type":"string"}},"required":["id","key","name","archived","stale","permanent"],"additionalProperties":false},"reflagUserHeader":{"description":"Reflag user's basic information","type":"object","properties":{"id":{"$ref":"#/components/schemas/reflagUserId"},"name":{"description":"User's name","type":"string","minLength":1},"email":{"description":"User's email","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"avatarUrl":{"description":"User's avatar URL","type":"string","format":"uri"}},"required":["id","name","email"],"additionalProperties":false},"reflagUserId":{"description":"Reflag user identifier","type":"string","minLength":1},"flag":{"description":"Flag","type":"object","properties":{"id":{"$ref":"#/components/schemas/flagId"},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"description":{"description":"Flag description","type":"string"},"stage":{"$ref":"#/components/schemas/stageHeader"},"owner":{"$ref":"#/components/schemas/reflagUserHeader"},"archived":{"description":"Whether the flag is archived","type":"boolean"},"stale":{"description":"Whether the flag is stale","type":"boolean"},"permanent":{"description":"Whether the flag is permanent","type":"boolean"},"createdAt":{"description":"Timestamp when the flag was created","type":"string"},"lastCheckAt":{"description":"Timestamp when the flag was last checked","type":"string"},"lastTrackAt":{"description":"Timestamp when the flag was last tracked","type":"string"},"rolledOutToEveryoneAt":{"description":"Timestamp when the flag was rolled out to everyone","type":"string"},"automatedSurveysEnabled":{"description":"Whether automated surveys are enabled for this flag","type":"boolean"},"parentFlagId":{"description":"Parent flag ID","type":"string"},"feedbackCount":{"description":"Feedback count for this flag","type":"number"}},"required":["id","key","name","archived","stale","permanent"],"additionalProperties":false},"flagTargeting":{"description":"Flag targeting information and its audience","type":"object","properties":{"flagKey":{"$ref":"#/components/schemas/flagKey"},"version":{"$ref":"#/components/schemas/flagVersion"},"updatedAt":{"description":"Last time the targeting was updated","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"},"specificTargets":{"description":"The flag targeting for each value","type":"object","propertyNames":{"$ref":"#/components/schemas/flagValue"},"additionalProperties":{"$ref":"#/components/schemas/flagValueTargeting"}}},"required":["flagKey","version","updatedAt","specificTargets"],"additionalProperties":false},"flagVersion":{"description":"Flag targeting version","type":"integer"},"flagValue":{"description":"The value of the flag served to the audience.","type":"string","const":"true"},"flagValueTargeting":{"description":"Flag targeting value and its audience","type":"object","properties":{"companyIds":{"description":"Companies that were explicitly given the value","type":"array","items":{"$ref":"#/components/schemas/companyId"}},"userIds":{"description":"Users that were explicitly given the value","type":"array","items":{"$ref":"#/components/schemas/userId"}}},"required":["companyIds","userIds"],"additionalProperties":false},"flagTargetingCollection":{"description":"Collection response containing flags' targeting information","type":"object","properties":{"data":{"description":"Collection of flags' targeting information","type":"array","items":{"$ref":"#/components/schemas/flagTargeting"}}},"required":["data"],"additionalProperties":false},"entityFlagsResponse":{"description":"Response containing flags for an entity","type":"object","properties":{"data":{"description":"List of flags with their enabled status","type":"array","items":{"$ref":"#/components/schemas/entityFlag"}},"totalCount":{"description":"Total number of flags","type":"integer"},"pageSize":{"description":"Page size","type":"integer"},"pageIndex":{"description":"Page index","type":"integer"}},"required":["data","totalCount","pageSize","pageIndex"],"additionalProperties":false},"entityFlag":{"description":"Flag information with enabled status for an entity","type":"object","properties":{"id":{"$ref":"#/components/schemas/flagId"},"key":{"$ref":"#/components/schemas/flagKey"},"name":{"description":"Flag name","type":"string","minLength":1,"maxLength":255},"value":{"description":"Whether the flag is enabled for this entity","type":"boolean"},"specificallyTargetedValue":{"description":"Value if directly added via specific targets, null if not specifically targeted","anyOf":[{"type":"boolean"},{"type":"null"}]},"firstExposureAt":{"description":"First time the entity was exposed to this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastExposureAt":{"description":"Last time the entity was exposed to this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastCheckAt":{"description":"Last time the flag was checked for this entity","anyOf":[{"type":"string"},{"type":"null"}]},"exposureCount":{"description":"Number of times the entity was exposed to this flag","type":"integer"},"firstTrackAt":{"description":"First time a track event was recorded for this flag","anyOf":[{"type":"string"},{"type":"null"}]},"lastTrackAt":{"description":"Last time a track event was recorded for this flag","anyOf":[{"type":"string"},{"type":"null"}]},"trackCount":{"description":"Number of track events for this flag","type":"integer"}},"required":["id","key","name","value","specificallyTargetedValue","firstExposureAt","lastExposureAt","lastCheckAt","exposureCount","firstTrackAt","lastTrackAt","trackCount"],"additionalProperties":false}},"securitySchemes":{"APIKey":{"type":"http","scheme":"bearer","description":"API key authentication, for service access"}}}} \ No newline at end of file From e21c2bf58d90aa2e0fbb5048cd3bf3a8adaa1a53 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 12:59:02 +0100 Subject: [PATCH 13/26] show demo image --- .../examples/customer-admin-panel/README.md | 2 ++ .../docs/company-flags-screenshot.png | Bin 0 -> 202656 bytes 2 files changed, 2 insertions(+) create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/docs/company-flags-screenshot.png diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/README.md b/packages/rest-api-sdk/examples/customer-admin-panel/README.md index fbfe9de8..dd7a1c7e 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/README.md +++ b/packages/rest-api-sdk/examples/customer-admin-panel/README.md @@ -2,6 +2,8 @@ Small Next.js (App Router) app that uses `@reflag/rest-api-sdk` with server actions to view and toggle flags for users and companies. +![Company Flags Screenshot](docs/company-flags-screenshot.png) + ## Setup Create a `.env.local` file in this folder with: diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/docs/company-flags-screenshot.png b/packages/rest-api-sdk/examples/customer-admin-panel/docs/company-flags-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..f2f208b50f52289b28281d34d20f748e8bdfa637 GIT binary patch literal 202656 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 literal 0 HcmV?d00001 From 94d32ee76fe2c586d3e10957f630ae9d6e4f0b5a Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 13:14:19 +0100 Subject: [PATCH 14/26] feat(example): auto-apply app/env filters in customer admin panel --- .../examples/customer-admin-panel/README.md | 3 +- .../app/flags/AppEnvForm.tsx | 66 + .../app/flags/EntityFlagsFilterForm.tsx | 85 + .../app/flags/company/page.tsx | 57 +- .../customer-admin-panel/app/flags/page.tsx | 39 +- .../app/flags/user/page.tsx | 55 +- yarn.lock | 1652 ++++++++++++++++- 7 files changed, 1795 insertions(+), 162 deletions(-) create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/app/flags/AppEnvForm.tsx create mode 100644 packages/rest-api-sdk/examples/customer-admin-panel/app/flags/EntityFlagsFilterForm.tsx diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/README.md b/packages/rest-api-sdk/examples/customer-admin-panel/README.md index dd7a1c7e..666f9af5 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/README.md +++ b/packages/rest-api-sdk/examples/customer-admin-panel/README.md @@ -21,5 +21,4 @@ yarn dev ``` Visit: -- http://localhost:3000/flags/user -- http://localhost:3000/flags/company +- http://localhost:3000/ diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/AppEnvForm.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/AppEnvForm.tsx new file mode 100644 index 00000000..c971fa26 --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/AppEnvForm.tsx @@ -0,0 +1,66 @@ +"use client"; + +type Option = { + id: string; + name: string; +}; + +type AppEnvFormProps = { + apps: Option[]; + envs: Option[]; + selectedAppId: string; + selectedEnvId: string; +}; + +export default function AppEnvForm({ + apps, + envs, + selectedAppId, + selectedEnvId, +}: AppEnvFormProps) { + return ( +

{ + if (event.target instanceof HTMLSelectElement) { + event.currentTarget.requestSubmit(); + } + }} + > + + + +
+ ); +} diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/EntityFlagsFilterForm.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/EntityFlagsFilterForm.tsx new file mode 100644 index 00000000..f20f1012 --- /dev/null +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/EntityFlagsFilterForm.tsx @@ -0,0 +1,85 @@ +"use client"; + +type Option = { + id: string; + name: string; +}; + +type EntityFlagsFilterFormProps = { + action: "/flags/user" | "/flags/company"; + apps: Option[]; + envs: Option[]; + selectedAppId: string; + selectedEnvId: string; + entityIdName: "userId" | "companyId"; + entityIdLabel: string; + entityIdValue: string; + submitLabel: string; +}; + +export default function EntityFlagsFilterForm({ + action, + apps, + envs, + selectedAppId, + selectedEnvId, + entityIdName, + entityIdLabel, + entityIdValue, + submitLabel, +}: EntityFlagsFilterFormProps) { + return ( +
{ + if (event.target instanceof HTMLSelectElement) { + event.currentTarget.requestSubmit(); + } + }} + > + + + +
+ + +
+
+ ); +} diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx index f17668d0..ac393dcf 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx @@ -1,6 +1,7 @@ import Link from "next/link"; import { redirect } from "next/navigation"; import { revalidatePath } from "next/cache"; +import EntityFlagsFilterForm from "../EntityFlagsFilterForm"; import { fetchCompanyFlags, listApps, @@ -64,53 +65,17 @@ export default async function CompanyFlagsPage({ searchParams }: PageProps) { Back to flags

Company Flags

-
- - - -
- - -
-
+ apps={apps} + envs={envs} + selectedAppId={selectedAppId} + selectedEnvId={selectedEnvId} + entityIdName="companyId" + entityIdLabel="Company ID" + entityIdValue={companyId} + submitLabel="Load company flags" + /> {!companyId ?

Enter a company ID to load flags.

: null} {companyId && flags.length === 0 ?

No flags loaded.

: null} diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/page.tsx index 6b702fba..cf71c56b 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/page.tsx +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/page.tsx @@ -1,4 +1,5 @@ import Link from "next/link"; +import AppEnvForm from "./AppEnvForm"; import { listApps, listEnvironments, listFlags } from "./actions"; type QueryValue = string | string[] | undefined; @@ -34,38 +35,12 @@ export default async function FlagsPage({ searchParams }: PageProps) {

Flags

-
- - -
- -
-
+
Back to flags

User Flags

- - - - -
- - -
-
+ {!userId ?

Enter a user ID to load flags.

: null} {userId && flags.length === 0 ?

No flags loaded.

: null} diff --git a/yarn.lock b/yarn.lock index ee752c53..37290f7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1735,6 +1735,13 @@ __metadata: languageName: node linkType: hard +"@borewit/text-codec@npm:^0.2.1": + version: 0.2.1 + resolution: "@borewit/text-codec@npm:0.2.1" + checksum: 10c0/aabd9c86497197aacc9ddb413857f112a98a9fd4be9ed56a24971a47bbec7c0d5d449efcad830f9895009c1a5914e5c448f972a0c968e97c4ebf99297dea7a6b + languageName: node + linkType: hard + "@bundled-es-modules/cookie@npm:^2.0.0": version: 2.0.0 resolution: "@bundled-es-modules/cookie@npm:2.0.0" @@ -2195,6 +2202,15 @@ __metadata: languageName: node linkType: hard +"@emnapi/runtime@npm:^1.7.0": + version: 1.8.1 + resolution: "@emnapi/runtime@npm:1.8.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/f4929d75e37aafb24da77d2f58816761fe3f826aad2e37fa6d4421dac9060cbd5098eea1ac3c9ecc4526b89deb58153852fa432f87021dc57863f2ff726d713f + languageName: node + linkType: hard + "@esbuild/aix-ppc64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/aix-ppc64@npm:0.21.5" @@ -2567,6 +2583,13 @@ __metadata: languageName: node linkType: hard +"@eslint-community/regexpp@npm:^4.6.1": + version: 4.12.2 + resolution: "@eslint-community/regexpp@npm:4.12.2" + checksum: 10c0/fddcbc66851b308478d04e302a4d771d6917a0b3740dc351513c0da9ca2eab8a1adf99f5e0aa7ab8b13fa0df005c81adeee7e63a92f3effd7d367a163b721c2d + languageName: node + linkType: hard + "@eslint/config-array@npm:^0.19.2": version: 0.19.2 resolution: "@eslint/config-array@npm:0.19.2" @@ -2616,6 +2639,23 @@ __metadata: languageName: node linkType: hard +"@eslint/eslintrc@npm:^2.1.4": + version: 2.1.4 + resolution: "@eslint/eslintrc@npm:2.1.4" + dependencies: + ajv: "npm:^6.12.4" + debug: "npm:^4.3.2" + espree: "npm:^9.6.0" + globals: "npm:^13.19.0" + ignore: "npm:^5.2.0" + import-fresh: "npm:^3.2.1" + js-yaml: "npm:^4.1.0" + minimatch: "npm:^3.1.2" + strip-json-comments: "npm:^3.1.1" + checksum: 10c0/32f67052b81768ae876c84569ffd562491ec5a5091b0c1e1ca1e0f3c24fb42f804952fdd0a137873bc64303ba368a71ba079a6f691cee25beee9722d94cc8573 + languageName: node + linkType: hard + "@eslint/eslintrc@npm:^3.3.0": version: 3.3.0 resolution: "@eslint/eslintrc@npm:3.3.0" @@ -2650,6 +2690,13 @@ __metadata: languageName: node linkType: hard +"@eslint/js@npm:8.57.1": + version: 8.57.1 + resolution: "@eslint/js@npm:8.57.1" + checksum: 10c0/b489c474a3b5b54381c62e82b3f7f65f4b8a5eaaed126546520bf2fede5532a8ed53212919fed1e9048dcf7f37167c8561d58d0ba4492a4244004e7793805223 + languageName: node + linkType: hard + "@eslint/js@npm:9.21.0, @eslint/js@npm:^9.21.0": version: 9.21.0 resolution: "@eslint/js@npm:9.21.0" @@ -3166,6 +3213,17 @@ __metadata: languageName: node linkType: hard +"@humanwhocodes/config-array@npm:^0.13.0": + version: 0.13.0 + resolution: "@humanwhocodes/config-array@npm:0.13.0" + dependencies: + "@humanwhocodes/object-schema": "npm:^2.0.3" + debug: "npm:^4.3.1" + minimatch: "npm:^3.0.5" + checksum: 10c0/205c99e756b759f92e1f44a3dc6292b37db199beacba8f26c2165d4051fe73a4ae52fdcfd08ffa93e7e5cb63da7c88648f0e84e197d154bbbbe137b2e0dd332e + languageName: node + linkType: hard + "@humanwhocodes/module-importer@npm:^1.0.1": version: 1.0.1 resolution: "@humanwhocodes/module-importer@npm:1.0.1" @@ -3173,6 +3231,13 @@ __metadata: languageName: node linkType: hard +"@humanwhocodes/object-schema@npm:^2.0.3": + version: 2.0.3 + resolution: "@humanwhocodes/object-schema@npm:2.0.3" + checksum: 10c0/80520eabbfc2d32fe195a93557cef50dfe8c8905de447f022675aaf66abc33ae54098f5ea78548d925aa671cd4ab7c7daa5ad704fe42358c9b5e7db60f80696c + languageName: node + linkType: hard + "@humanwhocodes/retry@npm:^0.3.0": version: 0.3.1 resolution: "@humanwhocodes/retry@npm:0.3.1" @@ -3194,6 +3259,233 @@ __metadata: languageName: node linkType: hard +"@img/colour@npm:^1.0.0": + version: 1.1.0 + resolution: "@img/colour@npm:1.1.0" + checksum: 10c0/2ebea2c0bbaee73b99badcefa04e1e71d83f36e5369337d3121dca841f4569533c4e2faddda6d62dd247f0d5cca143711f9446c59bcce81e427ba433a7a94a17 + languageName: node + linkType: hard + +"@img/sharp-darwin-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-darwin-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-darwin-arm64": + optional: true + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-darwin-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-darwin-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-darwin-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-darwin-x64": + optional: true + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-darwin-arm64@npm:1.2.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-darwin-x64@npm:1.2.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-arm64@npm:1.2.4" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-arm@npm:1.2.4" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-ppc64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-ppc64@npm:1.2.4" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-riscv64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-riscv64@npm:1.2.4" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-s390x@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-s390x@npm:1.2.4" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-x64@npm:1.2.4" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.2.4" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linux-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-arm@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-arm@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-arm": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-arm": + optional: true + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-ppc64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-ppc64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-ppc64": + optional: true + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-riscv64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-riscv64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-riscv64": + optional: true + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-s390x@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-s390x@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-s390x": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-s390x": + optional: true + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linuxmusl-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linuxmusl-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-wasm32@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-wasm32@npm:0.34.5" + dependencies: + "@emnapi/runtime": "npm:^1.7.0" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@img/sharp-win32-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-arm64@npm:0.34.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-win32-ia32@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-ia32@npm:0.34.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@img/sharp-win32-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-x64@npm:0.34.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@inquirer/ansi@npm:^1.0.1": version: 1.0.1 resolution: "@inquirer/ansi@npm:1.0.1" @@ -3265,6 +3557,28 @@ __metadata: languageName: node linkType: hard +"@inquirer/core@npm:^6.0.0": + version: 6.0.0 + resolution: "@inquirer/core@npm:6.0.0" + dependencies: + "@inquirer/type": "npm:^1.1.6" + "@types/mute-stream": "npm:^0.0.4" + "@types/node": "npm:^20.10.7" + "@types/wrap-ansi": "npm:^3.0.0" + ansi-escapes: "npm:^4.3.2" + chalk: "npm:^4.1.2" + cli-spinners: "npm:^2.9.2" + cli-width: "npm:^4.1.0" + figures: "npm:^3.2.0" + mute-stream: "npm:^1.0.0" + run-async: "npm:^3.0.0" + signal-exit: "npm:^4.1.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^6.2.0" + checksum: 10c0/0663330936c9baea58d8a10e93de6c3446ab84ed909c41d7b3f6762842473b8f88e10d776326d89a278abfb3c4083240d0f5876293908eb1005d0026aa2cfb7d + languageName: node + linkType: hard + "@inquirer/core@npm:^9.0.5": version: 9.0.5 resolution: "@inquirer/core@npm:9.0.5" @@ -3449,6 +3763,19 @@ __metadata: languageName: node linkType: hard +"@inquirer/select@npm:1.3.3": + version: 1.3.3 + resolution: "@inquirer/select@npm:1.3.3" + dependencies: + "@inquirer/core": "npm:^6.0.0" + "@inquirer/type": "npm:^1.1.6" + ansi-escapes: "npm:^4.3.2" + chalk: "npm:^4.1.2" + figures: "npm:^3.2.0" + checksum: 10c0/695de7dc85bf1b4ae4d13bbacb39e73cf4ff12f04da5cff4f0cc046db6bb32ff6051d30753a94299370908051133535e0db7e011e3b61e9806908eb1a7ef6b39 + languageName: node + linkType: hard + "@inquirer/select@npm:^4.4.0": version: 4.4.0 resolution: "@inquirer/select@npm:4.4.0" @@ -3467,6 +3794,15 @@ __metadata: languageName: node linkType: hard +"@inquirer/type@npm:^1.1.6": + version: 1.5.5 + resolution: "@inquirer/type@npm:1.5.5" + dependencies: + mute-stream: "npm:^1.0.0" + checksum: 10c0/4c41736c09ba9426b5a9e44993bdd54e8f532e791518802e33866f233a2a6126a25c1c82c19d1abbf1df627e57b1b957dd3f8318ea96073d8bfc32193943bcb3 + languageName: node + linkType: hard + "@inquirer/type@npm:^1.5.1": version: 1.5.1 resolution: "@inquirer/type@npm:1.5.1" @@ -3834,6 +4170,13 @@ __metadata: languageName: node linkType: hard +"@lukeed/csprng@npm:^1.0.0": + version: 1.1.0 + resolution: "@lukeed/csprng@npm:1.1.0" + checksum: 10c0/5d6dcf478af732972083ab2889c294b57f1028fa13c2c240d7a4aaa079c2c75df7ef0dcbdda5419147fc6704b4adf96b2de92f1a9a72ac21c6350c4014fffe6c + languageName: node + linkType: hard + "@mdn/browser-compat-data@npm:^5.3.13, @mdn/browser-compat-data@npm:^5.6.19": version: 5.7.6 resolution: "@mdn/browser-compat-data@npm:5.7.6" @@ -4028,6 +4371,68 @@ __metadata: languageName: node linkType: hard +"@nestjs/axios@npm:4.0.1": + version: 4.0.1 + resolution: "@nestjs/axios@npm:4.0.1" + peerDependencies: + "@nestjs/common": ^10.0.0 || ^11.0.0 + axios: ^1.3.1 + rxjs: ^7.0.0 + checksum: 10c0/6290f6ceaab2ea1000ee705dfd81da8cd776eb9fb388287a74a4faecf0afd8a8cdef7f6cc3afe2a9b10e47067df027280ca1ddb75b7885ff365e2faed37b10c7 + languageName: node + linkType: hard + +"@nestjs/common@npm:11.1.14": + version: 11.1.14 + resolution: "@nestjs/common@npm:11.1.14" + dependencies: + file-type: "npm:21.3.0" + iterare: "npm:1.2.1" + load-esm: "npm:1.0.3" + tslib: "npm:2.8.1" + uid: "npm:2.0.2" + peerDependencies: + class-transformer: ">=0.4.1" + class-validator: ">=0.13.2" + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + checksum: 10c0/bba35fabdc350dfd362009e15a2cb6b660a53325ee6c1863f04667df0201b51b334226ace2985505969727aaf5b494396dd33f682ce75d0f3b1cab1d206a11e9 + languageName: node + linkType: hard + +"@nestjs/core@npm:11.1.14": + version: 11.1.14 + resolution: "@nestjs/core@npm:11.1.14" + dependencies: + "@nuxt/opencollective": "npm:0.4.1" + fast-safe-stringify: "npm:2.1.1" + iterare: "npm:1.2.1" + path-to-regexp: "npm:8.3.0" + tslib: "npm:2.8.1" + uid: "npm:2.0.2" + peerDependencies: + "@nestjs/common": ^11.0.0 + "@nestjs/microservices": ^11.0.0 + "@nestjs/platform-express": ^11.0.0 + "@nestjs/websockets": ^11.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + "@nestjs/microservices": + optional: true + "@nestjs/platform-express": + optional: true + "@nestjs/websockets": + optional: true + checksum: 10c0/842c8048fb83bc6df70975d91ec357bacb6d2cc42791e709d122fda16c6343d279cf9f4ec88c5427b02de5c3f40f3271b9c62367c662c39cdad838b5962669e9 + languageName: node + linkType: hard + "@next/env@npm:14.2.26": version: 14.2.26 resolution: "@next/env@npm:14.2.26" @@ -4035,6 +4440,27 @@ __metadata: languageName: node linkType: hard +"@next/env@npm:14.2.5": + version: 14.2.5 + resolution: "@next/env@npm:14.2.5" + checksum: 10c0/63d8b88ac450b3c37940a9e2119a63a1074aca89908574ade6157a8aa295275dcb3ac5f69e00883fc55d0f12963b73b74e87ba32a5768a489f9609c6be57b699 + languageName: node + linkType: hard + +"@next/env@npm:15.5.10": + version: 15.5.10 + resolution: "@next/env@npm:15.5.10" + checksum: 10c0/fb26c299ff388a3a0b2d14379616e3e59fe7960002e5aa67be4d195a377b9803fb93dd90067a5eb7d889d6f437aff091171701eec0e8d201666160269dda4e95 + languageName: node + linkType: hard + +"@next/env@npm:16.1.5": + version: 16.1.5 + resolution: "@next/env@npm:16.1.5" + checksum: 10c0/9d6442bee75386593d5da6e952146cf3c4338202e68a0ba9464d70c24ab7abb0c7e4ecd0ab661ca211e544fe7d6bd075270379bee3e97c5da3b6d082f95a04cd + languageName: node + linkType: hard + "@next/eslint-plugin-next@npm:14.2.5": version: 14.2.5 resolution: "@next/eslint-plugin-next@npm:14.2.5" @@ -4051,6 +4477,27 @@ __metadata: languageName: node linkType: hard +"@next/swc-darwin-arm64@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-darwin-arm64@npm:14.2.5" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-darwin-arm64@npm:15.5.7": + version: 15.5.7 + resolution: "@next/swc-darwin-arm64@npm:15.5.7" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-darwin-arm64@npm:16.1.5": + version: 16.1.5 + resolution: "@next/swc-darwin-arm64@npm:16.1.5" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@next/swc-darwin-x64@npm:14.2.26": version: 14.2.26 resolution: "@next/swc-darwin-x64@npm:14.2.26" @@ -4058,6 +4505,27 @@ __metadata: languageName: node linkType: hard +"@next/swc-darwin-x64@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-darwin-x64@npm:14.2.5" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@next/swc-darwin-x64@npm:15.5.7": + version: 15.5.7 + resolution: "@next/swc-darwin-x64@npm:15.5.7" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@next/swc-darwin-x64@npm:16.1.5": + version: 16.1.5 + resolution: "@next/swc-darwin-x64@npm:16.1.5" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@next/swc-linux-arm64-gnu@npm:14.2.26": version: 14.2.26 resolution: "@next/swc-linux-arm64-gnu@npm:14.2.26" @@ -4065,6 +4533,27 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-arm64-gnu@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-linux-arm64-gnu@npm:14.2.5" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-arm64-gnu@npm:15.5.7": + version: 15.5.7 + resolution: "@next/swc-linux-arm64-gnu@npm:15.5.7" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-arm64-gnu@npm:16.1.5": + version: 16.1.5 + resolution: "@next/swc-linux-arm64-gnu@npm:16.1.5" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@next/swc-linux-arm64-musl@npm:14.2.26": version: 14.2.26 resolution: "@next/swc-linux-arm64-musl@npm:14.2.26" @@ -4072,6 +4561,27 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-arm64-musl@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-linux-arm64-musl@npm:14.2.5" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-linux-arm64-musl@npm:15.5.7": + version: 15.5.7 + resolution: "@next/swc-linux-arm64-musl@npm:15.5.7" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-linux-arm64-musl@npm:16.1.5": + version: 16.1.5 + resolution: "@next/swc-linux-arm64-musl@npm:16.1.5" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "@next/swc-linux-x64-gnu@npm:14.2.26": version: 14.2.26 resolution: "@next/swc-linux-x64-gnu@npm:14.2.26" @@ -4079,6 +4589,27 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-x64-gnu@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-linux-x64-gnu@npm:14.2.5" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-x64-gnu@npm:15.5.7": + version: 15.5.7 + resolution: "@next/swc-linux-x64-gnu@npm:15.5.7" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-x64-gnu@npm:16.1.5": + version: 16.1.5 + resolution: "@next/swc-linux-x64-gnu@npm:16.1.5" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@next/swc-linux-x64-musl@npm:14.2.26": version: 14.2.26 resolution: "@next/swc-linux-x64-musl@npm:14.2.26" @@ -4086,6 +4617,27 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-x64-musl@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-linux-x64-musl@npm:14.2.5" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-linux-x64-musl@npm:15.5.7": + version: 15.5.7 + resolution: "@next/swc-linux-x64-musl@npm:15.5.7" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-linux-x64-musl@npm:16.1.5": + version: 16.1.5 + resolution: "@next/swc-linux-x64-musl@npm:16.1.5" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "@next/swc-win32-arm64-msvc@npm:14.2.26": version: 14.2.26 resolution: "@next/swc-win32-arm64-msvc@npm:14.2.26" @@ -4093,6 +4645,27 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-arm64-msvc@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-win32-arm64-msvc@npm:14.2.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-win32-arm64-msvc@npm:15.5.7": + version: 15.5.7 + resolution: "@next/swc-win32-arm64-msvc@npm:15.5.7" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-win32-arm64-msvc@npm:16.1.5": + version: 16.1.5 + resolution: "@next/swc-win32-arm64-msvc@npm:16.1.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@next/swc-win32-ia32-msvc@npm:14.2.26": version: 14.2.26 resolution: "@next/swc-win32-ia32-msvc@npm:14.2.26" @@ -4100,6 +4673,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-ia32-msvc@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-win32-ia32-msvc@npm:14.2.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@next/swc-win32-x64-msvc@npm:14.2.26": version: 14.2.26 resolution: "@next/swc-win32-x64-msvc@npm:14.2.26" @@ -4107,6 +4687,27 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-x64-msvc@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-win32-x64-msvc@npm:14.2.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@next/swc-win32-x64-msvc@npm:15.5.7": + version: 15.5.7 + resolution: "@next/swc-win32-x64-msvc@npm:15.5.7" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@next/swc-win32-x64-msvc@npm:16.1.5": + version: 16.1.5 + resolution: "@next/swc-win32-x64-msvc@npm:16.1.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@nicolo-ribaudo/eslint-scope-5-internals@npm:5.1.1-v1": version: 5.1.1-v1 resolution: "@nicolo-ribaudo/eslint-scope-5-internals@npm:5.1.1-v1" @@ -4133,7 +4734,7 @@ __metadata: languageName: node linkType: hard -"@nodelib/fs.walk@npm:^1.2.3": +"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": version: 1.2.8 resolution: "@nodelib/fs.walk@npm:1.2.8" dependencies: @@ -4285,6 +4886,30 @@ __metadata: languageName: node linkType: hard +"@nuxt/opencollective@npm:0.4.1": + version: 0.4.1 + resolution: "@nuxt/opencollective@npm:0.4.1" + dependencies: + consola: "npm:^3.2.3" + bin: + opencollective: bin/opencollective.js + checksum: 10c0/ef2835d8635d2928152eff8b5a1ec42c145e2ab00cb02ff4bb61f0a6f5528afc9b169c06c32308c783779fe26855ebc67419743046caa80e582e814cff73187d + languageName: node + linkType: hard + +"@nuxtjs/opencollective@npm:0.3.2": + version: 0.3.2 + resolution: "@nuxtjs/opencollective@npm:0.3.2" + dependencies: + chalk: "npm:^4.1.0" + consola: "npm:^2.15.0" + node-fetch: "npm:^2.6.1" + bin: + opencollective: bin/opencollective.js + checksum: 10c0/540268687af3289ff107585484d42201b404cdbb98b3a512487c12a6b180a8f0e1df0d701df47d3d9e0d5c0f6eb3252d80535562aedca9edf52cf7fd17ae4601 + languageName: node + linkType: hard + "@nx/devkit@npm:19.0.4, @nx/devkit@npm:>=17.1.2 < 20": version: 19.0.4 resolution: "@nx/devkit@npm:19.0.4" @@ -4557,6 +5182,33 @@ __metadata: languageName: node linkType: hard +"@openapitools/openapi-generator-cli@npm:^2.13.5": + version: 2.30.0 + resolution: "@openapitools/openapi-generator-cli@npm:2.30.0" + dependencies: + "@inquirer/select": "npm:1.3.3" + "@nestjs/axios": "npm:4.0.1" + "@nestjs/common": "npm:11.1.14" + "@nestjs/core": "npm:11.1.14" + "@nuxtjs/opencollective": "npm:0.3.2" + axios: "npm:1.13.5" + chalk: "npm:4.1.2" + commander: "npm:8.3.0" + compare-versions: "npm:6.1.1" + concurrently: "npm:9.2.1" + console.table: "npm:0.10.0" + fs-extra: "npm:11.3.3" + glob: "npm:13.0.6" + proxy-agent: "npm:6.5.0" + reflect-metadata: "npm:0.2.2" + rxjs: "npm:7.8.2" + tslib: "npm:2.8.1" + bin: + openapi-generator-cli: main.js + checksum: 10c0/dc0830dfafee27a4243eb3cce58ee179fea44adbb05889ab5d95a7123679a34b47c90c776dabae42ce7932d3919ea1f9d8da2773bbbd0a742cb60332e11293ea + languageName: node + linkType: hard + "@openfeature/core@npm:1.3.0": version: 1.3.0 resolution: "@openfeature/core@npm:1.3.0" @@ -5149,7 +5801,7 @@ __metadata: languageName: node linkType: hard -"@reflag/node-sdk@workspace:packages/node-sdk": +"@reflag/node-sdk@workspace:^, @reflag/node-sdk@workspace:packages/node-sdk": version: 0.0.0-use.local resolution: "@reflag/node-sdk@workspace:packages/node-sdk" dependencies: @@ -5273,6 +5925,22 @@ __metadata: languageName: unknown linkType: soft +"@reflag/rest-api-sdk@workspace:*, @reflag/rest-api-sdk@workspace:packages/rest-api-sdk": + version: 0.0.0-use.local + resolution: "@reflag/rest-api-sdk@workspace:packages/rest-api-sdk" + dependencies: + "@openapitools/openapi-generator-cli": "npm:^2.13.5" + "@reflag/eslint-config": "npm:~0.0.2" + "@reflag/tsconfig": "npm:~0.0.2" + "@types/node": "npm:^22.12.0" + "@vitest/coverage-v8": "npm:~1.6.0" + eslint: "npm:^9.21.0" + prettier: "npm:^3.5.2" + typescript: "npm:^5.7.3" + vitest: "npm:~1.6.0" + languageName: unknown + linkType: soft + "@reflag/tsconfig@npm:0.0.2, @reflag/tsconfig@npm:^0.0.2, @reflag/tsconfig@npm:~0.0.2, @reflag/tsconfig@workspace:packages/tsconfig": version: 0.0.0-use.local resolution: "@reflag/tsconfig@workspace:packages/tsconfig" @@ -5869,6 +6537,15 @@ __metadata: languageName: node linkType: hard +"@swc/helpers@npm:0.5.15": + version: 0.5.15 + resolution: "@swc/helpers@npm:0.5.15" + dependencies: + tslib: "npm:^2.8.0" + checksum: 10c0/33002f74f6f885f04c132960835fdfc474186983ea567606db62e86acd0680ca82f34647e8e610f4e1e422d1c16fce729dde22cd3b797ab1fd9061a825dabca4 + languageName: node + linkType: hard + "@swc/helpers@npm:0.5.5": version: 0.5.5 resolution: "@swc/helpers@npm:0.5.5" @@ -5913,6 +6590,23 @@ __metadata: languageName: node linkType: hard +"@tokenizer/inflate@npm:^0.4.1": + version: 0.4.1 + resolution: "@tokenizer/inflate@npm:0.4.1" + dependencies: + debug: "npm:^4.4.3" + token-types: "npm:^6.1.1" + checksum: 10c0/9817516efe21d1ce3bdfb80a1f94efc8981064ce3873448ba79f4d81d96c0694c484c289bd042d346ae5536cf77f5aa9a367d39c3df700eb610761b7c306b4de + languageName: node + linkType: hard + +"@tokenizer/token@npm:^0.3.0": + version: 0.3.0 + resolution: "@tokenizer/token@npm:0.3.0" + checksum: 10c0/7ab9a822d4b5ff3f5bca7f7d14d46bdd8432528e028db4a52be7fbf90c7f495cc1af1324691dda2813c6af8dc4b8eb29de3107d4508165f9aa5b53e7d501f155 + languageName: node + linkType: hard + "@tootallnate/once@npm:2": version: 2.0.0 resolution: "@tootallnate/once@npm:2.0.0" @@ -5920,6 +6614,13 @@ __metadata: languageName: node linkType: hard +"@tootallnate/quickjs-emscripten@npm:^0.23.0": + version: 0.23.0 + resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" + checksum: 10c0/2a939b781826fb5fd3edd0f2ec3b321d259d760464cf20611c9877205aaca3ccc0b7304dea68416baa0d568e82cd86b17d29548d1e5139fa3155a4a86a2b4b49 + languageName: node + linkType: hard + "@tsconfig/node10@npm:^1.0.7": version: 1.0.8 resolution: "@tsconfig/node10@npm:1.0.8" @@ -6248,6 +6949,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^20.10.7": + version: 20.19.35 + resolution: "@types/node@npm:20.19.35" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10c0/e27d3e1eac4105b627fdba2d86577e603c66fc7b4ad61418d41f3898d84b2f5ff3e0c6b6d3948b34976ea91056e6359740b387edcc65ca516c3f2b5a0c0f9c6a + languageName: node + linkType: hard + "@types/node@npm:^20.14.11": version: 20.14.11 resolution: "@types/node@npm:20.14.11" @@ -6622,7 +7332,7 @@ __metadata: languageName: node linkType: hard -"@ungap/structured-clone@npm:^1.3.0": +"@ungap/structured-clone@npm:^1.2.0, @ungap/structured-clone@npm:^1.3.0": version: 1.3.0 resolution: "@ungap/structured-clone@npm:1.3.0" checksum: 10c0/0fc3097c2540ada1fc340ee56d58d96b5b536a2a0dab6e3ec17d4bfc8c4c86db345f61a375a8185f9da96f01c69678f836a2b57eeaa9e4b8eeafd26428e57b0a @@ -8264,6 +8974,15 @@ __metadata: languageName: node linkType: hard +"ast-types@npm:^0.13.4": + version: 0.13.4 + resolution: "ast-types@npm:0.13.4" + dependencies: + tslib: "npm:^2.0.1" + checksum: 10c0/3a1a409764faa1471601a0ad01b3aa699292991aa9c8a30c7717002cabdf5d98008e7b53ae61f6e058f757fc6ba965e147967a93c13e62692c907d79cfb245f8 + languageName: node + linkType: hard + "astral-regex@npm:^1.0.0": version: 1.0.0 resolution: "astral-regex@npm:1.0.0" @@ -8342,6 +9061,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:1.13.5": + version: 1.13.5 + resolution: "axios@npm:1.13.5" + dependencies: + follow-redirects: "npm:^1.15.11" + form-data: "npm:^4.0.5" + proxy-from-env: "npm:^1.1.0" + checksum: 10c0/abf468c34f2d145f3dc7dbc0f1be67e520630624307bda69a41bbe8d386bd672d87b4405c4ee77f9ff54b235ab02f96a9968fb00e75b13ce64706e352a3068fd + languageName: node + linkType: hard + "axios@npm:^1.6.0": version: 1.8.4 resolution: "axios@npm:1.8.4" @@ -8557,6 +9287,13 @@ __metadata: languageName: node linkType: hard +"balanced-match@npm:^4.0.2": + version: 4.0.4 + resolution: "balanced-match@npm:4.0.4" + checksum: 10c0/07e86102a3eb2ee2a6a1a89164f29d0dbaebd28f2ca3f5ca786f36b8b23d9e417eb3be45a4acf754f837be5ac0a2317de90d3fcb7f4f4dc95720a1f36b26a17b + languageName: node + linkType: hard + "base64-js@npm:^1.2.3, base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -8564,6 +9301,15 @@ __metadata: languageName: node linkType: hard +"baseline-browser-mapping@npm:^2.8.3": + version: 2.10.0 + resolution: "baseline-browser-mapping@npm:2.10.0" + bin: + baseline-browser-mapping: dist/cli.cjs + checksum: 10c0/da9c3ec0fcd7f325226a47d2142794d41706b6e0a405718a2c15410bbdb72aacadd65738bedef558c6f1b106ed19458cb25b06f63b66df2c284799905dbbd003 + languageName: node + linkType: hard + "baseline-browser-mapping@npm:^2.9.0": version: 2.9.19 resolution: "baseline-browser-mapping@npm:2.9.19" @@ -8582,6 +9328,13 @@ __metadata: languageName: node linkType: hard +"basic-ftp@npm:^5.0.2": + version: 5.2.0 + resolution: "basic-ftp@npm:5.2.0" + checksum: 10c0/a0f85c01deae0723021f9bf4a7be29378186fa8bba41e74ea11832fe74c187ce90c3599c3cc5ec936581cfd150020e79f4a9ed0ee9fb20b2308e69b045f3a059 + languageName: node + linkType: hard + "before-after-hook@npm:^2.2.0": version: 2.2.3 resolution: "before-after-hook@npm:2.2.3" @@ -8716,6 +9469,15 @@ __metadata: languageName: node linkType: hard +"brace-expansion@npm:^5.0.2": + version: 5.0.4 + resolution: "brace-expansion@npm:5.0.4" + dependencies: + balanced-match: "npm:^4.0.2" + checksum: 10c0/359cbcfa80b2eb914ca1f3440e92313fbfe7919ee6b274c35db55bec555aded69dac5ee78f102cec90c35f98c20fa43d10936d0cd9978158823c249257e1643a + languageName: node + linkType: hard + "braces@npm:^3.0.3, braces@npm:~3.0.2": version: 3.0.3 resolution: "braces@npm:3.0.3" @@ -9130,6 +9892,16 @@ __metadata: languageName: node linkType: hard +"chalk@npm:4.1.2, chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: "npm:^4.1.0" + supports-color: "npm:^7.1.0" + checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880 + languageName: node + linkType: hard + "chalk@npm:^2.0.1, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -9141,16 +9913,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": - version: 4.1.2 - resolution: "chalk@npm:4.1.2" - dependencies: - ansi-styles: "npm:^4.1.0" - supports-color: "npm:^7.1.0" - checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880 - languageName: node - linkType: hard - "chalk@npm:^5.3.0": version: 5.3.0 resolution: "chalk@npm:5.3.0" @@ -9485,6 +10247,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:8.3.0": + version: 8.3.0 + resolution: "commander@npm:8.3.0" + checksum: 10c0/8b043bb8322ea1c39664a1598a95e0495bfe4ca2fad0d84a92d7d1d8d213e2a155b441d2470c8e08de7c4a28cf2bc6e169211c49e1b21d9f7edc6ae4d9356060 + languageName: node + linkType: hard + "commander@npm:^10.0.0, commander@npm:^10.0.1": version: 10.0.1 resolution: "commander@npm:10.0.1" @@ -9550,7 +10319,7 @@ __metadata: languageName: node linkType: hard -"compare-versions@npm:^6.1.1": +"compare-versions@npm:6.1.1, compare-versions@npm:^6.1.1": version: 6.1.1 resolution: "compare-versions@npm:6.1.1" checksum: 10c0/415205c7627f9e4f358f571266422980c9fe2d99086be0c9a48008ef7c771f32b0fbe8e97a441ffedc3910872f917a0675fe0fe3c3b6d331cda6d8690be06338 @@ -9607,6 +10376,23 @@ __metadata: languageName: node linkType: hard +"concurrently@npm:9.2.1": + version: 9.2.1 + resolution: "concurrently@npm:9.2.1" + dependencies: + chalk: "npm:4.1.2" + rxjs: "npm:7.8.2" + shell-quote: "npm:1.8.3" + supports-color: "npm:8.1.1" + tree-kill: "npm:1.2.2" + yargs: "npm:17.7.2" + bin: + conc: dist/bin/concurrently.js + concurrently: dist/bin/concurrently.js + checksum: 10c0/da37f239f82eb7ac24f5ddb56259861e5f1d6da2ade7602b6ea7ad3101b13b5ccec02a77b7001402d1028ff2fdc38eed55644b32853ad5abf30e057002a963aa + languageName: node + linkType: hard + "confbox@npm:^0.1.7": version: 0.1.7 resolution: "confbox@npm:0.1.7" @@ -9650,6 +10436,20 @@ __metadata: languageName: node linkType: hard +"consola@npm:^2.15.0": + version: 2.15.3 + resolution: "consola@npm:2.15.3" + checksum: 10c0/34a337e6b4a1349ee4d7b4c568484344418da8fdb829d7d71bfefcd724f608f273987633b6eef465e8de510929907a092e13cb7a28a5d3acb3be446fcc79fd5e + languageName: node + linkType: hard + +"consola@npm:^3.2.3": + version: 3.4.2 + resolution: "consola@npm:3.4.2" + checksum: 10c0/7cebe57ecf646ba74b300bcce23bff43034ed6fbec9f7e39c27cee1dc00df8a21cd336b466ad32e304ea70fba04ec9e890c200270de9a526ce021ba8a7e4c11a + languageName: node + linkType: hard + "console-control-strings@npm:^1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" @@ -9657,6 +10457,15 @@ __metadata: languageName: node linkType: hard +"console.table@npm:0.10.0": + version: 0.10.0 + resolution: "console.table@npm:0.10.0" + dependencies: + easy-table: "npm:1.1.0" + checksum: 10c0/b1893a06b422c7e82dca03dec000beabebc26415df558a05e1b9778407a76e4caa1db286df40f72e3780ac5c5b5ef5f4b8a3bef2d22020abb86f6408dc357875 + languageName: node + linkType: hard + "content-disposition@npm:0.5.4": version: 0.5.4 resolution: "content-disposition@npm:0.5.4" @@ -9867,7 +10676,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -9950,6 +10759,17 @@ __metadata: languageName: node linkType: hard +"customer-admin-panel@workspace:packages/rest-api-sdk/examples/customer-admin-panel": + version: 0.0.0-use.local + resolution: "customer-admin-panel@workspace:packages/rest-api-sdk/examples/customer-admin-panel" + dependencies: + "@reflag/rest-api-sdk": "workspace:*" + next: "npm:14.2.5" + react: "npm:18.3.1" + react-dom: "npm:18.3.1" + languageName: unknown + linkType: soft + "damerau-levenshtein@npm:^1.0.8": version: 1.0.8 resolution: "damerau-levenshtein@npm:1.0.8" @@ -9964,6 +10784,13 @@ __metadata: languageName: node linkType: hard +"data-uri-to-buffer@npm:^6.0.2": + version: 6.0.2 + resolution: "data-uri-to-buffer@npm:6.0.2" + checksum: 10c0/f76922bf895b3d7d443059ff278c9cc5efc89d70b8b80cd9de0aa79b3adc6d7a17948eefb8692e30398c43635f70ece1673d6085cc9eba2878dbc6c6da5292ac + languageName: node + linkType: hard + "data-urls@npm:^5.0.0": version: 5.0.0 resolution: "data-urls@npm:5.0.0" @@ -10316,6 +11143,17 @@ __metadata: languageName: node linkType: hard +"degenerator@npm:^5.0.0": + version: 5.0.1 + resolution: "degenerator@npm:5.0.1" + dependencies: + ast-types: "npm:^0.13.4" + escodegen: "npm:^2.1.0" + esprima: "npm:^4.0.1" + checksum: 10c0/e48d8a651edeb512a648711a09afec269aac6de97d442a4bb9cf121a66877e0eec11b9727100a10252335c0666ae1c84a8bc1e3a3f47788742c975064d2c7b1c + languageName: node + linkType: hard + "delayed-stream@npm:~1.0.0": version: 1.0.0 resolution: "delayed-stream@npm:1.0.0" @@ -10365,7 +11203,7 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.0.3": +"detect-libc@npm:^2.0.3, detect-libc@npm:^2.1.2": version: 2.1.2 resolution: "detect-libc@npm:2.1.2" checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4 @@ -10418,6 +11256,15 @@ __metadata: languageName: node linkType: hard +"doctrine@npm:^3.0.0": + version: 3.0.0 + resolution: "doctrine@npm:3.0.0" + dependencies: + esutils: "npm:^2.0.2" + checksum: 10c0/c96bdccabe9d62ab6fea9399fdff04a66e6563c1d6fb3a3a063e8d53c3bb136ba63e84250bbf63d00086a769ad53aef92d2bd483f03f837fc97b71cbee6b2520 + languageName: node + linkType: hard + "dom-accessibility-api@npm:^0.5.9": version: 0.5.16 resolution: "dom-accessibility-api@npm:0.5.16" @@ -10496,6 +11343,18 @@ __metadata: languageName: node linkType: hard +"easy-table@npm:1.1.0": + version: 1.1.0 + resolution: "easy-table@npm:1.1.0" + dependencies: + wcwidth: "npm:>=1.0.1" + dependenciesMeta: + wcwidth: + optional: true + checksum: 10c0/0b7b03723e450c8286bd375bbe7d23247456dbb8f79df055adcfd745bfb91f7604c4e78204ff75d65d5229bec8867cbefca51c57938004f487ff800b587540bb + languageName: node + linkType: hard + "editorconfig@npm:^1.0.4": version: 1.0.4 resolution: "editorconfig@npm:1.0.4" @@ -11282,6 +12141,24 @@ __metadata: languageName: node linkType: hard +"escodegen@npm:^2.1.0": + version: 2.1.0 + resolution: "escodegen@npm:2.1.0" + dependencies: + esprima: "npm:^4.0.1" + estraverse: "npm:^5.2.0" + esutils: "npm:^2.0.2" + source-map: "npm:~0.6.1" + dependenciesMeta: + source-map: + optional: true + bin: + escodegen: bin/escodegen.js + esgenerate: bin/esgenerate.js + checksum: 10c0/e1450a1f75f67d35c061bf0d60888b15f62ab63aef9df1901cffc81cffbbb9e8b3de237c5502cf8613a017c1df3a3003881307c78835a1ab54d8c8d2206e01d3 + languageName: node + linkType: hard + "eslint-config-next@npm:14.2.5": version: 14.2.5 resolution: "eslint-config-next@npm:14.2.5" @@ -11671,7 +12548,7 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:^7.1.1": +"eslint-scope@npm:^7.1.1, eslint-scope@npm:^7.2.2": version: 7.2.2 resolution: "eslint-scope@npm:7.2.2" dependencies: @@ -11729,6 +12606,54 @@ __metadata: languageName: node linkType: hard +"eslint@npm:^8": + version: 8.57.1 + resolution: "eslint@npm:8.57.1" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.2.0" + "@eslint-community/regexpp": "npm:^4.6.1" + "@eslint/eslintrc": "npm:^2.1.4" + "@eslint/js": "npm:8.57.1" + "@humanwhocodes/config-array": "npm:^0.13.0" + "@humanwhocodes/module-importer": "npm:^1.0.1" + "@nodelib/fs.walk": "npm:^1.2.8" + "@ungap/structured-clone": "npm:^1.2.0" + ajv: "npm:^6.12.4" + chalk: "npm:^4.0.0" + cross-spawn: "npm:^7.0.2" + debug: "npm:^4.3.2" + doctrine: "npm:^3.0.0" + escape-string-regexp: "npm:^4.0.0" + eslint-scope: "npm:^7.2.2" + eslint-visitor-keys: "npm:^3.4.3" + espree: "npm:^9.6.1" + esquery: "npm:^1.4.2" + esutils: "npm:^2.0.2" + fast-deep-equal: "npm:^3.1.3" + file-entry-cache: "npm:^6.0.1" + find-up: "npm:^5.0.0" + glob-parent: "npm:^6.0.2" + globals: "npm:^13.19.0" + graphemer: "npm:^1.4.0" + ignore: "npm:^5.2.0" + imurmurhash: "npm:^0.1.4" + is-glob: "npm:^4.0.0" + is-path-inside: "npm:^3.0.3" + js-yaml: "npm:^4.1.0" + json-stable-stringify-without-jsonify: "npm:^1.0.1" + levn: "npm:^0.4.1" + lodash.merge: "npm:^4.6.2" + minimatch: "npm:^3.1.2" + natural-compare: "npm:^1.4.0" + optionator: "npm:^0.9.3" + strip-ansi: "npm:^6.0.1" + text-table: "npm:^0.2.0" + bin: + eslint: bin/eslint.js + checksum: 10c0/1fd31533086c1b72f86770a4d9d7058ee8b4643fd1cfd10c7aac1ecb8725698e88352a87805cf4b2ce890aa35947df4b4da9655fb7fdfa60dbb448a43f6ebcf1 + languageName: node + linkType: hard + "eslint@npm:^9": version: 9.39.2 resolution: "eslint@npm:9.39.2" @@ -11849,7 +12774,7 @@ __metadata: languageName: node linkType: hard -"espree@npm:^9.3.1": +"espree@npm:^9.3.1, espree@npm:^9.6.0, espree@npm:^9.6.1": version: 9.6.1 resolution: "espree@npm:9.6.1" dependencies: @@ -11879,6 +12804,15 @@ __metadata: languageName: node linkType: hard +"esquery@npm:^1.4.2": + version: 1.7.0 + resolution: "esquery@npm:1.7.0" + dependencies: + estraverse: "npm:^5.1.0" + checksum: 10c0/77d5173db450b66f3bc685d11af4c90cffeedb340f34a39af96d43509a335ce39c894fd79233df32d38f5e4e219fa0f7076f6ec90bae8320170ba082c0db4793 + languageName: node + linkType: hard + "esrecurse@npm:^4.3.0": version: 4.3.0 resolution: "esrecurse@npm:4.3.0" @@ -12282,6 +13216,13 @@ __metadata: languageName: node linkType: hard +"fast-safe-stringify@npm:2.1.1": + version: 2.1.1 + resolution: "fast-safe-stringify@npm:2.1.1" + checksum: 10c0/d90ec1c963394919828872f21edaa3ad6f1dddd288d2bd4e977027afff09f5db40f94e39536d4646f7e01761d704d72d51dce5af1b93717f3489ef808f5f4e4d + languageName: node + linkType: hard + "fast-uri@npm:^3.0.1": version: 3.0.1 resolution: "fast-uri@npm:3.0.1" @@ -12361,7 +13302,7 @@ __metadata: languageName: node linkType: hard -"figures@npm:3.2.0, figures@npm:^3.0.0": +"figures@npm:3.2.0, figures@npm:^3.0.0, figures@npm:^3.2.0": version: 3.2.0 resolution: "figures@npm:3.2.0" dependencies: @@ -12370,6 +13311,15 @@ __metadata: languageName: node linkType: hard +"file-entry-cache@npm:^6.0.1": + version: 6.0.1 + resolution: "file-entry-cache@npm:6.0.1" + dependencies: + flat-cache: "npm:^3.0.4" + checksum: 10c0/58473e8a82794d01b38e5e435f6feaf648e3f36fdb3a56e98f417f4efae71ad1c0d4ebd8a9a7c50c3ad085820a93fc7494ad721e0e4ebc1da3573f4e1c3c7cdd + languageName: node + linkType: hard + "file-entry-cache@npm:^8.0.0": version: 8.0.0 resolution: "file-entry-cache@npm:8.0.0" @@ -12379,6 +13329,18 @@ __metadata: languageName: node linkType: hard +"file-type@npm:21.3.0": + version: 21.3.0 + resolution: "file-type@npm:21.3.0" + dependencies: + "@tokenizer/inflate": "npm:^0.4.1" + strtok3: "npm:^10.3.4" + token-types: "npm:^6.1.1" + uint8array-extras: "npm:^1.4.0" + checksum: 10c0/1b1fa909e6063044a6da1d2ea348ee4d747ed9286382d3f0d4d6532c11fb2ea9f2e7e67b2bc7d745d1bc937e05dee1aa8cb912c64250933bcb393a3744f4e284 + languageName: node + linkType: hard + "filelist@npm:^1.0.4": version: 1.0.4 resolution: "filelist@npm:1.0.4" @@ -12467,6 +13429,17 @@ __metadata: languageName: node linkType: hard +"flat-cache@npm:^3.0.4": + version: 3.2.0 + resolution: "flat-cache@npm:3.2.0" + dependencies: + flatted: "npm:^3.2.9" + keyv: "npm:^4.5.3" + rimraf: "npm:^3.0.2" + checksum: 10c0/b76f611bd5f5d68f7ae632e3ae503e678d205cf97a17c6ab5b12f6ca61188b5f1f7464503efae6dc18683ed8f0b41460beb48ac4b9ac63fe6201296a91ba2f75 + languageName: node + linkType: hard + "flat-cache@npm:^4.0.0": version: 4.0.1 resolution: "flat-cache@npm:4.0.1" @@ -12517,6 +13490,16 @@ __metadata: languageName: node linkType: hard +"follow-redirects@npm:^1.15.11": + version: 1.15.11 + resolution: "follow-redirects@npm:1.15.11" + peerDependenciesMeta: + debug: + optional: true + checksum: 10c0/d301f430542520a54058d4aeeb453233c564aaccac835d29d15e050beb33f339ad67d9bddbce01739c5dc46a6716dbe3d9d0d5134b1ca203effa11a7ef092343 + languageName: node + linkType: hard + "follow-redirects@npm:^1.15.6": version: 1.15.6 resolution: "follow-redirects@npm:1.15.6" @@ -12566,6 +13549,19 @@ __metadata: languageName: node linkType: hard +"form-data@npm:^4.0.5": + version: 4.0.5 + resolution: "form-data@npm:4.0.5" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.2" + mime-types: "npm:^2.1.12" + checksum: 10c0/dd6b767ee0bbd6d84039db12a0fa5a2028160ffbfaba1800695713b46ae974a5f6e08b3356c3195137f8530dcd9dfcb5d5ae1eeff53d0db1e5aad863b619ce3b + languageName: node + linkType: hard + "forwarded@npm:0.2.0": version: 0.2.0 resolution: "forwarded@npm:0.2.0" @@ -12601,6 +13597,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:11.3.3": + version: 11.3.3 + resolution: "fs-extra@npm:11.3.3" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10c0/984924ff4104e3e9f351b658a864bf3b354b2c90429f57aec0acd12d92c4e6b762cbacacdffb4e745b280adce882e1f980c485d9f02c453f769ab4e7fc646ce3 + languageName: node + linkType: hard + "fs-extra@npm:^11.1.0, fs-extra@npm:^11.1.1": version: 11.2.0 resolution: "fs-extra@npm:11.2.0" @@ -12963,6 +13970,17 @@ __metadata: languageName: node linkType: hard +"get-uri@npm:^6.0.1": + version: 6.0.5 + resolution: "get-uri@npm:6.0.5" + dependencies: + basic-ftp: "npm:^5.0.2" + data-uri-to-buffer: "npm:^6.0.2" + debug: "npm:^4.3.4" + checksum: 10c0/c7ff5d5d55de53d23ecce7c5108cc3ed0db1174db43c9aa15506d640283d36ee0956fd8ba1fc50b06a718466cc85794ae9d8860193f91318afe846e3e7010f3a + languageName: node + linkType: hard + "getenv@npm:^2.0.0": version: 2.0.0 resolution: "getenv@npm:2.0.0" @@ -13073,6 +14091,17 @@ __metadata: languageName: node linkType: hard +"glob@npm:13.0.6": + version: 13.0.6 + resolution: "glob@npm:13.0.6" + dependencies: + minimatch: "npm:^10.2.2" + minipass: "npm:^7.1.3" + path-scurry: "npm:^2.0.2" + checksum: 10c0/269c236f11a9b50357fe7a8c6aadac667e01deb5242b19c84975628f05f4438d8ee1354bb62c5d6c10f37fd59911b54d7799730633a2786660d8c69f1d18120a + languageName: node + linkType: hard + "glob@npm:^10.2.2, glob@npm:^10.3.10": version: 10.3.14 resolution: "glob@npm:10.3.14" @@ -13191,7 +14220,7 @@ __metadata: languageName: node linkType: hard -"globals@npm:^13.24.0": +"globals@npm:^13.19.0, globals@npm:^13.24.0": version: 13.24.0 resolution: "globals@npm:13.24.0" dependencies: @@ -13621,7 +14650,7 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^7.0.2": +"http-proxy-agent@npm:^7.0.1, http-proxy-agent@npm:^7.0.2": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" dependencies: @@ -13685,7 +14714,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^7.0.5": +"https-proxy-agent@npm:^7.0.5, https-proxy-agent@npm:^7.0.6": version: 7.0.6 resolution: "https-proxy-agent@npm:7.0.6" dependencies: @@ -13745,7 +14774,7 @@ __metadata: languageName: node linkType: hard -"ieee754@npm:^1.1.13": +"ieee754@npm:^1.1.13, ieee754@npm:^1.2.1": version: 1.2.1 resolution: "ieee754@npm:1.2.1" checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb @@ -13956,6 +14985,13 @@ __metadata: languageName: node linkType: hard +"ip-address@npm:^10.0.1": + version: 10.1.0 + resolution: "ip-address@npm:10.1.0" + checksum: 10c0/0103516cfa93f6433b3bd7333fa876eb21263912329bfa47010af5e16934eeeff86f3d2ae700a3744a137839ddfad62b900c7a445607884a49b5d1e32a3d7566 + languageName: node + linkType: hard + "ip-address@npm:^9.0.5": version: 9.0.5 resolution: "ip-address@npm:9.0.5" @@ -14339,6 +15375,13 @@ __metadata: languageName: node linkType: hard +"is-path-inside@npm:^3.0.3": + version: 3.0.3 + resolution: "is-path-inside@npm:3.0.3" + checksum: 10c0/cf7d4ac35fb96bab6a1d2c3598fe5ebb29aafb52c0aaa482b5a3ed9d8ba3edc11631e3ec2637660c44b3ce0e61a08d54946e8af30dec0b60a7c27296c68ffd05 + languageName: node + linkType: hard + "is-plain-obj@npm:^1.0.0, is-plain-obj@npm:^1.1.0": version: 1.1.0 resolution: "is-plain-obj@npm:1.1.0" @@ -14733,6 +15776,13 @@ __metadata: languageName: node linkType: hard +"iterare@npm:1.2.1": + version: 1.2.1 + resolution: "iterare@npm:1.2.1" + checksum: 10c0/02667d486e3e83ead028ba8484d927498c2ceab7e8c6a69dd881fd02abc4114f00b13abb36b592252fbb578b6e6f99ca1dfc2835408b9158c9a112a9964f453f + languageName: node + linkType: hard + "iterator.prototype@npm:^1.1.2": version: 1.1.2 resolution: "iterator.prototype@npm:1.1.2" @@ -15262,7 +16312,7 @@ __metadata: languageName: node linkType: hard -"keyv@npm:^4.5.4": +"keyv@npm:^4.5.3, keyv@npm:^4.5.4": version: 4.5.4 resolution: "keyv@npm:4.5.4" dependencies: @@ -15621,6 +16671,13 @@ __metadata: languageName: node linkType: hard +"load-esm@npm:1.0.3": + version: 1.0.3 + resolution: "load-esm@npm:1.0.3" + checksum: 10c0/285a3666a29c11f7b466bb70ee3582af32893d03ed91b68be939c656a15afd27f3683f5f8d56b52834ce2911ecf1c84e515e6048248fb5268a89b724a8ddbf65 + languageName: node + linkType: hard + "load-json-file@npm:6.2.0": version: 6.2.0 resolution: "load-json-file@npm:6.2.0" @@ -15876,7 +16933,7 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^7.5.1, lru-cache@npm:^7.7.1": +"lru-cache@npm:^7.14.1, lru-cache@npm:^7.5.1, lru-cache@npm:^7.7.1": version: 7.18.3 resolution: "lru-cache@npm:7.18.3" checksum: 10c0/b3a452b491433db885beed95041eb104c157ef7794b9c9b4d647be503be91769d11206bb573849a16b4cc0d03cbd15ffd22df7960997788b74c1d399ac7a4fed @@ -16508,6 +17565,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^10.2.2": + version: 10.2.4 + resolution: "minimatch@npm:10.2.4" + dependencies: + brace-expansion: "npm:^5.0.2" + checksum: 10c0/35f3dfb7b99b51efd46afd378486889f590e7efb10e0f6a10ba6800428cf65c9a8dedb74427d0570b318d749b543dc4e85f06d46d2858bc8cac7e1eb49a95945 + languageName: node + linkType: hard + "minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -16517,6 +17583,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^3.0.5": + version: 3.1.5 + resolution: "minimatch@npm:3.1.5" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/2ecbdc0d33f07bddb0315a8b5afbcb761307a8778b48f0b312418ccbced99f104a2d17d8aca7573433c70e8ccd1c56823a441897a45e384ea76ef401a26ace70 + languageName: node + linkType: hard + "minimatch@npm:^5.0.1": version: 5.1.6 resolution: "minimatch@npm:5.1.6" @@ -16687,6 +17762,13 @@ __metadata: languageName: node linkType: hard +"minipass@npm:^7.1.3": + version: 7.1.3 + resolution: "minipass@npm:7.1.3" + checksum: 10c0/539da88daca16533211ea5a9ee98dc62ff5742f531f54640dd34429e621955e91cc280a91a776026264b7f9f6735947629f920944e9c1558369e8bf22eb33fbb + languageName: node + linkType: hard + "minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": version: 2.1.2 resolution: "minizlib@npm:2.1.2" @@ -16954,6 +18036,13 @@ __metadata: languageName: node linkType: hard +"netmask@npm:^2.0.2": + version: 2.0.2 + resolution: "netmask@npm:2.0.2" + checksum: 10c0/cafd28388e698e1138ace947929f842944d0f1c0b87d3fa2601a61b38dc89397d33c0ce2c8e7b99e968584b91d15f6810b91bef3f3826adf71b1833b61d4bf4f + languageName: node + linkType: hard + "next@npm:14.2.26": version: 14.2.26 resolution: "next@npm:14.2.26" @@ -17008,10 +18097,226 @@ __metadata: optional: true bin: next: dist/bin/next - checksum: 10c0/d5792ad9aff56b42648456f9cced2f269100c4d4200590c2f4e419f3ad2c5795b57f7caf273bc336323f866dbb31268197b46a07016ca7e55637d68b53176b29 + checksum: 10c0/d5792ad9aff56b42648456f9cced2f269100c4d4200590c2f4e419f3ad2c5795b57f7caf273bc336323f866dbb31268197b46a07016ca7e55637d68b53176b29 + languageName: node + linkType: hard + +"next@npm:14.2.5": + version: 14.2.5 + resolution: "next@npm:14.2.5" + dependencies: + "@next/env": "npm:14.2.5" + "@next/swc-darwin-arm64": "npm:14.2.5" + "@next/swc-darwin-x64": "npm:14.2.5" + "@next/swc-linux-arm64-gnu": "npm:14.2.5" + "@next/swc-linux-arm64-musl": "npm:14.2.5" + "@next/swc-linux-x64-gnu": "npm:14.2.5" + "@next/swc-linux-x64-musl": "npm:14.2.5" + "@next/swc-win32-arm64-msvc": "npm:14.2.5" + "@next/swc-win32-ia32-msvc": "npm:14.2.5" + "@next/swc-win32-x64-msvc": "npm:14.2.5" + "@swc/helpers": "npm:0.5.5" + busboy: "npm:1.6.0" + caniuse-lite: "npm:^1.0.30001579" + graceful-fs: "npm:^4.2.11" + postcss: "npm:8.4.31" + styled-jsx: "npm:5.1.1" + peerDependencies: + "@opentelemetry/api": ^1.1.0 + "@playwright/test": ^1.41.2 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-ia32-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + "@playwright/test": + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: 10c0/8df7d8ccc1a5bab03fa50dd6656c8a6f3750e81ef0b087dc329fea9346847c3094a933a890a8e87151dc32f0bc55020b8f6386d4565856d83bcc10895d29ec08 + languageName: node + linkType: hard + +"next@npm:15.5.10": + version: 15.5.10 + resolution: "next@npm:15.5.10" + dependencies: + "@next/env": "npm:15.5.10" + "@next/swc-darwin-arm64": "npm:15.5.7" + "@next/swc-darwin-x64": "npm:15.5.7" + "@next/swc-linux-arm64-gnu": "npm:15.5.7" + "@next/swc-linux-arm64-musl": "npm:15.5.7" + "@next/swc-linux-x64-gnu": "npm:15.5.7" + "@next/swc-linux-x64-musl": "npm:15.5.7" + "@next/swc-win32-arm64-msvc": "npm:15.5.7" + "@next/swc-win32-x64-msvc": "npm:15.5.7" + "@swc/helpers": "npm:0.5.15" + caniuse-lite: "npm:^1.0.30001579" + postcss: "npm:8.4.31" + sharp: "npm:^0.34.3" + styled-jsx: "npm:5.1.6" + peerDependencies: + "@opentelemetry/api": ^1.1.0 + "@playwright/test": ^1.51.1 + babel-plugin-react-compiler: "*" + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + sharp: + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + "@playwright/test": + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: 10c0/ebf358cfc13e856ac2d60f6b1eb166560c541796d3422d6719712c0e8038626407f6075cb0d2805d9d6392404a5b0e63e501515982d78d7e934b721ea9753b17 + languageName: node + linkType: hard + +"next@npm:16.1.5": + version: 16.1.5 + resolution: "next@npm:16.1.5" + dependencies: + "@next/env": "npm:16.1.5" + "@next/swc-darwin-arm64": "npm:16.1.5" + "@next/swc-darwin-x64": "npm:16.1.5" + "@next/swc-linux-arm64-gnu": "npm:16.1.5" + "@next/swc-linux-arm64-musl": "npm:16.1.5" + "@next/swc-linux-x64-gnu": "npm:16.1.5" + "@next/swc-linux-x64-musl": "npm:16.1.5" + "@next/swc-win32-arm64-msvc": "npm:16.1.5" + "@next/swc-win32-x64-msvc": "npm:16.1.5" + "@swc/helpers": "npm:0.5.15" + baseline-browser-mapping: "npm:^2.8.3" + caniuse-lite: "npm:^1.0.30001579" + postcss: "npm:8.4.31" + sharp: "npm:^0.34.4" + styled-jsx: "npm:5.1.6" + peerDependencies: + "@opentelemetry/api": ^1.1.0 + "@playwright/test": ^1.51.1 + babel-plugin-react-compiler: "*" + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + sharp: + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + "@playwright/test": + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: 10c0/ff9f7dd0cae79f7ba2a64ac0d29ca77c94921dfc37b36899f4f997b948242f72585f4bb75829aaf8704d46c6f7d1a9796b8ff30688a3467c04c738b3a4247a9f languageName: node linkType: hard +"nextjs-bootstrap-demo@workspace:packages/react-sdk/dev/nextjs-bootstrap-demo": + version: 0.0.0-use.local + resolution: "nextjs-bootstrap-demo@workspace:packages/react-sdk/dev/nextjs-bootstrap-demo" + dependencies: + "@reflag/node-sdk": "workspace:^" + "@reflag/react-sdk": "workspace:^" + "@types/node": "npm:^22.12.0" + "@types/react": "npm:^18" + "@types/react-dom": "npm:^18" + eslint: "npm:^8" + eslint-config-next: "npm:14.2.5" + next: "npm:16.1.5" + postcss: "npm:^8" + react: "npm:^18" + react-dom: "npm:^18" + tailwindcss: "npm:^3.4.1" + typescript: "npm:^5.7.3" + languageName: unknown + linkType: soft + +"nextjs-flag-demo@workspace:packages/react-sdk/dev/nextjs-flag-demo": + version: 0.0.0-use.local + resolution: "nextjs-flag-demo@workspace:packages/react-sdk/dev/nextjs-flag-demo" + dependencies: + "@reflag/react-sdk": "workspace:^" + "@types/node": "npm:^22.12.0" + "@types/react": "npm:^18" + "@types/react-dom": "npm:^18" + eslint: "npm:^8" + eslint-config-next: "npm:14.2.5" + next: "npm:15.5.10" + postcss: "npm:^8" + react: "npm:^18" + react-dom: "npm:^18" + tailwindcss: "npm:^3.4.1" + typescript: "npm:^5.7.3" + languageName: unknown + linkType: soft + "nextjs-openfeature-example@workspace:packages/openfeature-browser-provider/example": version: 0.0.0-use.local resolution: "nextjs-openfeature-example@workspace:packages/openfeature-browser-provider/example" @@ -17066,7 +18371,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.7": +"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" dependencies: @@ -18101,6 +19406,32 @@ __metadata: languageName: node linkType: hard +"pac-proxy-agent@npm:^7.1.0": + version: 7.2.0 + resolution: "pac-proxy-agent@npm:7.2.0" + dependencies: + "@tootallnate/quickjs-emscripten": "npm:^0.23.0" + agent-base: "npm:^7.1.2" + debug: "npm:^4.3.4" + get-uri: "npm:^6.0.1" + http-proxy-agent: "npm:^7.0.0" + https-proxy-agent: "npm:^7.0.6" + pac-resolver: "npm:^7.0.1" + socks-proxy-agent: "npm:^8.0.5" + checksum: 10c0/0265c17c9401c2ea735697931a6553a0c6d8b20c4d7d4e3b3a0506080ba69a8d5ad656e2a6be875411212e2b6ed7a4d9526dd3997e08581fdfb1cbcad454c296 + languageName: node + linkType: hard + +"pac-resolver@npm:^7.0.1": + version: 7.0.1 + resolution: "pac-resolver@npm:7.0.1" + dependencies: + degenerator: "npm:^5.0.0" + netmask: "npm:^2.0.2" + checksum: 10c0/5f3edd1dd10fded31e7d1f95776442c3ee51aa098c28b74ede4927d9677ebe7cebb2636750c24e945f5b84445e41ae39093d3a1014a994e5ceb9f0b1b88ebff5 + languageName: node + linkType: hard + "package-json-from-dist@npm:^1.0.0": version: 1.0.0 resolution: "package-json-from-dist@npm:1.0.0" @@ -18296,6 +19627,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^2.0.2": + version: 2.0.2 + resolution: "path-scurry@npm:2.0.2" + dependencies: + lru-cache: "npm:^11.0.0" + minipass: "npm:^7.1.2" + checksum: 10c0/b35ad37cf6557a87fd057121ce2be7695380c9138d93e87ae928609da259ea0a170fac6f3ef1eb3ece8a068e8b7f2f3adf5bb2374cf4d4a57fe484954fcc9482 + languageName: node + linkType: hard + "path-to-regexp@npm:0.1.12": version: 0.1.12 resolution: "path-to-regexp@npm:0.1.12" @@ -18303,6 +19644,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:8.3.0": + version: 8.3.0 + resolution: "path-to-regexp@npm:8.3.0" + checksum: 10c0/ee1544a73a3f294a97a4c663b0ce71bbf1621d732d80c9c9ed201b3e911a86cb628ebad691b9d40f40a3742fe22011e5a059d8eed2cf63ec2cb94f6fb4efe67c + languageName: node + linkType: hard + "path-to-regexp@npm:^6.2.0": version: 6.3.0 resolution: "path-to-regexp@npm:6.3.0" @@ -19241,6 +20589,22 @@ __metadata: languageName: node linkType: hard +"proxy-agent@npm:6.5.0": + version: 6.5.0 + resolution: "proxy-agent@npm:6.5.0" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:^4.3.4" + http-proxy-agent: "npm:^7.0.1" + https-proxy-agent: "npm:^7.0.6" + lru-cache: "npm:^7.14.1" + pac-proxy-agent: "npm:^7.1.0" + proxy-from-env: "npm:^1.1.0" + socks-proxy-agent: "npm:^8.0.5" + checksum: 10c0/7fd4e6f36bf17098a686d4aee3b8394abfc0b0537c2174ce96b0a4223198b9fafb16576c90108a3fcfc2af0168bd7747152bfa1f58e8fee91d3780e79aab7fd8 + languageName: node + linkType: hard + "proxy-from-env@npm:^1.1.0": version: 1.1.0 resolution: "proxy-from-env@npm:1.1.0" @@ -19737,6 +21101,13 @@ __metadata: languageName: unknown linkType: soft +"reflect-metadata@npm:0.2.2": + version: 0.2.2 + resolution: "reflect-metadata@npm:0.2.2" + checksum: 10c0/1cd93a15ea291e420204955544637c264c216e7aac527470e393d54b4bb075f10a17e60d8168ec96600c7e0b9fcc0cb0bb6e91c3fbf5b0d8c9056f04e6ac1ec2 + languageName: node + linkType: hard + "reflect.getprototypeof@npm:^1.0.4": version: 1.0.6 resolution: "reflect.getprototypeof@npm:1.0.6" @@ -20320,6 +21691,13 @@ __metadata: languageName: node linkType: hard +"run-async@npm:^3.0.0": + version: 3.0.0 + resolution: "run-async@npm:3.0.0" + checksum: 10c0/b18b562ae37c3020083dcaae29642e4cc360c824fbfb6b7d50d809a9d5227bb986152d09310255842c8dce40526e82ca768f02f00806c91ba92a8dfa6159cb85 + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -20329,6 +21707,15 @@ __metadata: languageName: node linkType: hard +"rxjs@npm:7.8.2": + version: 7.8.2 + resolution: "rxjs@npm:7.8.2" + dependencies: + tslib: "npm:^2.1.0" + checksum: 10c0/1fcd33d2066ada98ba8f21fcbbcaee9f0b271de1d38dc7f4e256bfbc6ffcdde68c8bfb69093de7eeb46f24b1fb820620bf0223706cff26b4ab99a7ff7b2e2c45 + languageName: node + linkType: hard + "rxjs@npm:^7.5.5": version: 7.8.1 resolution: "rxjs@npm:7.8.1" @@ -20547,7 +21934,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.5.2": +"semver@npm:^7.5.2, semver@npm:^7.7.3": version: 7.7.4 resolution: "semver@npm:7.7.4" bin: @@ -20730,6 +22117,90 @@ __metadata: languageName: node linkType: hard +"sharp@npm:^0.34.3, sharp@npm:^0.34.4": + version: 0.34.5 + resolution: "sharp@npm:0.34.5" + dependencies: + "@img/colour": "npm:^1.0.0" + "@img/sharp-darwin-arm64": "npm:0.34.5" + "@img/sharp-darwin-x64": "npm:0.34.5" + "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" + "@img/sharp-libvips-darwin-x64": "npm:1.2.4" + "@img/sharp-libvips-linux-arm": "npm:1.2.4" + "@img/sharp-libvips-linux-arm64": "npm:1.2.4" + "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" + "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" + "@img/sharp-libvips-linux-s390x": "npm:1.2.4" + "@img/sharp-libvips-linux-x64": "npm:1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" + "@img/sharp-linux-arm": "npm:0.34.5" + "@img/sharp-linux-arm64": "npm:0.34.5" + "@img/sharp-linux-ppc64": "npm:0.34.5" + "@img/sharp-linux-riscv64": "npm:0.34.5" + "@img/sharp-linux-s390x": "npm:0.34.5" + "@img/sharp-linux-x64": "npm:0.34.5" + "@img/sharp-linuxmusl-arm64": "npm:0.34.5" + "@img/sharp-linuxmusl-x64": "npm:0.34.5" + "@img/sharp-wasm32": "npm:0.34.5" + "@img/sharp-win32-arm64": "npm:0.34.5" + "@img/sharp-win32-ia32": "npm:0.34.5" + "@img/sharp-win32-x64": "npm:0.34.5" + detect-libc: "npm:^2.1.2" + semver: "npm:^7.7.3" + dependenciesMeta: + "@img/sharp-darwin-arm64": + optional: true + "@img/sharp-darwin-x64": + optional: true + "@img/sharp-libvips-darwin-arm64": + optional: true + "@img/sharp-libvips-darwin-x64": + optional: true + "@img/sharp-libvips-linux-arm": + optional: true + "@img/sharp-libvips-linux-arm64": + optional: true + "@img/sharp-libvips-linux-ppc64": + optional: true + "@img/sharp-libvips-linux-riscv64": + optional: true + "@img/sharp-libvips-linux-s390x": + optional: true + "@img/sharp-libvips-linux-x64": + optional: true + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + "@img/sharp-libvips-linuxmusl-x64": + optional: true + "@img/sharp-linux-arm": + optional: true + "@img/sharp-linux-arm64": + optional: true + "@img/sharp-linux-ppc64": + optional: true + "@img/sharp-linux-riscv64": + optional: true + "@img/sharp-linux-s390x": + optional: true + "@img/sharp-linux-x64": + optional: true + "@img/sharp-linuxmusl-arm64": + optional: true + "@img/sharp-linuxmusl-x64": + optional: true + "@img/sharp-wasm32": + optional: true + "@img/sharp-win32-arm64": + optional: true + "@img/sharp-win32-ia32": + optional: true + "@img/sharp-win32-x64": + optional: true + checksum: 10c0/fd79e29df0597a7d5704b8461c51f944ead91a5243691697be6e8243b966402beda53ddc6f0a53b96ea3cb8221f0b244aa588114d3ebf8734fb4aefd41ab802f + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -20746,7 +22217,7 @@ __metadata: languageName: node linkType: hard -"shell-quote@npm:^1.6.1, shell-quote@npm:^1.8.3": +"shell-quote@npm:1.8.3, shell-quote@npm:^1.6.1, shell-quote@npm:^1.8.3": version: 1.8.3 resolution: "shell-quote@npm:1.8.3" checksum: 10c0/bee87c34e1e986cfb4c30846b8e6327d18874f10b535699866f368ade11ea4ee45433d97bf5eada22c4320c27df79c3a6a7eb1bf3ecfc47f2c997d9e5e2672fd @@ -20980,6 +22451,17 @@ __metadata: languageName: node linkType: hard +"socks-proxy-agent@npm:^8.0.5": + version: 8.0.5 + resolution: "socks-proxy-agent@npm:8.0.5" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:^4.3.4" + socks: "npm:^2.8.3" + checksum: 10c0/5d2c6cecba6821389aabf18728325730504bf9bb1d9e342e7987a5d13badd7a98838cc9a55b8ed3cb866ad37cc23e1086f09c4d72d93105ce9dfe76330e9d2a6 + languageName: node + linkType: hard + "socks@npm:^2.6.2, socks@npm:^2.7.1": version: 2.8.3 resolution: "socks@npm:2.8.3" @@ -20990,6 +22472,16 @@ __metadata: languageName: node linkType: hard +"socks@npm:^2.8.3": + version: 2.8.7 + resolution: "socks@npm:2.8.7" + dependencies: + ip-address: "npm:^10.0.1" + smart-buffer: "npm:^4.2.0" + checksum: 10c0/2805a43a1c4bcf9ebf6e018268d87b32b32b06fbbc1f9282573583acc155860dc361500f89c73bfbb157caa1b4ac78059eac0ef15d1811eb0ca75e0bdadbc9d2 + languageName: node + linkType: hard + "sort-keys@npm:^2.0.0": version: 2.0.0 resolution: "sort-keys@npm:2.0.0" @@ -21559,6 +23051,15 @@ __metadata: languageName: node linkType: hard +"strtok3@npm:^10.3.4": + version: 10.3.4 + resolution: "strtok3@npm:10.3.4" + dependencies: + "@tokenizer/token": "npm:^0.3.0" + checksum: 10c0/277ab69e417f4545e364ffaf9d560c991f531045dbace32d77b5c822cccd76a608b782785a2c60595274288d4d32dced184a5c21dc20348791da697127dc69a8 + languageName: node + linkType: hard + "structured-headers@npm:^0.4.1": version: 0.4.1 resolution: "structured-headers@npm:0.4.1" @@ -21582,6 +23083,22 @@ __metadata: languageName: node linkType: hard +"styled-jsx@npm:5.1.6": + version: 5.1.6 + resolution: "styled-jsx@npm:5.1.6" + dependencies: + client-only: "npm:0.0.1" + peerDependencies: + react: ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + peerDependenciesMeta: + "@babel/core": + optional: true + babel-plugin-macros: + optional: true + checksum: 10c0/ace50e7ea5ae5ae6a3b65a50994c51fca6ae7df9c7ecfd0104c36be0b4b3a9c5c1a2374d16e2a11e256d0b20be6d47256d768ecb4f91ab390f60752a075780f5 + languageName: node + linkType: hard + "sucrase@npm:^3.32.0": version: 3.35.0 resolution: "sucrase@npm:3.35.0" @@ -21618,6 +23135,15 @@ __metadata: languageName: node linkType: hard +"supports-color@npm:8.1.1, supports-color@npm:^8.0.0, supports-color@npm:~8.1.1": + version: 8.1.1 + resolution: "supports-color@npm:8.1.1" + dependencies: + has-flag: "npm:^4.0.0" + checksum: 10c0/ea1d3c275dd604c974670f63943ed9bd83623edc102430c05adb8efc56ba492746b6e95386e7831b872ec3807fd89dd8eb43f735195f37b5ec343e4234cc7e89 + languageName: node + linkType: hard + "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -21636,15 +23162,6 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^8.0.0, supports-color@npm:~8.1.1": - version: 8.1.1 - resolution: "supports-color@npm:8.1.1" - dependencies: - has-flag: "npm:^4.0.0" - checksum: 10c0/ea1d3c275dd604c974670f63943ed9bd83623edc102430c05adb8efc56ba492746b6e95386e7831b872ec3807fd89dd8eb43f735195f37b5ec343e4234cc7e89 - languageName: node - linkType: hard - "supports-hyperlinks@npm:^2.0.0": version: 2.3.0 resolution: "supports-hyperlinks@npm:2.3.0" @@ -21845,6 +23362,13 @@ __metadata: languageName: node linkType: hard +"text-table@npm:^0.2.0": + version: 0.2.0 + resolution: "text-table@npm:0.2.0" + checksum: 10c0/02805740c12851ea5982686810702e2f14369a5f4c5c40a836821e3eefc65ffeec3131ba324692a37608294b0fd8c1e55a2dd571ffed4909822787668ddbee5c + languageName: node + linkType: hard + "thenify-all@npm:^1.0.0": version: 1.6.0 resolution: "thenify-all@npm:1.6.0" @@ -22026,6 +23550,17 @@ __metadata: languageName: node linkType: hard +"token-types@npm:^6.1.1": + version: 6.1.2 + resolution: "token-types@npm:6.1.2" + dependencies: + "@borewit/text-codec": "npm:^0.2.1" + "@tokenizer/token": "npm:^0.3.0" + ieee754: "npm:^1.2.1" + checksum: 10c0/8786e28e3cb65b9e890bc3c38def98e6dfe4565538237f8c0e47dbe549ed8f5f00de8dc464717868308abb4729f1958f78f69e1c4c3deebbb685729113a6fee8 + languageName: node + linkType: hard + "tough-cookie@npm:^4.1.4": version: 4.1.4 resolution: "tough-cookie@npm:4.1.4" @@ -22054,6 +23589,15 @@ __metadata: languageName: node linkType: hard +"tree-kill@npm:1.2.2": + version: 1.2.2 + resolution: "tree-kill@npm:1.2.2" + bin: + tree-kill: cli.js + checksum: 10c0/7b1b7c7f17608a8f8d20a162e7957ac1ef6cd1636db1aba92f4e072dc31818c2ff0efac1e3d91064ede67ed5dc57c565420531a8134090a12ac10cf792ab14d2 + languageName: node + linkType: hard + "trim-newlines@npm:^3.0.0": version: 3.0.1 resolution: "trim-newlines@npm:3.0.1" @@ -22147,6 +23691,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:2.8.1, tslib@npm:^2.0.1, tslib@npm:^2.8.0": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 + languageName: node + linkType: hard + "tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": version: 2.6.2 resolution: "tslib@npm:2.6.2" @@ -22620,6 +24171,22 @@ __metadata: languageName: node linkType: hard +"uid@npm:2.0.2": + version: 2.0.2 + resolution: "uid@npm:2.0.2" + dependencies: + "@lukeed/csprng": "npm:^1.0.0" + checksum: 10c0/e9d02d0562c74e74b5a2519e586db9d7f8204978e476cddd191ee1a9efb85efafdbab2dbf3fc3dde0f5da01fd9da161f37d604dabf513447fd2c03d008f1324c + languageName: node + linkType: hard + +"uint8array-extras@npm:^1.4.0": + version: 1.5.0 + resolution: "uint8array-extras@npm:1.5.0" + checksum: 10c0/0e74641ac7dadb02eadefc1ccdadba6010e007757bda824960de3c72bbe2b04e6d3af75648441f412148c4103261d54fcb60be45a2863beb76643a55fddba3bd + languageName: node + linkType: hard + "unbox-primitive@npm:^1.0.2": version: 1.0.2 resolution: "unbox-primitive@npm:1.0.2" @@ -22665,6 +24232,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 + languageName: node + linkType: hard + "undici@npm:^6.18.2": version: 6.23.0 resolution: "undici@npm:6.23.0" @@ -23463,7 +25037,7 @@ __metadata: languageName: node linkType: hard -"wcwidth@npm:^1.0.0, wcwidth@npm:^1.0.1": +"wcwidth@npm:>=1.0.1, wcwidth@npm:^1.0.0, wcwidth@npm:^1.0.1": version: 1.0.1 resolution: "wcwidth@npm:1.0.1" dependencies: From 037ad4f9b3a2a7fa98450ae03d6949388192e6d2 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 13:33:45 +0100 Subject: [PATCH 15/26] chore(rest-api-sdk): update README --- packages/rest-api-sdk/README.md | 150 +++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 4 deletions(-) diff --git a/packages/rest-api-sdk/README.md b/packages/rest-api-sdk/README.md index 6f22ef95..57c7d7ad 100644 --- a/packages/rest-api-sdk/README.md +++ b/packages/rest-api-sdk/README.md @@ -43,6 +43,19 @@ Core method groups: ```typescript const apps = await api.listApps(); console.log(apps.data); +// [ +// { +// "org": { "id": "org-1", "name": "Acme Org" }, +// "id": "app-123", +// "name": "Acme App", +// "demo": false, +// "flagKeyFormat": "kebabCaseLower", +// "environments": [ +// { "id": "env-123", "name": "Development", "isProduction": false, "order": 0 }, +// { "id": "env-456", "name": "Production", "isProduction": true, "order": 1 } +// ] +// } +// ] const app = apps.data[0]; const appId = app?.id; @@ -55,6 +68,9 @@ if (appId) { }); console.log(environments.data); + // [ + // { "id": "env-456", "name": "Production", "isProduction": true, "order": 1 } + // ] } ``` @@ -73,8 +89,34 @@ const environments = await appApi.listEnvironments({ sortBy: "order", sortOrder: "asc", }); +console.log(environments.data); +// [ +// { "id": "env-456", "name": "Production", "isProduction": true, "order": 1 } +// ] const flags = await appApi.listFlags({}); +console.log(flags.data); +// [ +// { +// "id": "flag-1", +// "key": "new-checkout", +// "name": "New checkout", +// "description": "Rollout for redesigned checkout flow", +// "stage": { "id": "stage-1", "name": "Beta", "color": "#4f46e5", "order": 2 }, +// "owner": { +// "id": "user-99", +// "name": "Jane Doe", +// "email": "jane@acme.com", +// "avatarUrl": "https://example.com/avatar.png" +// }, +// "archived": false, +// "stale": false, +// "permanent": false, +// "createdAt": "2026-03-03T09:00:00.000Z", +// "lastCheckAt": "2026-03-03T09:30:00.000Z", +// "lastTrackAt": "2026-03-03T09:31:00.000Z" +// } +// ] ``` ## Common workflows @@ -94,12 +136,34 @@ const created = await api.createFlag({ secret: false, }); -await api.updateFlag({ +const updated = await api.updateFlag({ appId: "app-123", flagId: created.flag.id, name: "New checkout experience", ownerUserId: null, }); +console.log(updated.flag); +// { +// "id": "flag-1", +// "key": "new-checkout", +// "name": "New checkout experience", +// "description": "Rollout for redesigned checkout flow", +// "stage": { "id": "stage-1", "name": "Beta", "color": "#4f46e5", "order": 2 }, +// "owner": { +// "id": "user-99", +// "name": "Jane Doe", +// "email": "jane@acme.com", +// "avatarUrl": "https://example.com/avatar.png" +// }, +// "archived": false, +// "stale": false, +// "permanent": false, +// "createdAt": "2026-03-03T09:00:00.000Z", +// "lastCheckAt": "2026-03-03T09:35:00.000Z", +// "lastTrackAt": "2026-03-03T09:36:00.000Z", +// "rolledOutToEveryoneAt": "2026-03-10T12:00:00.000Z", +// "parentFlagId": "flag-parent-1" +// } ``` ### Read user flags for an environment @@ -115,6 +179,23 @@ const userFlags = await api.getUserFlags({ }); console.log(userFlags.data); +// [ +// { +// "id": "flag-1", +// "key": "new-checkout", +// "name": "New checkout", +// "createdAt": "2026-03-03T09:00:00.000Z", +// "value": true, +// "specificTargetValue": true, +// "firstExposureAt": "2026-03-03T09:05:00.000Z", +// "lastExposureAt": "2026-03-03T09:30:00.000Z", +// "lastCheckAt": "2026-03-03T09:31:00.000Z", +// "exposureCount": 12, +// "firstTrackAt": "2026-03-03T09:06:00.000Z", +// "lastTrackAt": "2026-03-03T09:32:00.000Z", +// "trackCount": 5 +// } +// ] ``` ### Toggle a user flag @@ -122,15 +203,33 @@ console.log(userFlags.data); Use `true` to explicitly target on, and `null` to remove specific targeting. ```typescript -await api.updateUserFlags({ +const updatedUserFlags = await api.updateUserFlags({ appId: "app-123", envId: "env-456", userId: "user-1", updates: [{ flagKey: "new-checkout", specificTargetValue: true }], }); +console.log(updatedUserFlags.data); +// [ +// { +// "id": "flag-1", +// "key": "new-checkout", +// "name": "New checkout", +// "createdAt": "2026-03-03T09:00:00.000Z", +// "value": true, +// "specificTargetValue": true, +// "firstExposureAt": "2026-03-03T09:05:00.000Z", +// "lastExposureAt": "2026-03-03T09:35:00.000Z", +// "lastCheckAt": "2026-03-03T09:36:00.000Z", +// "exposureCount": 13, +// "firstTrackAt": "2026-03-03T09:06:00.000Z", +// "lastTrackAt": "2026-03-03T09:37:00.000Z", +// "trackCount": 6 +// } +// ] ``` -### Read and update company flags +### Read company flags for an environment ```typescript const companyFlags = await api.getCompanyFlags({ @@ -138,13 +237,56 @@ const companyFlags = await api.getCompanyFlags({ envId: "env-456", companyId: "company-1", }); +console.log(companyFlags.data); +// [ +// { +// "id": "flag-1", +// "key": "new-checkout", +// "name": "New checkout", +// "createdAt": "2026-03-03T09:00:00.000Z", +// "value": false, +// "specificTargetValue": null, +// "firstExposureAt": null, +// "lastExposureAt": null, +// "lastCheckAt": "2026-03-03T09:31:00.000Z", +// "exposureCount": 0, +// "firstTrackAt": null, +// "lastTrackAt": null, +// "trackCount": 0 +// } +// ] +``` + +### Toggle a company flag -await api.updateCompanyFlags({ +Use `true` to explicitly target on, and `null` to remove specific targeting. + +```typescript +const updatedCompanyFlags = await api.updateCompanyFlags({ appId: "app-123", envId: "env-456", companyId: "company-1", + // Use `null` to stop targeting the company specifically for that flag. updates: [{ flagKey: "new-checkout", specificTargetValue: null }], }); +console.log(updatedCompanyFlags.data); +// [ +// { +// "id": "flag-1", +// "key": "new-checkout", +// "name": "New checkout", +// "createdAt": "2026-03-03T09:00:00.000Z", +// "value": false, +// "specificTargetValue": null, +// "firstExposureAt": null, +// "lastExposureAt": null, +// "lastCheckAt": "2026-03-03T09:36:00.000Z", +// "exposureCount": 0, +// "firstTrackAt": null, +// "lastTrackAt": null, +// "trackCount": 0 +// } +// ] ``` ## Error handling From c6bf330ead3338582915f1861864ff899f0d62dc Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 13:33:55 +0100 Subject: [PATCH 16/26] chore: bump version --- packages/rest-api-sdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rest-api-sdk/package.json b/packages/rest-api-sdk/package.json index 6c8f8e28..114484e8 100644 --- a/packages/rest-api-sdk/package.json +++ b/packages/rest-api-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@reflag/rest-api-sdk", - "version": "0.0.1", + "version": "0.0.2", "description": "Reflag REST API SDK", "license": "MIT", "repository": { From d7a69931739de855a475acad0729b65360112dc9 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 13:37:10 +0100 Subject: [PATCH 17/26] fix(example): avoid build-time API key requirement in CI --- .../examples/customer-admin-panel/app/flags/company/page.tsx | 2 ++ .../examples/customer-admin-panel/app/flags/page.tsx | 2 ++ .../examples/customer-admin-panel/app/flags/user/page.tsx | 2 ++ .../rest-api-sdk/examples/customer-admin-panel/app/page.tsx | 2 ++ 4 files changed, 8 insertions(+) diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx index ac393dcf..2e0088b3 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/company/page.tsx @@ -9,6 +9,8 @@ import { toggleCompanyFlag, } from "../actions"; +export const dynamic = "force-dynamic"; + type QueryValue = string | string[] | undefined; type PageProps = { diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/page.tsx index cf71c56b..3a8dc9c3 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/page.tsx +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/page.tsx @@ -2,6 +2,8 @@ import Link from "next/link"; import AppEnvForm from "./AppEnvForm"; import { listApps, listEnvironments, listFlags } from "./actions"; +export const dynamic = "force-dynamic"; + type QueryValue = string | string[] | undefined; type PageProps = { diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx index f4dfe4b7..f1726479 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/user/page.tsx @@ -4,6 +4,8 @@ import { revalidatePath } from "next/cache"; import EntityFlagsFilterForm from "../EntityFlagsFilterForm"; import { fetchUserFlags, listApps, listEnvironments, toggleUserFlag } from "../actions"; +export const dynamic = "force-dynamic"; + type QueryValue = string | string[] | undefined; type PageProps = { diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/page.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/page.tsx index b73b4f49..fa5397a9 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/page.tsx +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/page.tsx @@ -1,5 +1,7 @@ import FlagsPage from "./flags/page"; +export const dynamic = "force-dynamic"; + export default function Home() { return ; } From e1149c88ff70966d0c8558dde10fb020b03bfc5a Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 13:53:42 +0100 Subject: [PATCH 18/26] fix(browser-sdk): avoid localStorage crash during SSR --- packages/browser-sdk/src/flag/flags.ts | 4 +-- packages/browser-sdk/src/storage.ts | 22 +++++++++++++ packages/browser-sdk/test/client.test.ts | 21 +++++++++++- packages/browser-sdk/test/storage.test.ts | 39 +++++++++++++++++++++++ 4 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 packages/browser-sdk/test/storage.test.ts diff --git a/packages/browser-sdk/src/flag/flags.ts b/packages/browser-sdk/src/flag/flags.ts index 408d3adc..1a776f6a 100644 --- a/packages/browser-sdk/src/flag/flags.ts +++ b/packages/browser-sdk/src/flag/flags.ts @@ -5,7 +5,7 @@ import { ReflagContext } from "../context"; import { HttpClient } from "../httpClient"; import { Logger, loggerWithPrefix } from "../logger"; import RateLimiter from "../rateLimiter"; -import { getLocalStorageAdapter, StorageAdapter } from "../storage"; +import { getDefaultStorageAdapter, StorageAdapter } from "../storage"; import { createAbortController } from "../utils/abortController"; import { createEventTarget } from "../utils/eventTarget"; @@ -235,7 +235,7 @@ export class FlagsClient { this.logger = loggerWithPrefix(logger, "[Flags]"); this.rateLimiter = rateLimiter ?? new RateLimiter(FLAG_EVENTS_PER_MIN, this.logger); - this.storage = (cache ? undefined : storage) ?? getLocalStorageAdapter(); + this.storage = (cache ? undefined : storage) ?? getDefaultStorageAdapter(); this.cache = cache ?? this.setupCache(this.config.staleTimeMs, this.config.expireTimeMs); diff --git a/packages/browser-sdk/src/storage.ts b/packages/browser-sdk/src/storage.ts index ffe5ab08..5b003300 100644 --- a/packages/browser-sdk/src/storage.ts +++ b/packages/browser-sdk/src/storage.ts @@ -4,6 +4,20 @@ export type StorageAdapter = { removeItem?(key: string): Promise; }; +export function createMemoryStorageAdapter(): StorageAdapter { + const memoryStorage = new Map(); + + return { + getItem: async (key) => memoryStorage.get(key) ?? null, + setItem: async (key, value) => { + memoryStorage.set(key, value); + }, + removeItem: async (key) => { + memoryStorage.delete(key); + }, + }; +} + export function getLocalStorageAdapter(): StorageAdapter { if ( typeof localStorage === "undefined" || @@ -24,3 +38,11 @@ export function getLocalStorageAdapter(): StorageAdapter { }, }; } + +export function getDefaultStorageAdapter(): StorageAdapter { + try { + return getLocalStorageAdapter(); + } catch { + return createMemoryStorageAdapter(); + } +} diff --git a/packages/browser-sdk/test/client.test.ts b/packages/browser-sdk/test/client.test.ts index 48e2b215..d42d4408 100644 --- a/packages/browser-sdk/test/client.test.ts +++ b/packages/browser-sdk/test/client.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { ReflagClient } from "../src/client"; import { FlagsClient } from "../src/flag/flags"; @@ -23,6 +23,25 @@ describe("ReflagClient", () => { vi.clearAllMocks(); }); + afterEach(() => { + vi.unstubAllGlobals(); + }); + + describe("storage runtime compatibility", () => { + it("can be constructed without localStorage", () => { + vi.stubGlobal("localStorage", undefined); + + expect( + () => + new ReflagClient({ + publishableKey: "test-key", + user: { id: "user1" }, + company: { id: "company1" }, + }), + ).not.toThrow(); + }); + }); + describe("updateUser", () => { it("should update the user context", async () => { // and send new user data and trigger flag update diff --git a/packages/browser-sdk/test/storage.test.ts b/packages/browser-sdk/test/storage.test.ts new file mode 100644 index 00000000..2542daa5 --- /dev/null +++ b/packages/browser-sdk/test/storage.test.ts @@ -0,0 +1,39 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; + +import { + createMemoryStorageAdapter, + getDefaultStorageAdapter, + getLocalStorageAdapter, +} from "../src/storage"; + +describe("storage adapters", () => { + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it("memory adapter stores and retrieves values", async () => { + const adapter = createMemoryStorageAdapter(); + + expect(await adapter.getItem("key")).toBeNull(); + await adapter.setItem("key", "value"); + expect(await adapter.getItem("key")).toBe("value"); + + await adapter.removeItem?.("key"); + expect(await adapter.getItem("key")).toBeNull(); + }); + + it("localStorage adapter throws when localStorage is unavailable", () => { + vi.stubGlobal("localStorage", undefined); + expect(() => getLocalStorageAdapter()).toThrowError( + "localStorage is not available. Provide a custom storage adapter.", + ); + }); + + it("default adapter falls back when localStorage is unavailable", async () => { + vi.stubGlobal("localStorage", undefined); + const adapter = getDefaultStorageAdapter(); + + await adapter.setItem("fallback", "ok"); + expect(await adapter.getItem("fallback")).toBe("ok"); + }); +}); From bccd7ebefc146c19f2003d5d69ec0e01f3d10a2b Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 14:02:15 +0100 Subject: [PATCH 19/26] fix(browser-sdk): use noop storage fallback on server --- packages/browser-sdk/src/storage.ts | 23 +++++++------------ packages/browser-sdk/test/client.test.ts | 15 ------------ packages/browser-sdk/test/storage.test.ts | 28 ++++++++++++++--------- 3 files changed, 25 insertions(+), 41 deletions(-) diff --git a/packages/browser-sdk/src/storage.ts b/packages/browser-sdk/src/storage.ts index 5b003300..b031736b 100644 --- a/packages/browser-sdk/src/storage.ts +++ b/packages/browser-sdk/src/storage.ts @@ -1,20 +1,16 @@ +import { IS_SERVER } from "./config"; + export type StorageAdapter = { getItem(key: string): Promise; setItem(key: string, value: string): Promise; removeItem?(key: string): Promise; }; -export function createMemoryStorageAdapter(): StorageAdapter { - const memoryStorage = new Map(); - +export function createNoopStorageAdapter(): StorageAdapter { return { - getItem: async (key) => memoryStorage.get(key) ?? null, - setItem: async (key, value) => { - memoryStorage.set(key, value); - }, - removeItem: async (key) => { - memoryStorage.delete(key); - }, + getItem: async () => null, + setItem: async () => undefined, + removeItem: async () => undefined, }; } @@ -40,9 +36,6 @@ export function getLocalStorageAdapter(): StorageAdapter { } export function getDefaultStorageAdapter(): StorageAdapter { - try { - return getLocalStorageAdapter(); - } catch { - return createMemoryStorageAdapter(); - } + if (IS_SERVER) return createNoopStorageAdapter(); + return getLocalStorageAdapter(); } diff --git a/packages/browser-sdk/test/client.test.ts b/packages/browser-sdk/test/client.test.ts index d42d4408..bf1fe228 100644 --- a/packages/browser-sdk/test/client.test.ts +++ b/packages/browser-sdk/test/client.test.ts @@ -27,21 +27,6 @@ describe("ReflagClient", () => { vi.unstubAllGlobals(); }); - describe("storage runtime compatibility", () => { - it("can be constructed without localStorage", () => { - vi.stubGlobal("localStorage", undefined); - - expect( - () => - new ReflagClient({ - publishableKey: "test-key", - user: { id: "user1" }, - company: { id: "company1" }, - }), - ).not.toThrow(); - }); - }); - describe("updateUser", () => { it("should update the user context", async () => { // and send new user data and trigger flag update diff --git a/packages/browser-sdk/test/storage.test.ts b/packages/browser-sdk/test/storage.test.ts index 2542daa5..ffcc5fab 100644 --- a/packages/browser-sdk/test/storage.test.ts +++ b/packages/browser-sdk/test/storage.test.ts @@ -1,39 +1,45 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { - createMemoryStorageAdapter, - getDefaultStorageAdapter, - getLocalStorageAdapter, -} from "../src/storage"; +async function loadStorageModule() { + vi.resetModules(); + return import("../src/storage"); +} describe("storage adapters", () => { afterEach(() => { vi.unstubAllGlobals(); }); - it("memory adapter stores and retrieves values", async () => { - const adapter = createMemoryStorageAdapter(); + it("noop adapter ignores writes", async () => { + const { createNoopStorageAdapter } = await loadStorageModule(); + const adapter = createNoopStorageAdapter(); expect(await adapter.getItem("key")).toBeNull(); await adapter.setItem("key", "value"); - expect(await adapter.getItem("key")).toBe("value"); + expect(await adapter.getItem("key")).toBeNull(); await adapter.removeItem?.("key"); expect(await adapter.getItem("key")).toBeNull(); }); - it("localStorage adapter throws when localStorage is unavailable", () => { + it("localStorage adapter throws when localStorage is unavailable", async () => { + const { getLocalStorageAdapter } = await loadStorageModule(); vi.stubGlobal("localStorage", undefined); expect(() => getLocalStorageAdapter()).toThrowError( "localStorage is not available. Provide a custom storage adapter.", ); }); - it("default adapter falls back when localStorage is unavailable", async () => { + it("default adapter falls back to noop on server runtimes", async () => { + vi.resetModules(); + vi.stubGlobal("window", undefined); + vi.stubGlobal("document", undefined); vi.stubGlobal("localStorage", undefined); + + const { getDefaultStorageAdapter } = await import("../src/storage"); const adapter = getDefaultStorageAdapter(); await adapter.setItem("fallback", "ok"); - expect(await adapter.getItem("fallback")).toBe("ok"); + expect(await adapter.getItem("fallback")).toBeNull(); }); }); From 0865e4b220255f818fc8f623a076b2a16ccae7b0 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 14:05:39 +0100 Subject: [PATCH 20/26] Revert "fix(browser-sdk): use noop storage fallback on server" This reverts commit bccd7ebefc146c19f2003d5d69ec0e01f3d10a2b. --- packages/browser-sdk/src/storage.ts | 23 ++++++++++++------- packages/browser-sdk/test/client.test.ts | 15 ++++++++++++ packages/browser-sdk/test/storage.test.ts | 28 +++++++++-------------- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/packages/browser-sdk/src/storage.ts b/packages/browser-sdk/src/storage.ts index b031736b..5b003300 100644 --- a/packages/browser-sdk/src/storage.ts +++ b/packages/browser-sdk/src/storage.ts @@ -1,16 +1,20 @@ -import { IS_SERVER } from "./config"; - export type StorageAdapter = { getItem(key: string): Promise; setItem(key: string, value: string): Promise; removeItem?(key: string): Promise; }; -export function createNoopStorageAdapter(): StorageAdapter { +export function createMemoryStorageAdapter(): StorageAdapter { + const memoryStorage = new Map(); + return { - getItem: async () => null, - setItem: async () => undefined, - removeItem: async () => undefined, + getItem: async (key) => memoryStorage.get(key) ?? null, + setItem: async (key, value) => { + memoryStorage.set(key, value); + }, + removeItem: async (key) => { + memoryStorage.delete(key); + }, }; } @@ -36,6 +40,9 @@ export function getLocalStorageAdapter(): StorageAdapter { } export function getDefaultStorageAdapter(): StorageAdapter { - if (IS_SERVER) return createNoopStorageAdapter(); - return getLocalStorageAdapter(); + try { + return getLocalStorageAdapter(); + } catch { + return createMemoryStorageAdapter(); + } } diff --git a/packages/browser-sdk/test/client.test.ts b/packages/browser-sdk/test/client.test.ts index bf1fe228..d42d4408 100644 --- a/packages/browser-sdk/test/client.test.ts +++ b/packages/browser-sdk/test/client.test.ts @@ -27,6 +27,21 @@ describe("ReflagClient", () => { vi.unstubAllGlobals(); }); + describe("storage runtime compatibility", () => { + it("can be constructed without localStorage", () => { + vi.stubGlobal("localStorage", undefined); + + expect( + () => + new ReflagClient({ + publishableKey: "test-key", + user: { id: "user1" }, + company: { id: "company1" }, + }), + ).not.toThrow(); + }); + }); + describe("updateUser", () => { it("should update the user context", async () => { // and send new user data and trigger flag update diff --git a/packages/browser-sdk/test/storage.test.ts b/packages/browser-sdk/test/storage.test.ts index ffcc5fab..2542daa5 100644 --- a/packages/browser-sdk/test/storage.test.ts +++ b/packages/browser-sdk/test/storage.test.ts @@ -1,45 +1,39 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -async function loadStorageModule() { - vi.resetModules(); - return import("../src/storage"); -} +import { + createMemoryStorageAdapter, + getDefaultStorageAdapter, + getLocalStorageAdapter, +} from "../src/storage"; describe("storage adapters", () => { afterEach(() => { vi.unstubAllGlobals(); }); - it("noop adapter ignores writes", async () => { - const { createNoopStorageAdapter } = await loadStorageModule(); - const adapter = createNoopStorageAdapter(); + it("memory adapter stores and retrieves values", async () => { + const adapter = createMemoryStorageAdapter(); expect(await adapter.getItem("key")).toBeNull(); await adapter.setItem("key", "value"); - expect(await adapter.getItem("key")).toBeNull(); + expect(await adapter.getItem("key")).toBe("value"); await adapter.removeItem?.("key"); expect(await adapter.getItem("key")).toBeNull(); }); - it("localStorage adapter throws when localStorage is unavailable", async () => { - const { getLocalStorageAdapter } = await loadStorageModule(); + it("localStorage adapter throws when localStorage is unavailable", () => { vi.stubGlobal("localStorage", undefined); expect(() => getLocalStorageAdapter()).toThrowError( "localStorage is not available. Provide a custom storage adapter.", ); }); - it("default adapter falls back to noop on server runtimes", async () => { - vi.resetModules(); - vi.stubGlobal("window", undefined); - vi.stubGlobal("document", undefined); + it("default adapter falls back when localStorage is unavailable", async () => { vi.stubGlobal("localStorage", undefined); - - const { getDefaultStorageAdapter } = await import("../src/storage"); const adapter = getDefaultStorageAdapter(); await adapter.setItem("fallback", "ok"); - expect(await adapter.getItem("fallback")).toBeNull(); + expect(await adapter.getItem("fallback")).toBe("ok"); }); }); From e5d84b14970b334cd93ba737bbeb0a88e3e067a6 Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 15:10:19 +0100 Subject: [PATCH 21/26] fix(nextjs-bootstrap-demo): default to offline without secret key --- packages/react-sdk/dev/nextjs-bootstrap-demo/app/client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-sdk/dev/nextjs-bootstrap-demo/app/client.ts b/packages/react-sdk/dev/nextjs-bootstrap-demo/app/client.ts index 7f024393..1e8663f4 100644 --- a/packages/react-sdk/dev/nextjs-bootstrap-demo/app/client.ts +++ b/packages/react-sdk/dev/nextjs-bootstrap-demo/app/client.ts @@ -1,7 +1,7 @@ import { ReflagClient as ReflagNodeClient } from "@reflag/node-sdk"; -const secretKey = process.env.REFLAG_SECRET_KEY || ""; -const offline = process.env.CI === "true"; +const secretKey = process.env.REFLAG_SECRET_KEY; +const offline = process.env.CI === "true" || !secretKey; declare global { var serverClient: ReflagNodeClient; From 2e6c93af76b4c68c24c4bd6d77659d131521c7ea Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 15:31:09 +0100 Subject: [PATCH 22/26] docs(rest-api-sdk): update API key wording and flags surface --- packages/rest-api-sdk/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rest-api-sdk/README.md b/packages/rest-api-sdk/README.md index 57c7d7ad..bcd62a51 100644 --- a/packages/rest-api-sdk/README.md +++ b/packages/rest-api-sdk/README.md @@ -12,7 +12,7 @@ yarn add @reflag/rest-api-sdk ## Create a client -All requests require a Reflag API key. +Initialize the SDK with a [Reflag REST API Key](https://app.reflag.com/env-current/settings/org-api-access). ```typescript import { Api } from "@reflag/rest-api-sdk"; @@ -35,7 +35,7 @@ Core method groups: - Applications: `listApps`, `getApp` - Environments: `listEnvironments`, `getEnvironment` -- Flags: `listFlags`, `createFlag`, `updateFlag`, `getFlagTargeting` +- Flags: `listFlags`, `createFlag`, `updateFlag` - User/company evaluation: `getUserFlags`, `updateUserFlags`, `getCompanyFlags`, `updateCompanyFlags` ## Quick start From de3e0fa4809804de26f8828eef80fed3e15833cf Mon Sep 17 00:00:00 2001 From: Ron Cohen Date: Tue, 3 Mar 2026 15:35:23 +0100 Subject: [PATCH 23/26] chore: run prettier fix and ignore .next outputs --- .prettierignore | 1 + packages/rest-api-sdk/.prettierignore | 1 + .../examples/customer-admin-panel/README.md | 1 + .../app/flags/AppEnvForm.tsx | 6 ++- .../app/flags/EntityFlagsFilterForm.tsx | 6 ++- .../app/flags/company/page.tsx | 42 +++++++++++++++---- .../customer-admin-panel/app/flags/page.tsx | 11 ++++- .../app/flags/user/page.tsx | 37 ++++++++++++---- .../customer-admin-panel/app/globals.css | 7 +++- .../customer-admin-panel/app/layout.tsx | 3 +- .../customer-admin-panel/tsconfig.json | 16 ++----- 11 files changed, 97 insertions(+), 34 deletions(-) diff --git a/.prettierignore b/.prettierignore index 2d3d01a2..c2163a57 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,4 +2,5 @@ **/gen **/node_modules **/dist +**/.next **/.expo diff --git a/packages/rest-api-sdk/.prettierignore b/packages/rest-api-sdk/.prettierignore index 1ea6743a..71e1d20f 100644 --- a/packages/rest-api-sdk/.prettierignore +++ b/packages/rest-api-sdk/.prettierignore @@ -1,2 +1,3 @@ dist/ src/generated/ +**/.next/ diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/README.md b/packages/rest-api-sdk/examples/customer-admin-panel/README.md index 666f9af5..873c6ea0 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/README.md +++ b/packages/rest-api-sdk/examples/customer-admin-panel/README.md @@ -21,4 +21,5 @@ yarn dev ``` Visit: + - http://localhost:3000/ diff --git a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/AppEnvForm.tsx b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/AppEnvForm.tsx index c971fa26..a87cff56 100644 --- a/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/AppEnvForm.tsx +++ b/packages/rest-api-sdk/examples/customer-admin-panel/app/flags/AppEnvForm.tsx @@ -31,7 +31,11 @@ export default function AppEnvForm({ >