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:
parent
9438085cc0
commit
5195f9c052
@ -43,6 +43,21 @@ const securityHeaders = [
|
|||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
...(process.env.VERCEL ? {} : { output: 'standalone' }),
|
...(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() {
|
async headers() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
* any route handlers or Cosmos client initialization.
|
* 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() {
|
export async function register() {
|
||||||
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
||||||
|
|||||||
17
dashboards/admin-web/src/lib/product-constants.ts
Normal file
17
dashboards/admin-web/src/lib/product-constants.ts
Normal 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;
|
||||||
@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
|
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';
|
const STORAGE_KEY = 'admin_selected_product';
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,14 @@
|
|||||||
".": {
|
".": {
|
||||||
"import": "./dist/index.js",
|
"import": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts"
|
"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",
|
"main": "./dist/index.js",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { describe, it, expect, vi } from 'vitest';
|
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';
|
import type { ApiClient } from '@bytelyst/api-client';
|
||||||
|
|
||||||
describe('FeedbackClient', () => {
|
describe('FeedbackClient', () => {
|
||||||
@ -30,16 +30,19 @@ describe('FeedbackClient', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toEqual(mockResponse);
|
expect(result).toEqual(mockResponse);
|
||||||
expect(mockApi.fetch).toHaveBeenCalledWith('/api/feedback', expect.objectContaining({
|
expect(mockApi.fetch).toHaveBeenCalledWith(
|
||||||
method: 'POST',
|
'/api/feedback',
|
||||||
}));
|
expect.objectContaining({
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if captureAndSubmit called without screenshot', async () => {
|
it('should throw if captureAndSubmit called without screenshot', async () => {
|
||||||
const client = createClient();
|
const client = createClient();
|
||||||
|
|
||||||
await expect(
|
await expect(client.captureAndSubmit({ type: 'bug', title: 'Test' })).rejects.toThrow(
|
||||||
client.captureAndSubmit({ type: 'bug', title: 'Test' })
|
'Screenshot capture only available in browser environment'
|
||||||
).rejects.toThrow('TODO-1');
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -220,11 +220,12 @@ describe('Sync Engine', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await engine.push('tasks', { title: 'Test' });
|
await engine.push('tasks', { title: 'Test' });
|
||||||
|
const requestsBefore = apiClient.getRequests().length;
|
||||||
await engine.reprocessFailed();
|
await engine.reprocessFailed();
|
||||||
|
|
||||||
// Items should be reprocessed
|
// reprocessFailed calls flush() which pushes the item
|
||||||
const result = await engine.fullSync();
|
const requestsAfter = apiClient.getRequests().length;
|
||||||
expect(result.pushed).toBe(1);
|
expect(requestsAfter).toBeGreaterThan(requestsBefore);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user