fix(platform): production readiness — admin-web client bundling, config sub-path exports, stale tests

- 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)
This commit is contained in:
saravanakumardb1 2026-03-12 16:49:15 -07:00
parent 9438085cc0
commit 5195f9c052
7 changed files with 56 additions and 12 deletions

View File

@ -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 [
{

View File

@ -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') {

View File

@ -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;

View File

@ -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';

View File

@ -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",

View File

@ -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'
);
});
});

View File

@ -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);
});
});
});