UNPKG

@oxog/spark

Version:

Ultra-fast, zero-dependency Node.js web framework with security hardening, memory leak protection, and enhanced error handling

117 lines (94 loc) 2.7 kB
class Layer { constructor(path, options, handler, router = null) { this.path = path; this.handler = handler; this.name = handler.name || '<anonymous>'; this.options = options; this.route = null; this.method = null; this.router = router; // Use router's pathToRegExp if available, otherwise create a default regex if (router && router.pathToRegExp) { this.regexp = router.pathToRegExp(path, options); } else { // Fallback to default regex creation without circular dependency this.regexp = this.createDefaultRegex(path, options); } this.keys = this.regexp.keys; } createDefaultRegex(path, options = {}) { const { sensitive = false, strict = false, end = true } = options; if (path instanceof RegExp) { return { regexp: path, keys: [] }; } const keys = []; let regexp = path; // Escape forward slashes regexp = regexp.replace(/\\\//g, '/'); // Replace parameters regexp = regexp.replace(/:([^(/\\]+)/g, (match, key) => { keys.push({ name: key, optional: false }); return '([^/]+)'; }); // Replace wildcards regexp = regexp.replace(/\*([^(/\\]*)/g, (match, key) => { keys.push({ name: key || 'wild', optional: false }); return '(.*)'; }); // Add end pattern if (end) { regexp += strict ? '' : '/?'; } regexp += end ? '$' : ''; const flags = sensitive ? '' : 'i'; return { regexp: new RegExp(`^${regexp}`, flags), keys }; } match(path) { const match = this.regexp.regexp.exec(path); if (!match) { return false; } const params = {}; for (let i = 1; i < match.length; i++) { const key = this.keys[i - 1]; const value = match[i]; if (value !== undefined) { params[key.name] = decodeParam(value); } } return { path: match[0], params }; } async handle(ctx, next) { if (this.handler.length > 2) { return this.handler(ctx, next); } else { await this.handler(ctx, next); } } handles_method(method) { if (this.method) { return this.method === method; } if (this.route) { return this.route.handles_method(method); } return true; } } function decodeParam(value) { if (typeof value !== 'string' || value.length === 0) { return value; } try { return decodeURIComponent(value); } catch (error) { throw new Error(`Failed to decode param '${value}'`); } } module.exports = Layer;