From 5195f9c0522b6f42ae414012c3452da44f032846 Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Thu, 12 Mar 2026 16:49:15 -0700 Subject: [PATCH] =?UTF-8?q?fix(platform):=20production=20readiness=20?= =?UTF-8?q?=E2=80=94=20admin-web=20client=20bundling,=20config=20sub-path?= =?UTF-8?q?=20exports,=20stale=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dashboards/admin-web: split product-constants.ts for client-safe imports - dashboards/admin-web: serverExternalPackages + webpack fallbacks for @bytelyst/config - dashboards/admin-web: instrumentation.ts uses @bytelyst/config/keyvault sub-path - packages/config: add ./keyvault and ./product-identity sub-path exports - packages/feedback-client: fix stale test expectation (TODO-1 → actual error message) - packages/sync: fix reprocessFailed test (flush already pushes items) --- dashboards/admin-web/next.config.ts | 15 +++++++++++++++ dashboards/admin-web/src/instrumentation.ts | 2 +- .../admin-web/src/lib/product-constants.ts | 17 +++++++++++++++++ .../admin-web/src/lib/product-context.tsx | 2 +- packages/config/package.json | 8 ++++++++ packages/feedback-client/src/index.test.ts | 17 ++++++++++------- packages/sync/src/sync.test.ts | 7 ++++--- 7 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 dashboards/admin-web/src/lib/product-constants.ts diff --git a/dashboards/admin-web/next.config.ts b/dashboards/admin-web/next.config.ts index 7fcce266..ce85c91a 100644 --- a/dashboards/admin-web/next.config.ts +++ b/dashboards/admin-web/next.config.ts @@ -43,6 +43,21 @@ const securityHeaders = [ const nextConfig: NextConfig = { ...(process.env.VERCEL ? {} : { output: 'standalone' }), + serverExternalPackages: ['@bytelyst/config'], + webpack: (config, { isServer }) => { + if (!isServer) { + // Prevent Node.js modules from being bundled in client code + config.resolve = config.resolve ?? {}; + config.resolve.fallback = { + ...config.resolve.fallback, + fs: false, + path: false, + 'fs/promises': false, + crypto: false, + }; + } + return config; + }, async headers() { return [ { diff --git a/dashboards/admin-web/src/instrumentation.ts b/dashboards/admin-web/src/instrumentation.ts index 23a93780..53f14cc3 100644 --- a/dashboards/admin-web/src/instrumentation.ts +++ b/dashboards/admin-web/src/instrumentation.ts @@ -4,7 +4,7 @@ * any route handlers or Cosmos client initialization. */ -import { resolveSecrets, LYSNR_SECRETS } from '@bytelyst/config'; +import { resolveSecrets, LYSNR_SECRETS } from '@bytelyst/config/keyvault'; export async function register() { if (process.env.NEXT_RUNTIME === 'nodejs') { diff --git a/dashboards/admin-web/src/lib/product-constants.ts b/dashboards/admin-web/src/lib/product-constants.ts new file mode 100644 index 00000000..3138bf8c --- /dev/null +++ b/dashboards/admin-web/src/lib/product-constants.ts @@ -0,0 +1,17 @@ +/** + * Client-safe product constants — no Node.js imports. + * + * Use this in 'use client' components. For server-side code that needs + * loadProductIdentity(), import from product-config.ts instead. + */ + +export const PRODUCT_ID = process.env.NEXT_PUBLIC_PRODUCT_ID || process.env.PRODUCT_ID || 'lysnrai'; +export const DISPLAY_NAME = process.env.NEXT_PUBLIC_DISPLAY_NAME || 'LysnrAI'; + +/** All known products in the ByteLyst ecosystem. */ +export const KNOWN_PRODUCTS = [ + { id: 'lysnrai', name: 'LysnrAI', icon: 'Mic' }, + { id: 'chronomind', name: 'ChronoMind', icon: 'Clock' }, + { id: 'nomgap', name: 'NomGap', icon: 'Apple' }, + { id: 'mindlyst', name: 'MindLyst', icon: 'Brain' }, +] as const; diff --git a/dashboards/admin-web/src/lib/product-context.tsx b/dashboards/admin-web/src/lib/product-context.tsx index faad6f69..fb1b1299 100644 --- a/dashboards/admin-web/src/lib/product-context.tsx +++ b/dashboards/admin-web/src/lib/product-context.tsx @@ -1,7 +1,7 @@ 'use client'; import { createContext, useContext, useState, useCallback, type ReactNode } from 'react'; -import { KNOWN_PRODUCTS, PRODUCT_ID } from '@/lib/product-config'; +import { KNOWN_PRODUCTS, PRODUCT_ID } from '@/lib/product-constants'; const STORAGE_KEY = 'admin_selected_product'; diff --git a/packages/config/package.json b/packages/config/package.json index 17a7293e..0acb53d5 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -6,6 +6,14 @@ ".": { "import": "./dist/index.js", "types": "./dist/index.d.ts" + }, + "./keyvault": { + "import": "./dist/keyvault.js", + "types": "./dist/keyvault.d.ts" + }, + "./product-identity": { + "import": "./dist/product-identity.js", + "types": "./dist/product-identity.d.ts" } }, "main": "./dist/index.js", diff --git a/packages/feedback-client/src/index.test.ts b/packages/feedback-client/src/index.test.ts index 608f8a5b..dbbe6933 100644 --- a/packages/feedback-client/src/index.test.ts +++ b/packages/feedback-client/src/index.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi } from 'vitest'; -import { FeedbackClient, type SubmitFeedbackParams } from './index.js'; +import { FeedbackClient } from './index.js'; import type { ApiClient } from '@bytelyst/api-client'; describe('FeedbackClient', () => { @@ -30,16 +30,19 @@ describe('FeedbackClient', () => { }); expect(result).toEqual(mockResponse); - expect(mockApi.fetch).toHaveBeenCalledWith('/api/feedback', expect.objectContaining({ - method: 'POST', - })); + expect(mockApi.fetch).toHaveBeenCalledWith( + '/api/feedback', + expect.objectContaining({ + method: 'POST', + }) + ); }); it('should throw if captureAndSubmit called without screenshot', async () => { const client = createClient(); - await expect( - client.captureAndSubmit({ type: 'bug', title: 'Test' }) - ).rejects.toThrow('TODO-1'); + await expect(client.captureAndSubmit({ type: 'bug', title: 'Test' })).rejects.toThrow( + 'Screenshot capture only available in browser environment' + ); }); }); diff --git a/packages/sync/src/sync.test.ts b/packages/sync/src/sync.test.ts index 501e977c..5f5095f7 100644 --- a/packages/sync/src/sync.test.ts +++ b/packages/sync/src/sync.test.ts @@ -220,11 +220,12 @@ describe('Sync Engine', () => { }); await engine.push('tasks', { title: 'Test' }); + const requestsBefore = apiClient.getRequests().length; await engine.reprocessFailed(); - // Items should be reprocessed - const result = await engine.fullSync(); - expect(result.pushed).toBe(1); + // reprocessFailed calls flush() which pushes the item + const requestsAfter = apiClient.getRequests().length; + expect(requestsAfter).toBeGreaterThan(requestsBefore); }); }); });