UNPKG

@dbs-portal/core-api

Version:

HTTP client and API utilities for DBS Portal

349 lines (306 loc) 9.29 kB
/** * Build utilities for MSW integration */ // Use path-browserify for browser environments import * as nodePath from 'path-browserify'; // Detect if we're running in a Node.js environment const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null; // Initialize fs with empty functions for browser environments let fs = { existsSync: () => false, mkdirSync: () => { }, writeFileSync: () => { } }; // Dynamically import Node.js modules only in Node environment if (isNode) { try { // Use dynamic import for Node.js modules fs = require('node:fs'); } catch (error) { console.error('Failed to import Node.js fs module:', error); } } /** * Cross-platform path resolver */ function resolvePath(...paths) { if (isNode) { // Use Node.js path for server return require('node:path').resolve(...paths); } // Use path-browserify for browser return paths.reduce((acc, path) => nodePath.resolve(acc, path), ''); } /** * Initialize MSW service worker in public directory */ export async function initializeMSWServiceWorker(publicDir = 'public', filename = 'mockServiceWorker.js') { if (!isNode) { console.warn('MSW service worker initialization is only supported in Node.js environment'); return; } const publicPath = resolvePath(process.cwd(), publicDir); const serviceWorkerPath = resolvePath(publicPath, filename); // Create public directory if it doesn't exist if (!fs.existsSync(publicPath)) { fs.mkdirSync(publicPath, { recursive: true }); } // Check if service worker already exists if (fs.existsSync(serviceWorkerPath)) { console.log(`MSW service worker already exists at ${serviceWorkerPath}`); return; } try { // Try to use MSW CLI to generate service worker const { exec } = await import('node:child_process'); const { promisify } = await import('node:util'); const execAsync = promisify(exec); await execAsync(`npx msw init ${publicDir} --save`); console.log(`MSW service worker initialized at ${serviceWorkerPath}`); } catch (error) { console.warn('Failed to initialize MSW service worker via CLI, creating manually...'); // Fallback: create service worker manually const serviceWorkerContent = await generateServiceWorkerContent(); fs.writeFileSync(serviceWorkerPath, serviceWorkerContent, 'utf-8'); console.log(`MSW service worker created manually at ${serviceWorkerPath}`); } } /** * Generate service worker content */ async function generateServiceWorkerContent() { return ` /** * Mock Service Worker (MSW) Service Worker * Generated by @dbs-portal/core-api */ /* eslint-disable */ /* tslint:disable */ const INTEGRITY_CHECKSUM = '02f4ad4a2ca266d84b5226dda4ca245d' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() self.addEventListener('install', function () { self.skipWaiting() }) self.addEventListener('activate', function (event) { event.waitUntil(self.clients.claim()) }) self.addEventListener('message', async function (event) { const clientId = event.source.id if (!clientId || !event.data) { return } const allClients = await self.clients.matchAll({ type: 'window', }) switch (event.data.type) { case 'KEEPALIVE_REQUEST': { sendToClient(event.source, { type: 'KEEPALIVE_RESPONSE', payload: Date.now(), }) break } case 'INTEGRITY_CHECK_REQUEST': { sendToClient(event.source, { type: 'INTEGRITY_CHECK_RESPONSE', payload: INTEGRITY_CHECKSUM, }) break } case 'MOCK_ACTIVATE': { activeClientIds.add(clientId) sendToClient(event.source, { type: 'MOCKING_ENABLED', payload: true, }) break } case 'MOCK_DEACTIVATE': { activeClientIds.delete(clientId) break } case 'CLIENT_CLOSED': { activeClientIds.delete(clientId) const remainingClients = allClients.filter((client) => { return client.id !== clientId }) if (remainingClients.length === 0) { self.registration.unregister() } break } } }) self.addEventListener('fetch', function (event) { const { request } = event if (request.method === 'GET' && request.headers.get('accept')?.includes('text/html')) { return } if (activeClientIds.size === 0) { return } const requestId = Math.random().toString(16).slice(2) event.respondWith( handleRequest(event, requestId).catch((error) => { if (error.name === 'NetworkError') { console.warn( '[MSW] Successfully emulated a network error for the "%s %s" request.', request.method, request.url, ) return } console.error( '[MSW] Caught an exception from the "%s %s" request (%s).', request.method, request.url, \`\${error.name}: \${error.message}\`, ) }), ) }) async function handleRequest(event, requestId) { const client = await resolveMainClient(event) const response = await getResponse(event, client, requestId) if (client && activeClientIds.has(client.id)) { ;(response.headers || {}).forEach((value, name) => { event.response.headers.append(name, value) }) } return response } async function resolveMainClient(event) { const url = new URL(event.request.url) if (url.origin === self.location.origin) { const allClients = await self.clients.matchAll({ type: 'window', }) return allClients .filter((client) => activeClientIds.has(client.id)) .find((client) => { const clientUrl = new URL(client.url) return clientUrl.pathname === url.pathname }) } return await self.clients.get(event.clientId) } async function getResponse(event, client, requestId) { const { request } = event const clonedRequest = request.clone() function passthrough() { return fetch(clonedRequest) } if (!client) { return passthrough() } const requestBuffer = await request.arrayBuffer() const clientMessage = await sendToClient(client, { type: 'REQUEST', payload: { id: requestId, url: request.url, method: request.method, headers: Object.fromEntries(request.headers.entries()), cache: request.cache, mode: request.mode, credentials: request.credentials, destination: request.destination, integrity: request.integrity, redirect: request.redirect, referrer: request.referrer, referrerPolicy: request.referrerPolicy, body: requestBuffer, bodyUsed: request.bodyUsed, keepalive: request.keepalive, }, }) switch (clientMessage.type) { case 'MOCK_RESPONSE': { return respondWithMock(clientMessage.data) } case 'MOCK_NOT_FOUND': { return passthrough() } case 'NETWORK_ERROR': { const error = new Error(clientMessage.data) error.name = 'NetworkError' throw error } } return passthrough() } function sendToClient(client, message) { return new Promise((resolve, reject) => { const channel = new MessageChannel() channel.port1.onmessage = (event) => { if (event.data && event.data.error) { return reject(event.data.error) } resolve(event.data) } client.postMessage(message, [channel.port2]) }) } function respondWithMock(response) { return new Response(response.body, response) } `.trim(); } /** * Check if MSW service worker is properly installed */ export function checkMSWServiceWorker(publicDir = 'public', filename = 'mockServiceWorker.js') { if (!isNode) { console.warn('MSW service worker check is only supported in Node.js environment'); return false; } const serviceWorkerPath = resolvePath(process.cwd(), publicDir, filename); return fs.existsSync(serviceWorkerPath); } /** * Get MSW build configuration for different environments */ export function getMSWBuildConfig(environment) { const baseConfig = { enabled: false, mode: environment, publicDir: 'public', serviceWorkerFilename: 'mockServiceWorker.js', autoGenerateServiceWorker: true, verbose: false, }; switch (environment) { case 'development': return { ...baseConfig, enabled: process.env['VITE_ENABLE_MOCKING'] === 'true', verbose: true, }; case 'testing': return { ...baseConfig, enabled: true, autoGenerateServiceWorker: false, // Not needed for Node.js tests verbose: false, }; case 'storybook': return { ...baseConfig, enabled: true, publicDir: '.storybook/public', verbose: true, }; case 'production': default: return { ...baseConfig, enabled: false, }; } } //# sourceMappingURL=build-utils.js.map