🎯 Exemples recommandés
Balanced sample collections from various categories for you to explore
Exemples de Fonctionnalités Web Web TypeScript
Exemples de fonctionnalités web Web TypeScript incluant le routage, le middleware et le service de fichiers statiques
💻 Routage typescript
🟡 intermediate
⭐⭐⭐
Analyser les routes URL, gérer le routage de hachage et gérer l'historique du navigateur
⏱️ 25 min
🏷️ typescript, web, web features
Prerequisites:
Intermediate TypeScript, History API, URL API
// Web TypeScript Routing Examples
// Client-side routing with URL parsing, hash routing, and history management
// 1. Route Interface
interface Route {
path: string;
handler: (params: Record<string, string>, query: Record<string, string>) => void;
}
// 2. Router
class Router {
private routes: Route[] = [];
private notFoundHandler?: () => void;
private currentParams: Record<string, string> = {};
private currentQuery: Record<string, string> = {};
// Add route
addRoute(path: string, handler: Route['handler']): void {
this.routes.push({ path, handler });
}
// Set 404 handler
setNotFound(handler: () => void): void {
this.notFoundHandler = handler;
}
// Navigate to path
navigate(path: string, params: Record<string, string> = {}): void {
// Build URL with params
let url = path;
if (Object.keys(params).length > 0) {
url = this.replaceParams(path, params);
}
// Update browser URL
window.history.pushState({}, '', url);
// Handle route
this.handleRoute(url);
}
// Replace current route
replace(path: string, params: Record<string, string> = {}): void {
let url = path;
if (Object.keys(params).length > 0) {
url = this.replaceParams(path, params);
}
window.history.replaceState({}, '', url);
this.handleRoute(url);
}
// Go back
back(): void {
window.history.back();
}
// Go forward
forward(): void {
window.history.forward();
}
// Go to specific history entry
go(delta: number): void {
window.history.go(delta);
}
// Handle route change
private handleRoute(url: string): void {
const { path, query } = this.parseURL(url);
// Find matching route
const route = this.findRoute(path);
if (route) {
const params = this.extractParams(path, route.path);
this.currentParams = params;
this.currentQuery = query;
route.handler(params, query);
} else if (this.notFoundHandler) {
this.notFoundHandler();
}
}
// Find matching route
private findRoute(path: string): Route | null {
for (const route of this.routes) {
if (this.matchRoute(path, route.path)) {
return route;
}
}
return null;
}
// Check if path matches route pattern
private matchRoute(path: string, pattern: string): boolean {
// Convert pattern to regex
const regexPattern = pattern
.replace(/:\w+/g, '([^/]+)')
.replace(/\*/g, '.*');
const regex = new RegExp(`^${regexPattern}$`);
return regex.test(path);
}
// Extract params from path
private extractParams(path: string, pattern: string): Record<string, string> {
const params: Record<string, string> = {};
const patternParts = pattern.split('/');
const pathParts = path.split('/');
for (let i = 0; i < patternParts.length; i++) {
const part = patternParts[i];
if (part.startsWith(':')) {
const paramName = part.substring(1);
params[paramName] = pathParts[i] || '';
}
}
return params;
}
// Replace params in path
private replaceParams(path: string, params: Record<string, string>): string {
let result = path;
for (const [key, value] of Object.entries(params)) {
result = result.replace(`:${key}`, value);
}
return result;
}
// Parse URL into path and query
private parseURL(url: string): {
path: string;
query: Record<string, string>;
} {
const [path, queryString] = url.split('?');
const query: Record<string, string> = {};
if (queryString) {
const params = new URLSearchParams(queryString);
params.forEach((value, key) => {
query[key] = value;
});
}
return { path, query };
}
// Initialize router
initialize(): void {
// Handle popstate event (back/forward buttons)
window.addEventListener('popstate', () => {
this.handleRoute(window.location.pathname);
});
// Handle initial route
this.handleRoute(window.location.pathname);
}
}
// 3. Hash Router
class HashRouter {
private routes: Route[] = [];
private notFoundHandler?: () => void;
// Add route
addRoute(path: string, handler: Route['handler']): void {
// Remove leading slash for hash matching
const hashPath = path.startsWith('/') ? path.substring(1) : path;
this.routes.push({ path: hashPath, handler });
}
// Set 404 handler
setNotFound(handler: () => void): void {
this.notFoundHandler = handler;
}
// Navigate to hash
navigate(hash: string, params: Record<string, string> = {}): void {
let url = hash;
if (Object.keys(params).length > 0) {
url = this.replaceParams(hash, params);
}
window.location.hash = url;
}
// Get current hash
getHash(): string {
return window.location.hash.substring(1) || '/';
}
// Handle hash change
private handleHashChange(): void {
const hash = this.getHash();
const [path, queryString] = hash.split('?');
const query: Record<string, string> = {};
if (queryString) {
const params = new URLSearchParams(queryString);
params.forEach((value, key) => {
query[key] = value;
});
}
// Find matching route
const route = this.findRoute(path);
if (route) {
const params = this.extractParams(path, route.path);
route.handler(params, query);
} else if (this.notFoundHandler) {
this.notFoundHandler();
}
}
// Find matching route
private findRoute(path: string): Route | null {
for (const route of this.routes) {
if (this.matchRoute(path, route.path)) {
return route;
}
}
return null;
}
// Check if path matches route pattern
private matchRoute(path: string, pattern: string): boolean {
const regexPattern = pattern
.replace(/:\w+/g, '([^/]+)')
.replace(/\*/g, '.*');
const regex = new RegExp(`^${regexPattern}$`);
return regex.test(path);
}
// Extract params from path
private extractParams(path: string, pattern: string): Record<string, string> {
const params: Record<string, string> = {};
const patternParts = pattern.split('/');
const pathParts = path.split('/');
for (let i = 0; i < patternParts.length; i++) {
const part = patternParts[i];
if (part.startsWith(':')) {
const paramName = part.substring(1);
params[paramName] = pathParts[i] || '';
}
}
return params;
}
// Replace params in path
private replaceParams(path: string, params: Record<string, string>): string {
let result = path;
for (const [key, value] of Object.entries(params)) {
result = result.replace(`:${key}`, value);
}
return result;
}
// Initialize hash router
initialize(): void {
window.addEventListener('hashchange', () => {
this.handleHashChange();
});
// Handle initial hash
this.handleHashChange();
}
}
// 4. Query Params Manager
class QueryParamsManager {
// Get query param
get(param: string): string | null {
const params = new URLSearchParams(window.location.search);
return params.get(param);
}
// Get all params
getAll(): Record<string, string> {
const params = new URLSearchParams(window.location.search);
const result: Record<string, string> = {};
params.forEach((value, key) => {
result[key] = value;
});
return result;
}
// Set query param
set(param: string, value: string): void {
const params = new URLSearchParams(window.location.search);
params.set(param, value);
this.update(params.toString());
}
// Delete query param
delete(param: string): void {
const params = new URLSearchParams(window.location.search);
params.delete(param);
this.update(params.toString());
}
// Clear all params
clear(): void {
this.update('');
}
// Update URL
private update(queryString: string): void {
const url = new URL(window.location.href);
url.search = queryString;
window.history.replaceState({}, '', url.toString());
}
// Watch for changes
watch(callback: (params: Record<string, string>) => void): () => void {
const handler = () => {
callback(this.getAll());
};
window.addEventListener('popstate', handler);
// Return cleanup function
return () => {
window.removeEventListener('popstate', handler);
};
}
}
// 5. Route Guard
class RouteGuard {
private guards: Map<string, () => boolean | Promise<boolean>> = new Map();
// Add guard for route
addGuard(route: string, guard: () => boolean | Promise<boolean>): void {
this.guards.set(route, guard);
}
// Check if route is allowed
async canNavigate(route: string): Promise<boolean> {
const guard = this.guards.get(route);
if (guard) {
return await guard();
}
return true;
}
// Add authentication guard
addAuthGuard(route: string, isAuthenticated: () => boolean): void {
this.addGuard(route, isAuthenticated);
}
// Add permission guard
addPermissionGuard(route: string, hasPermission: () => boolean | Promise<boolean>): void {
this.addGuard(route, hasPermission);
}
}
// 6. Navigation Manager
class NavigationManager {
private router: Router;
private history: string[] = [];
private currentIndex: number = -1;
constructor(router: Router) {
this.router = router;
}
// Navigate with history tracking
navigate(path: string, params: Record<string, string> = {}): void {
// Remove forward history if we're not at the end
if (this.currentIndex < this.history.length - 1) {
this.history = this.history.slice(0, this.currentIndex + 1);
}
// Add to history
this.history.push(path);
this.currentIndex++;
// Navigate
this.router.navigate(path, params);
}
// Go back in custom history
back(): void {
if (this.currentIndex > 0) {
this.currentIndex--;
const path = this.history[this.currentIndex];
window.history.back();
}
}
// Go forward in custom history
forward(): void {
if (this.currentIndex < this.history.length - 1) {
this.currentIndex++;
const path = this.history[this.currentIndex];
window.history.forward();
}
}
// Can go back
canGoBack(): boolean {
return this.currentIndex > 0;
}
// Can go forward
canGoForward(): boolean {
return this.currentIndex < this.history.length - 1;
}
// Get current position
getCurrentPosition(): number {
return this.currentIndex;
}
// Get history length
getHistoryLength(): number {
return this.history.length;
}
}
// 7. Route Transition Manager
class RouteTransitionManager {
private transitions: Map<string, (from: string, to: string) => void> = new Map();
// Add transition
addTransition(from: string, to: string, handler: () => void): void {
const key = this.getKey(from, to);
this.transitions.set(key, handler);
}
// Execute transition
execute(from: string, to: string): void {
const key = this.getKey(from, to);
const handler = this.transitions.get(key);
if (handler) {
handler(from, to);
}
}
// Add global transition
addGlobalTransition(handler: (from: string, to: string) => void): void {
this.transitions.set('*', handler);
}
// Generate transition key
private getKey(from: string, to: string): string {
return `${from}>${to}`;
}
}
// Usage Examples
async function demonstrateRouting() {
console.log('=== Web TypeScript Routing Examples ===\n');
// 1. Basic router
console.log('--- 1. Basic Router ---');
const router = new Router();
router.addRoute('/', (params, query) => {
console.log('Home page');
});
router.addRoute('/about', (params, query) => {
console.log('About page');
});
router.addRoute('/users/:id', (params, query) => {
console.log(`User page: ${params.id}`);
});
router.addRoute('/posts/:postId/comments/:commentId', (params, query) => {
console.log(`Comment: ${params.commentId} on post ${params.postId}`);
});
// 2. Navigation
console.log('\n--- 2. Navigation ---');
router.navigate('/');
await new Promise(resolve => setTimeout(resolve, 100));
router.navigate('/about');
await new Promise(resolve => setTimeout(resolve, 100));
router.navigate('/users/123');
await new Promise(resolve => setTimeout(resolve, 100));
// 3. Query params
console.log('\n--- 3. Query Params ---');
router.navigate('/search?query=typescript&page=1');
await new Promise(resolve => setTimeout(resolve, 100));
// 4. Hash router
console.log('\n--- 4. Hash Router ---');
const hashRouter = new HashRouter();
hashRouter.addRoute('/', (params, query) => {
console.log('Hash home');
});
hashRouter.addRoute('profile/:userId', (params, query) => {
console.log(`Hash profile: ${params.userId}`);
});
hashRouter.navigate('profile/456');
await new Promise(resolve => setTimeout(resolve, 100));
// 5. Query params manager
console.log('\n--- 5. Query Params Manager ---');
const queryManager = new QueryParamsManager();
queryManager.set('tab', 'profile');
queryManager.set('section', 'details');
const allParams = queryManager.getAll();
console.log('All params:', allParams);
// 6. Route guard
console.log('\n--- 6. Route Guard ---');
const guard = new RouteGuard();
guard.addAuthGuard('/admin', () => {
const isAuthenticated = false; // Simulate
console.log(`Auth check: ${isAuthenticated}`);
return isAuthenticated;
});
const canAccess = await guard.canNavigate('/admin');
console.log(`Can access /admin: ${canAccess}`);
// 7. Navigation manager
console.log('\n--- 7. Navigation Manager ---');
const navManager = new NavigationManager(router);
navManager.navigate('/page1');
await new Promise(resolve => setTimeout(resolve, 100));
navManager.navigate('/page2');
await new Promise(resolve => setTimeout(resolve, 100));
console.log(`Can go back: ${navManager.canGoBack()}`);
console.log(`History length: ${navManager.getHistoryLength()}`);
console.log('\n=== All Routing Examples Completed ===');
}
// Export functions
export { Router, HashRouter, QueryParamsManager, RouteGuard, NavigationManager, RouteTransitionManager };
export { demonstrateRouting };
export type { Route };
💻 Middleware typescript
🟡 intermediate
⭐⭐⭐⭐
Implémenter une chaîne de middleware requête/réponse pour traiter et modifier des données
⏱️ 30 min
🏷️ typescript, web, web features
Prerequisites:
Intermediate TypeScript, Promise chains, Async/await
// Web TypeScript Middleware Examples
// Request/response processing middleware chain
// 1. Context Interface
interface Context {
request: any;
response: any;
state: Map<string, any>;
headers: Map<string, string>;
metadata: Record<string, any>;
}
// 2. Middleware Function Type
type Middleware = (context: Context, next: () => Promise<void>) => Promise<void>;
// 3. Middleware Pipeline
class MiddlewarePipeline {
private middlewares: Middleware[] = [];
// Add middleware
use(middleware: Middleware): this {
this.middlewares.push(middleware);
return this;
}
// Execute pipeline
async execute(context: Context): Promise<void> {
let index = 0;
const next = async (): Promise<void> => {
if (index < this.middlewares.length) {
const middleware = this.middlewares[index++];
await middleware(context, next);
}
};
await next();
}
// Clear all middlewares
clear(): void {
this.middlewares = [];
}
// Get middleware count
count(): number {
return this.middlewares.length;
}
}
// 4. Common Middlewares
class CommonMiddlewares {
// Logging middleware
static logging(): Middleware {
return async (context: Context, next: () => Promise<void>) => {
const start = performance.now();
console.log('[Request]', {
url: context.request?.url || 'N/A',
method: context.request?.method || 'N/A',
headers: Object.fromEntries(context.headers)
});
await next();
const duration = performance.now() - start;
console.log('[Response]', {
status: context.response?.status || 'N/A',
duration: `${duration.toFixed(2)}ms`
});
};
}
// Timing middleware
static timing(): Middleware {
return async (context: Context, next: () => Promise<void>) => {
const start = performance.now();
await next();
const duration = performance.now() - start;
context.metadata.duration = duration;
};
}
// Error handling middleware
static errorHandler(errorHandler: (error: Error) => void): Middleware {
return async (context: Context, next: () => Promise<void>) => {
try {
await next();
} catch (error) {
errorHandler(error as Error);
throw error;
}
};
}
// Header manipulation middleware
static headers(addHeaders: Record<string, string>): Middleware {
return async (context: Context, next: () => Promise<void>) => {
for (const [key, value] of Object.entries(addHeaders)) {
context.headers.set(key, value);
}
await next();
};
}
// Authentication middleware
static authentication(authCheck: (context: Context) => boolean | Promise<boolean>): Middleware {
return async (context: Context, next: () => Promise<void>) => {
const isAuthenticated = await authCheck(context);
if (!isAuthenticated) {
throw new Error('Unauthorized');
}
context.metadata.authenticated = true;
await next();
};
}
// Rate limiting middleware
static rateLimit(options: {
maxRequests: number;
windowMs: number;
}): Middleware {
const requests = new Map<string, number[]>();
return async (context: Context, next: () => Promise<void>) => {
const key = context.request?.ip || 'unknown';
const now = Date.now();
if (!requests.has(key)) {
requests.set(key, []);
}
const userRequests = requests.get(key)!;
// Remove old requests outside the window
const windowStart = now - options.windowMs;
const validRequests = userRequests.filter(time => time > windowStart);
// Check if limit exceeded
if (validRequests.length >= options.maxRequests) {
throw new Error('Rate limit exceeded');
}
// Add current request
validRequests.push(now);
requests.set(key, validRequests);
context.metadata.rateLimit = {
remaining: options.maxRequests - validRequests.length,
reset: now + options.windowMs
};
await next();
};
}
// CORS middleware
static cors(options: {
origin?: string | string[];
methods?: string[];
headers?: string[];
credentials?: boolean;
} = {}): Middleware {
return async (context: Context, next: () => Promise<void>) => {
const origin = context.request?.headers?.get('Origin') || '*';
// Set CORS headers
if (options.origin === '*' || !options.origin) {
context.headers.set('Access-Control-Allow-Origin', '*');
} else {
const allowedOrigins = Array.isArray(options.origin) ? options.origin : [options.origin];
if (allowedOrigins.includes(origin)) {
context.headers.set('Access-Control-Allow-Origin', origin);
}
}
if (options.methods) {
context.headers.set('Access-Control-Allow-Methods', options.methods.join(', '));
}
if (options.headers) {
context.headers.set('Access-Control-Allow-Headers', options.headers.join(', '));
}
if (options.credentials) {
context.headers.set('Access-Control-Allow-Credentials', 'true');
}
await next();
};
}
// Compression middleware (simulated)
static compression(): Middleware {
return async (context: Context, next: () => Promise<void>) => {
await next();
// Check if response should be compressed
const acceptEncoding = context.request?.headers?.get('Accept-Encoding') || '';
if (acceptEncoding.includes('gzip') || acceptEncoding.includes('br')) {
context.headers.set('Content-Encoding', 'gzip');
context.metadata.compressed = true;
}
};
}
// Body parser middleware
static bodyParser(): Middleware {
return async (context: Context, next: () => Promise<void>) => {
const contentType = context.request?.headers?.get('Content-Type') || '';
if (contentType.includes('application/json')) {
try {
const body = context.request?.body;
if (typeof body === 'string') {
context.request.parsedBody = JSON.parse(body);
}
} catch (error) {
throw new Error('Invalid JSON body');
}
}
await next();
};
}
// Validation middleware
static validation(validator: (context: Context) => boolean | Promise<boolean>): Middleware {
return async (context: Context, next: () => Promise<void>) => {
const isValid = await validator(context);
if (!isValid) {
throw new Error('Validation failed');
}
await next();
};
}
// Cache middleware
static cache(cacheKey: (context: Context) => string, ttl: number = 60000): Middleware {
const cache = new Map<string, { data: any; expiry: number }>();
return async (context: Context, next: () => Promise<void>) => {
const key = cacheKey(context);
const now = Date.now();
// Check cache
const cached = cache.get(key);
if (cached && cached.expiry > now) {
context.response = cached.data;
context.metadata.cached = true;
return;
}
await next();
// Store in cache
if (context.response) {
cache.set(key, {
data: context.response,
expiry: now + ttl
});
}
};
}
}
// 5. Middleware Composer
class MiddlewareComposer {
private pipeline: MiddlewarePipeline;
constructor() {
this.pipeline = new MiddlewarePipeline();
}
// Add middleware
use(middleware: Middleware): this {
this.pipeline.use(middleware);
return this;
}
// Add multiple middlewares
useAll(middlewares: Middleware[]): this {
middlewares.forEach(mw => this.pipeline.use(mw));
return this;
}
// Execute with context
async execute(context: Context): Promise<void> {
return this.pipeline.execute(context);
}
// Create from array
static from(middlewares: Middleware[]): MiddlewareComposer {
const composer = new MiddlewareComposer();
return composer.useAll(middlewares);
}
}
// 6. Request/Response Mock
class MockRequest {
constructor(
public url: string,
public method: string = 'GET',
public headers: Map<string, string> = new Map(),
public body?: any,
public ip: string = '127.0.0.1'
) {}
}
class MockResponse {
public status: number = 200;
public headers: Map<string, string> = new Map();
public body?: any;
setStatus(code: number): this {
this.status = code;
return this;
}
setHeader(key: string, value: string): this {
this.headers.set(key, value);
return this;
}
send(data: any): this {
this.body = data;
return this;
}
}
// 7. Application Builder
class ApplicationBuilder {
private composer: MiddlewareComposer;
private contextFactory: () => Context;
constructor() {
this.composer = new MiddlewareComposer();
this.contextFactory = () => ({
request: null,
response: null,
state: new Map(),
headers: new Map(),
metadata: {}
});
}
// Set context factory
setContextFactory(factory: () => Context): this {
this.contextFactory = factory;
return this;
}
// Add middleware
use(middleware: Middleware): this {
this.composer.use(middleware);
return this;
}
// Handle request
async handle(request: MockRequest): Promise<MockResponse> {
const context = this.contextFactory();
context.request = request;
context.response = new MockResponse();
await this.composer.execute(context);
return context.response as MockResponse;
}
}
// Usage Examples
async function demonstrateMiddleware() {
console.log('=== Web TypeScript Middleware Examples ===\n');
// 1. Basic middleware
console.log('--- 1. Basic Middleware ---');
const pipeline = new MiddlewarePipeline();
pipeline.use(async (context, next) => {
console.log('Middleware 1: Before');
await next();
console.log('Middleware 1: After');
});
pipeline.use(async (context, next) => {
console.log('Middleware 2: Before');
await next();
console.log('Middleware 2: After');
});
const context1: Context = {
request: { url: '/test' },
response: null,
state: new Map(),
headers: new Map(),
metadata: {}
};
await pipeline.execute(context1);
// 2. Common middlewares
console.log('\n--- 2. Common Middlewares ---');
const app = new ApplicationBuilder();
app.use(CommonMiddlewares.logging());
app.use(CommonMiddlewares.timing());
app.use(CommonMiddlewares.headers({
'X-Powered-By': 'TypeScript-Middleware',
'X-Response-Time': '0ms'
}));
const request = new MockRequest('/api/users', 'GET');
request.headers.set('Accept', 'application/json');
const response = await app.handle(request);
console.log('Response status:', response.status);
// 3. Error handling
console.log('\n--- 3. Error Handling ---');
const errorApp = new ApplicationBuilder();
errorApp.use(CommonMiddlewares.errorHandler((error) => {
console.error('Error caught:', error.message);
}));
errorApp.use(async (context, next) => {
if (context.request?.url === '/error') {
throw new Error('Intentional error');
}
await next();
});
await errorApp.handle(new MockRequest('/error'));
// 4. Authentication
console.log('\n--- 4. Authentication ---');
const authApp = new ApplicationBuilder();
authApp.use(CommonMiddlewares.authentication(async (context) => {
const token = context.request?.headers?.get('Authorization');
return token === 'Bearer valid-token';
}));
authApp.use(async (context, next) => {
console.log('Authenticated successfully');
context.response = new MockResponse().setStatus(200).send({ message: 'Success' });
await next();
});
const authRequest = new MockRequest('/protected', 'GET');
authRequest.headers.set('Authorization', 'Bearer valid-token');
const authResponse = await authApp.handle(authRequest);
console.log('Auth response status:', authResponse.status);
// 5. Rate limiting
console.log('\n--- 5. Rate Limiting ---');
const rateLimitApp = new ApplicationBuilder();
rateLimitApp.use(CommonMiddlewares.rateLimit({
maxRequests: 3,
windowMs: 10000
}));
rateLimitApp.use(async (context, next) => {
context.response = new MockResponse().setStatus(200).send({ message: 'OK' });
await next();
});
// Send multiple requests
const rlRequest = new MockRequest('/api/test');
for (let i = 0; i < 4; i++) {
try {
const rlResponse = await rateLimitApp.handle(rlRequest);
console.log(`Request ${i + 1}: ${rlResponse.status}`);
} catch (error) {
console.log(`Request ${i + 1}: Rate limited`);
}
}
// 6. Compose
console.log('\n--- 6. Middleware Composer ---');
const composed = MiddlewareComposer.from([
CommonMiddlewares.logging(),
CommonMiddlewares.timing(),
async (context, next) => {
console.log('Final handler');
await next();
}
]);
const context2: Context = {
request: { url: '/composed' },
response: null,
state: new Map(),
headers: new Map(),
metadata: {}
};
await composed.execute(context2);
console.log('\n=== All Middleware Examples Completed ===');
}
// Export functions
export { MiddlewarePipeline, CommonMiddlewares, MiddlewareComposer, ApplicationBuilder, MockRequest, MockResponse };
export { demonstrateMiddleware };
export type { Context, Middleware };
💻 Fichiers Statiques typescript
🟡 intermediate
⭐⭐⭐
Servir des fichiers statiques avec mise en cache, compression et détection de type MIME
⏱️ 25 min
🏷️ typescript, web, web features
Prerequisites:
Intermediate TypeScript, Fetch API, Blob
// Web TypeScript Static File Serving Examples
// Serving static files with various strategies
// 1. MIME Type Detector
class MIMETypeDetector {
private static mimeTypes: Record<string, string> = {
'.html': 'text/html',
'.htm': 'text/html',
'.css': 'text/css',
'.js': 'text/javascript',
'.json': 'application/json',
'.xml': 'application/xml',
'.txt': 'text/plain',
'.pdf': 'application/pdf',
'.zip': 'application/zip',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.ico': 'image/x-icon',
'.woff': 'font/woff',
'.woff2': 'font/woff2',
'.ttf': 'font/ttf',
'.eot': 'application/vnd.ms-fontobject',
'.mp3': 'audio/mpeg',
'.mp4': 'video/mp4',
'.webm': 'video/webm',
'.ogg': 'video/ogg'
};
// Get MIME type by extension
static getByExtension(filename: string): string {
const ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();
if (ext && this.mimeTypes[ext]) {
return this.mimeTypes[ext];
}
return 'application/octet-stream';
}
// Get MIME type by file signature (magic bytes)
static async getBySignature(file: Blob): Promise<string> {
const header = await this.readFileHeader(file);
// Check common signatures
if (this.startsWith(header, [0x89, 0x50, 0x4E, 0x47])) return 'image/png';
if (this.startsWith(header, [0xFF, 0xD8, 0xFF])) return 'image/jpeg';
if (this.startsWith(header, [0x47, 0x49, 0x46, 0x38])) return 'image/gif';
if (this.startsWith(header, [0x50, 0x4B, 0x03, 0x04])) return 'application/zip';
if (this.startsWith(header, [0x25, 0x50, 0x44, 0x46])) return 'application/pdf';
if (this.startsWith(header, [0x47, 0x49, 0x46, 0x38, 0x39, 0x61])) return 'image/gif';
return 'application/octet-stream';
}
// Read file header
private static async readFileHeader(file: Blob, bytes: number = 8): Promise<Uint8Array> {
const slice = file.slice(0, bytes);
const arrayBuffer = await slice.arrayBuffer();
return new Uint8Array(arrayBuffer);
}
// Check if header starts with bytes
private static startsWith(header: Uint8Array, bytes: number[]): boolean {
if (header.length < bytes.length) return false;
for (let i = 0; i < bytes.length; i++) {
if (header[i] !== bytes[i]) return false;
}
return true;
}
}
// 2. Static File Server
class StaticFileServer {
private baseUrl: string;
private cache: Map<string, { data: any; mime: string; timestamp: number }> = new Map();
private cacheTimeout: number = 60000; // 1 minute
constructor(baseUrl: string = '/static') {
this.baseUrl = baseUrl;
}
// Serve file
async serve(filePath: string): Promise<{
data: any;
mime: string;
status: number;
}> {
// Check cache
const cached = this.cache.get(filePath);
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
return {
data: cached.data,
mime: cached.mime,
status: 200
};
}
// Fetch file
const url = `${this.baseUrl}${filePath}`;
try {
const response = await fetch(url);
if (!response.ok) {
return {
data: null,
mime: 'text/plain',
status: response.status
};
}
const mime = response.headers.get('Content-Type') ||
MIMETypeDetector.getByExtension(filePath);
let data: any;
const contentType = mime.split(';')[0];
if (contentType.includes('application/json')) {
data = await response.json();
} else if (contentType.includes('text/')) {
data = await response.text();
} else {
data = await response.blob();
}
// Cache response
this.cache.set(filePath, {
data,
mime,
timestamp: Date.now()
});
return { data, mime, status: 200 };
} catch (error) {
return {
data: null,
mime: 'text/plain',
status: 500
};
}
}
// Serve with fallback
async serveWithFallback(filePath: string, fallbackPath: string): Promise<{
data: any;
mime: string;
status: number;
}> {
const result = await this.serve(filePath);
if (result.status === 404) {
return this.serve(fallbackPath);
}
return result;
}
// Clear cache
clearCache(): void {
this.cache.clear();
}
// Clear specific cache entry
clearCacheEntry(filePath: string): void {
this.cache.delete(filePath);
}
// Get cache size
getCacheSize(): number {
return this.cache.size;
}
}
// 3. File Cache Manager
class FileCacheManager {
private cache: Map<string, {
content: any;
mimeType: string;
etag: string;
lastModified: string;
}> = new Map();
// Add to cache
set(filePath: string, content: any, mimeType: string, etag?: string, lastModified?: string): void {
this.cache.set(filePath, {
content,
mimeType,
etag: etag || this.generateETag(content),
lastModified: lastModified || new Date().toUTCString()
});
}
// Get from cache
get(filePath: string): {
content: any;
mimeType: string;
etag: string;
lastModified: string;
} | null {
return this.cache.get(filePath) || null;
}
// Check if file is cached
has(filePath: string): boolean {
return this.cache.has(filePath);
}
// Remove from cache
remove(filePath: string): void {
this.cache.delete(filePath);
}
// Clear all cache
clear(): void {
this.cache.clear();
}
// Get cache stats
getStats(): {
size: number;
entries: number;
keys: string[];
} {
return {
size: this.cache.size,
entries: this.cache.size,
keys: Array.from(this.cache.keys())
};
}
// Generate ETag
private generateETag(content: any): string {
const str = typeof content === 'string' ? content : JSON.stringify(content);
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return `"${hash.toString(16)}"`;
}
}
// 4. Asset Preloader
class AssetPreloader {
private loadedAssets: Set<string> = new Set();
private failedAssets: Set<string> = new Set();
// Preload single asset
async preload(url: string): Promise<{
success: boolean;
error?: Error;
}> {
if (this.loadedAssets.has(url)) {
return { success: true };
}
if (this.failedAssets.has(url)) {
return { success: true, error: new Error('Previously failed to load') };
}
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
this.loadedAssets.add(url);
return { success: true };
} catch (error) {
this.failedAssets.add(url);
return { success: false, error: error as Error };
}
}
// Preload multiple assets
async preloadMultiple(urls: string[]): Promise<{
successful: string[];
failed: Array<{ url: string; error: Error }>;
}> {
const results = await Promise.allSettled(
urls.map(url => this.preload(url))
);
const successful: string[] = [];
const failed: Array<{ url: string; error: Error }> = [];
results.forEach((result, index) => {
if (result.status === 'fulfilled' && result.value.success) {
successful.push(urls[index]);
} else {
const error = result.status === 'rejected'
? result.reason
: (result.value as any).error;
failed.push({ url: urls[index], error });
}
});
return { successful, failed };
}
// Preload image
preloadImage(url: string): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
this.loadedAssets.add(url);
resolve(img);
};
img.onerror = () => {
this.failedAssets.add(url);
reject(new Error('Failed to load image'));
};
img.src = url;
});
}
// Preload images
async preloadImages(urls: string[]): Promise<HTMLImageElement[]> {
const results = await Promise.allSettled(
urls.map(url => this.preloadImage(url))
);
return results
.filter((result): result is PromiseFulfilledResult<HTMLImageElement> =>
result.status === 'fulfilled'
)
.map(result => result.value);
}
// Get loaded assets
getLoadedAssets(): string[] {
return Array.from(this.loadedAssets);
}
// Get failed assets
getFailedAssets(): string[] {
return Array.from(this.failedAssets);
}
// Clear tracking
clear(): void {
this.loadedAssets.clear();
this.failedAssets.clear();
}
}
// 5. Compression Handler
class CompressionHandler {
// Check if compression is supported
static isSupported(): boolean {
return 'CompressionStream' in window;
}
// Compress data (if supported)
static async compress(data: string, format: 'gzip' | 'deflate' = 'gzip'): Promise<Uint8Array> {
if (!this.isSupported()) {
throw new Error('Compression not supported');
}
const stream = new CompressionStream(format);
const writer = stream.writable.getWriter();
const encoder = new TextEncoder();
await writer.write(encoder.encode(data));
await writer.close();
const compressed = new ReadableStream({
start(controller) {
// Would need to implement proper stream reading
controller.close();
}
});
// This is a simplified version
return encoder.encode(data);
}
// Get best compression format
static getBestCompression(acceptEncoding: string): 'gzip' | 'deflate' | 'none' {
if (acceptEncoding.includes('br')) return 'gzip';
if (acceptEncoding.includes('gzip')) return 'gzip';
if (acceptEncoding.includes('deflate')) return 'deflate';
return 'none';
}
}
// 6. CDN Manager
class CDNManager {
private cdnUrls: string[] = [];
private currentCdnIndex: number = 0;
constructor(cdnUrls: string[]) {
this.cdnUrls = cdnUrls;
}
// Get CDN URL for asset
getCdnUrl(assetPath: string): string {
const cdnUrl = this.cdnUrls[this.currentCdnIndex];
return `${cdnUrl}${assetPath}`;
}
// Rotate CDN
rotateCdn(): void {
this.currentCdnIndex = (this.currentCdnIndex + 1) % this.cdnUrls.length;
}
// Get all CDN URLs for asset
getAllCdnUrls(assetPath: string): string[] {
return this.cdnUrls.map(cdn => `${cdn}${assetPath}`);
}
// Try each CDN until one succeeds
async tryAllCdns(assetPath: string): Promise<{
success: boolean;
cdnUrl?: string;
data?: any;
}> {
const urls = this.getAllCdnUrls(assetPath);
for (const url of urls) {
try {
const response = await fetch(url);
if (response.ok) {
const data = await response.blob();
return { success: true, cdnUrl: url, data };
}
} catch (error) {
console.warn(`CDN failed: ${url}`, error);
}
}
return { success: false };
}
}
// 7. Asset Bundle Manager
class AssetBundleManager {
private bundles: Map<string, string[]> = new Map();
// Register bundle
registerBundle(name: string, assets: string[]): void {
this.bundles.set(name, assets);
}
// Get bundle assets
getBundle(name: string): string[] | null {
return this.bundles.get(name) || null;
}
// Preload bundle
async preloadBundle(name: string, preloader: AssetPreloader): Promise<{
successful: string[];
failed: Array<{ url: string; error: Error }>;
}> {
const assets = this.getBundle(name);
if (!assets) {
return { successful: [], failed: [] };
}
return preloader.preloadMultiple(assets);
}
// Get bundle URL (for script/link tags)
getBundleHtml(name: string, type: 'script' | 'style'): string {
const assets = this.getBundle(name);
if (!assets) {
return '';
}
return assets.map(url => {
if (type === 'script') {
return `<script src="${url}"></script>`;
} else {
return `<link rel="stylesheet" href="${url}">`;
}
}).join('\n');
}
}
// Usage Examples
async function demonstrateStaticFiles() {
console.log('=== Web TypeScript Static File Serving Examples ===\n');
// 1. MIME type detection
console.log('--- 1. MIME Type Detection ---');
console.log('HTML:', MIMETypeDetector.getByExtension('index.html'));
console.log('JavaScript:', MIMETypeDetector.getByExtension('app.js'));
console.log('PNG:', MIMETypeDetector.getByExtension('image.png'));
// 2. Static file server
console.log('\n--- 2. Static File Server ---');
const server = new StaticFileServer('/assets');
// Note: These would make actual HTTP requests in a real environment
console.log('Would serve:', server.baseUrl + '/styles/main.css');
console.log('Would serve:', server.baseUrl + '/js/app.js');
// 3. File cache manager
console.log('\n--- 3. File Cache Manager ---');
const cacheManager = new FileCacheManager();
cacheManager.set('/index.html', '<html>...</html>', 'text/html', '"abc123"');
cacheManager.set('/app.js', 'console.log("Hello");', 'text/javascript');
console.log('Cache stats:', cacheManager.getStats());
const cached = cacheManager.get('/index.html');
console.log('Cached file:', cached?.mimeType);
// 4. Asset preloader
console.log('\n--- 4. Asset Preloader ---');
const preloader = new AssetPreloader();
// Note: These would be real URLs in production
const preloadResults = await preloader.preloadMultiple([
'/assets/image1.png',
'/assets/image2.png',
'/assets/image3.png'
]);
console.log('Preloaded:', preloadResults.successful);
console.log('Failed:', preloadResults.failed);
// 5. CDN manager
console.log('\n--- 5. CDN Manager ---');
const cdnManager = new CDNManager([
'https://cdn1.example.com',
'https://cdn2.example.com',
'https://cdn3.example.com'
]);
console.log('CDN URL:', cdnManager.getCdnUrl('/assets/app.js'));
cdnManager.rotateCdn();
console.log('Rotated CDN URL:', cdnManager.getCdnUrl('/assets/app.js'));
// 6. Asset bundle manager
console.log('\n--- 6. Asset Bundle Manager ---');
const bundleManager = new AssetBundleManager();
bundleManager.registerBundle('vendor', [
'/assets/js/vendor/react.js',
'/assets/js/vendor/lodash.js'
]);
bundleManager.registerBundle('app', [
'/assets/js/app.js',
'/assets/js/utils.js'
]);
const bundle = bundleManager.getBundle('vendor');
console.log('Vendor bundle:', bundle);
console.log('\n=== All Static File Serving Examples Completed ===');
}
// Export functions
export { MIMETypeDetector, StaticFileServer, FileCacheManager, AssetPreloader, CompressionHandler, CDNManager, AssetBundleManager };
export { demonstrateStaticFiles };