From 8755661049998a5845d0dff4e0f6266b72354c9f Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Tue, 10 Mar 2026 18:56:14 -0700 Subject: [PATCH] feat(web): wire feature-flag and kill-switch platform clients MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added @bytelyst/feature-flag-client and @bytelyst/kill-switch-client deps - Created web/src/lib/feature-flags.ts — poll-based flag client (fail-open) - Created web/src/lib/kill-switch.ts — kill-switch check client (fail-open) - Telemetry, diagnostics, extraction were already wired Verification: web typecheck + 6 tests pass. --- web/package-lock.json | 24 ++++++++++++++++++++++-- web/package.json | 2 ++ web/src/lib/feature-flags.ts | 28 ++++++++++++++++++++++++++++ web/src/lib/kill-switch.ts | 16 ++++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 web/src/lib/feature-flags.ts create mode 100644 web/src/lib/kill-switch.ts diff --git a/web/package-lock.json b/web/package-lock.json index d2db470..ae9d892 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,17 +1,19 @@ { - "name": "bytelyst-agentic-notes-web", + "name": "@notelett/web", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "bytelyst-agentic-notes-web", + "name": "@notelett/web", "version": "0.1.0", "dependencies": { "@bytelyst/api-client": "file:../../learning_ai_common_plat/packages/api-client", "@bytelyst/auth-client": "file:../../learning_ai_common_plat/packages/auth-client", "@bytelyst/design-tokens": "file:../../learning_ai_common_plat/packages/design-tokens", "@bytelyst/diagnostics-client": "file:../../learning_ai_common_plat/packages/diagnostics-client", + "@bytelyst/feature-flag-client": "file:../../learning_ai_common_plat/packages/feature-flag-client", + "@bytelyst/kill-switch-client": "file:../../learning_ai_common_plat/packages/kill-switch-client", "@bytelyst/platform-client": "file:../../learning_ai_common_plat/packages/platform-client", "@bytelyst/react-auth": "file:../../learning_ai_common_plat/packages/react-auth", "@bytelyst/telemetry-client": "file:../../learning_ai_common_plat/packages/telemetry-client", @@ -67,6 +69,14 @@ "zod": "^3.22.0" } }, + "../../learning_ai_common_plat/packages/feature-flag-client": { + "name": "@bytelyst/feature-flag-client", + "version": "0.1.0" + }, + "../../learning_ai_common_plat/packages/kill-switch-client": { + "name": "@bytelyst/kill-switch-client", + "version": "0.1.0" + }, "../../learning_ai_common_plat/packages/platform-client": { "name": "@bytelyst/platform-client", "version": "0.1.0" @@ -458,6 +468,14 @@ "resolved": "../../learning_ai_common_plat/packages/diagnostics-client", "link": true }, + "node_modules/@bytelyst/feature-flag-client": { + "resolved": "../../learning_ai_common_plat/packages/feature-flag-client", + "link": true + }, + "node_modules/@bytelyst/kill-switch-client": { + "resolved": "../../learning_ai_common_plat/packages/kill-switch-client", + "link": true + }, "node_modules/@bytelyst/platform-client": { "resolved": "../../learning_ai_common_plat/packages/platform-client", "link": true @@ -4801,6 +4819,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -8454,6 +8473,7 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", diff --git a/web/package.json b/web/package.json index b00e7ef..34e7920 100644 --- a/web/package.json +++ b/web/package.json @@ -16,6 +16,8 @@ "@bytelyst/auth-client": "file:../../learning_ai_common_plat/packages/auth-client", "@bytelyst/design-tokens": "file:../../learning_ai_common_plat/packages/design-tokens", "@bytelyst/diagnostics-client": "file:../../learning_ai_common_plat/packages/diagnostics-client", + "@bytelyst/feature-flag-client": "file:../../learning_ai_common_plat/packages/feature-flag-client", + "@bytelyst/kill-switch-client": "file:../../learning_ai_common_plat/packages/kill-switch-client", "@bytelyst/platform-client": "file:../../learning_ai_common_plat/packages/platform-client", "@bytelyst/react-auth": "file:../../learning_ai_common_plat/packages/react-auth", "@bytelyst/telemetry-client": "file:../../learning_ai_common_plat/packages/telemetry-client", diff --git a/web/src/lib/feature-flags.ts b/web/src/lib/feature-flags.ts new file mode 100644 index 0000000..f2d296a --- /dev/null +++ b/web/src/lib/feature-flags.ts @@ -0,0 +1,28 @@ +"use client"; + +import { createFeatureFlagClient } from "@bytelyst/feature-flag-client"; +import { PLATFORM_SERVICE_URL, PRODUCT_ID } from "@/lib/product-config"; + +let initialized = false; + +const featureFlagClient = createFeatureFlagClient({ + baseUrl: PLATFORM_SERVICE_URL, + productId: PRODUCT_ID, + platform: "web", + pollIntervalMs: 5 * 60 * 1000, +}); + +export async function initFeatureFlags(userId?: string) { + if (initialized) return featureFlagClient; + await featureFlagClient.init({ userId }).catch(() => { + // Feature flags are best-effort — fail-open + }); + initialized = true; + return featureFlagClient; +} + +export function isFeatureEnabled(key: string): boolean { + return featureFlagClient.isEnabled(key); +} + +export { featureFlagClient }; diff --git a/web/src/lib/kill-switch.ts b/web/src/lib/kill-switch.ts new file mode 100644 index 0000000..6eaa19a --- /dev/null +++ b/web/src/lib/kill-switch.ts @@ -0,0 +1,16 @@ +"use client"; + +import { createKillSwitchClient } from "@bytelyst/kill-switch-client"; +import { PLATFORM_SERVICE_URL, PRODUCT_ID } from "@/lib/product-config"; + +const killSwitchClient = createKillSwitchClient({ + baseUrl: PLATFORM_SERVICE_URL, + productId: PRODUCT_ID, + platform: "web", +}); + +export async function checkKillSwitch(): Promise<{ disabled: boolean; message: string | null }> { + return killSwitchClient.check(); +} + +export { killSwitchClient };