├── .gitignore
├── README.md
├── assets
├── .DS_Store
└── img
│ ├── di-container-diagram.svg
│ ├── plugin-system-diagram.svg
│ ├── rate-limiter-diagram.svg
│ ├── retry-mechanism-diagram.svg
│ └── stream-transformer-diagram.svg
├── examples
├── AdaptiveRateLimiter.js
├── DependencyInjectionContainer.js
├── PluginManager.js
├── RetryMechanism.js
└── StreamTransformer.js
├── package-lock.json
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Node template
2 | # Logs
3 | logs
4 | *.log
5 | npm-debug.log*
6 | yarn-debug.log*
7 | yarn-error.log*
8 | lerna-debug.log*
9 | .pnpm-debug.log*
10 | .idea
11 |
12 | # Diagnostic reports (https://nodejs.org/api/report.html)
13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
14 |
15 | # Runtime data
16 | pids
17 | *.pid
18 | *.seed
19 | *.pid.lock
20 |
21 | # Directory for instrumented libs generated by jscoverage/JSCover
22 | lib-cov
23 |
24 | # Coverage directory used by tools like istanbul
25 | coverage
26 | *.lcov
27 |
28 | # nyc test coverage
29 | .nyc_output
30 |
31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
32 | .grunt
33 |
34 | # Bower dependency directory (https://bower.io/)
35 | bower_components
36 |
37 | # node-waf configuration
38 | .lock-wscript
39 |
40 | # Compiled binary addons (https://nodejs.org/api/addons.html)
41 | build/Release
42 |
43 | # Dependency directories
44 | node_modules/
45 | jspm_packages/
46 |
47 | # Snowpack dependency directory (https://snowpack.dev/)
48 | web_modules/
49 |
50 | # TypeScript cache
51 | *.tsbuildinfo
52 |
53 | # Optional npm cache directory
54 | .npm
55 |
56 | # Optional eslint cache
57 | .eslintcache
58 |
59 | # Optional stylelint cache
60 | .stylelintcache
61 |
62 | # Microbundle cache
63 | .rpt2_cache/
64 | .rts2_cache_cjs/
65 | .rts2_cache_es/
66 | .rts2_cache_umd/
67 |
68 | # Optional REPL history
69 | .node_repl_history
70 |
71 | # Output of 'npm pack'
72 | *.tgz
73 |
74 | # Yarn Integrity file
75 | .yarn-integrity
76 |
77 | # dotenv environment variable files
78 | .env
79 | .env.development.local
80 | .env.test.local
81 | .env.production.local
82 | .env.local
83 |
84 | # parcel-bundler cache (https://parceljs.org/)
85 | .cache
86 | .parcel-cache
87 |
88 | # Next.js build output
89 | .next
90 | out
91 |
92 | # Nuxt.js build / generate output
93 | .nuxt
94 | dist
95 |
96 | # Gatsby files
97 | .cache/
98 | # Comment in the public line in if your project uses Gatsby and not Next.js
99 | # https://nextjs.org/blog/next-9-1#public-directory-support
100 | # public
101 |
102 | # vuepress build output
103 | .vuepress/dist
104 |
105 | # vuepress v2.x temp and cache directory
106 | .temp
107 | .cache
108 |
109 | # Docusaurus cache and generated files
110 | .docusaurus
111 |
112 | # Serverless directories
113 | .serverless/
114 |
115 | # FuseBox cache
116 | .fusebox/
117 |
118 | # DynamoDB Local files
119 | .dynamodb/
120 |
121 | # TernJS port file
122 | .tern-port
123 |
124 | # Stores VSCode versions used for testing VSCode extensions
125 | .vscode-test
126 |
127 | # yarn v2
128 | .yarn/cache
129 | .yarn/unplugged
130 | .yarn/build-state.yml
131 | .yarn/install-state.gz
132 | .pnp.*
133 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Node.js Advanced Patterns
3 |
4 | ## Table of Contents
5 |
6 | - **[Dependency Injection Container](./examples/DependencyInjectionContainer.js)**
7 | 
8 | - Dynamic dependency management
9 | - Service registration and resolution
10 | - Supports complex dependency graphs
11 | - [Full Article (Medium)](https://v-checha.medium.com/node-js-advanced-patterns-dependency-injection-container-45938e88e873)
12 |
13 | ```bash
14 | npm run example:4
15 | ```
16 |
17 | - Circuit Breaker Pattern
18 | - Prevents cascading failures
19 | - Automatic recovery mechanism
20 | - Configurable failure thresholds
21 | - Different circuit states (CLOSED, OPEN, HALF_OPEN)
22 |
23 |
24 | - **[Streaming Transformer](./examples/StreamTransformer.js)**
25 | 
26 | - Advanced stream processing
27 | - Composable transformation streams
28 | - Supports async transformations
29 | - Easy stream composition
30 | - [Full Article (Medium)](https://v-checha.medium.com/node-js-advanced-patterns-stream-transformer-1b1f3b1b3b3d)
31 |
32 | ```bash
33 | npm run example:1
34 | ```
35 |
36 | - **[Robust Retry Mechanism](./examples/RetryMechanism.js)**
37 | 
38 | - Exponential backoff with jitter
39 | - Configurable retry strategies
40 | - Prevents thundering herd problem
41 | - Intelligent delay calculation
42 | - [Full Article (Medium)](https://v-checha.medium.com/node-js-advanced-patterns-robust-retry-mechanism-1b1f3b1b3b3d)
43 |
44 | ```bash
45 | npm run example:2
46 | ```
47 |
48 |
49 | - **[Adaptive Rate Limiter](./examples/AdaptiveRateLimiter.js)**
50 | 
51 | - Dynamic request throttling
52 | - Adaptive threshold management
53 | - Prevents system overload
54 | - Flexible configuration
55 | - [Full Article (Medium)](https://medium.com/@v-checha/node-js-advanced-patterns-adaptive-rate-limiter-aa8221177162)
56 |
57 | ```bash
58 | npm run example:3
59 | ```
60 |
61 | - **[Dynamic Plugin System](./examples/PluginManager.js)**
62 | 
63 | - Runtime plugin loading
64 | - Hot-reload capabilities
65 | - Safe plugin execution
66 | - Error-tolerant plugin management
67 | - [Full Article (Medium)](https://v-checha.medium.com/node-js-advanced-patterns-plugin-manager-44adb72aa6bb)
68 | ```bash
69 | npm run example:5
70 | ```
71 |
--------------------------------------------------------------------------------
/assets/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/v-checha/node-js-advanced-patterns/38341dbb0ef591ee6e5aabdb3cc83161c92abae5/assets/.DS_Store
--------------------------------------------------------------------------------
/assets/img/di-container-diagram.svg:
--------------------------------------------------------------------------------
1 |
87 |
--------------------------------------------------------------------------------
/assets/img/plugin-system-diagram.svg:
--------------------------------------------------------------------------------
1 |
64 |
--------------------------------------------------------------------------------
/assets/img/rate-limiter-diagram.svg:
--------------------------------------------------------------------------------
1 |
79 |
--------------------------------------------------------------------------------
/assets/img/retry-mechanism-diagram.svg:
--------------------------------------------------------------------------------
1 |
81 |
--------------------------------------------------------------------------------
/assets/img/stream-transformer-diagram.svg:
--------------------------------------------------------------------------------
1 |
55 |
--------------------------------------------------------------------------------
/examples/AdaptiveRateLimiter.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 |
4 | class AdaptiveRateLimiter {
5 | constructor(options = {}) {
6 | this.baseLimit = options.baseLimit || 100;
7 | this.windowMs = options.windowMs || 60000;
8 | this.minLimit = options.minLimit || 10;
9 | this.maxLimit = options.maxLimit || 1000;
10 |
11 | this.currentLimit = this.baseLimit;
12 | this.requests = new Map();
13 | this.metrics = {
14 | successCount: 0,
15 | errorCount: 0,
16 | avgResponseTime: 0
17 | };
18 | }
19 |
20 | async isAllowed(clientId) {
21 | this.cleanup();
22 | await this.updateMetrics();
23 |
24 | const clientRequests = this.requests.get(clientId) || [];
25 | const windowStart = Date.now() - this.windowMs;
26 | const recentRequests = clientRequests.filter(time => time > windowStart);
27 |
28 | if (recentRequests.length >= this.currentLimit) {
29 | return false;
30 | }
31 |
32 | recentRequests.push(Date.now());
33 | this.requests.set(clientId, recentRequests);
34 | return true;
35 | }
36 |
37 | cleanup() {
38 | const windowStart = Date.now() - this.windowMs;
39 | for (const [clientId, requests] of this.requests.entries()) {
40 | const validRequests = requests.filter(time => time > windowStart);
41 | if (validRequests.length === 0) {
42 | this.requests.delete(clientId);
43 | } else {
44 | this.requests.set(clientId, validRequests);
45 | }
46 | }
47 | }
48 |
49 | async updateMetrics() {
50 | const systemLoad = await this.getSystemMetrics();
51 | const errorRate = this.metrics.errorCount /
52 | (this.metrics.successCount + this.metrics.errorCount || 1);
53 |
54 | this.adjustLimit(systemLoad, errorRate);
55 | }
56 |
57 | adjustLimit(systemLoad, errorRate) {
58 | let multiplier = 1;
59 |
60 | // Adjust based on system load
61 | if (systemLoad > 0.8) multiplier *= 0.8;
62 | else if (systemLoad < 0.3) multiplier *= 1.2;
63 |
64 | // Adjust based on error rate
65 | if (errorRate > 0.1) multiplier *= 0.7;
66 | else if (errorRate < 0.01) multiplier *= 1.1;
67 |
68 | this.currentLimit = Math.min(
69 | this.maxLimit,
70 | Math.max(
71 | this.minLimit,
72 | Math.floor(this.currentLimit * multiplier)
73 | )
74 | );
75 | }
76 |
77 | async getSystemMetrics() {
78 | const usage = process.cpuUsage();
79 | return (usage.user + usage.system) / 1000000; // Convert to seconds
80 | }
81 | }
82 |
83 | class EnhancedAdaptiveRateLimiter extends AdaptiveRateLimiter {
84 | constructor(options = {}) {
85 | super(options);
86 | this.metricsWindow = new Array(10).fill({
87 | timestamp: Date.now(),
88 | cpuLoad: 0,
89 | memoryUsage: 0,
90 | responseTime: 0,
91 | errorRate: 0
92 | });
93 | }
94 |
95 | async collectMetrics() {
96 | const metrics = {
97 | timestamp: Date.now(),
98 | cpuLoad: await this.getCPULoad(),
99 | memoryUsage: await this.getMemoryUsage(),
100 | responseTime: this.metrics.avgResponseTime,
101 | errorRate: this.getErrorRate()
102 | };
103 |
104 | this.metricsWindow.shift();
105 | this.metricsWindow.push(metrics);
106 |
107 | return this.analyzeMetrics();
108 | }
109 |
110 | analyzeMetrics() {
111 | const recent = this.metricsWindow.slice(-3);
112 | const trend = {
113 | cpuLoad: this.calculateTrend(recent, 'cpuLoad'),
114 | responseTime: this.calculateTrend(recent, 'responseTime'),
115 | errorRate: this.calculateTrend(recent, 'errorRate')
116 | };
117 |
118 | return trend;
119 | }
120 |
121 | calculateTrend(metrics, key) {
122 | if (metrics.length < 2) return 0;
123 | const values = metrics.map(m => m[key]);
124 | const delta = values[values.length - 1] - values[0];
125 | return delta / values[0];
126 | }
127 |
128 | async getCPULoad() {
129 | const startUsage = process.cpuUsage();
130 | await new Promise(resolve => setTimeout(resolve, 100));
131 | const endUsage = process.cpuUsage(startUsage);
132 | return (endUsage.user + endUsage.system) / 1000000;
133 | }
134 |
135 | async getMemoryUsage() {
136 | const used = process.memoryUsage();
137 | return used.heapUsed / used.heapTotal;
138 | }
139 |
140 | getErrorRate() {
141 | const total = this.metrics.successCount + this.metrics.errorCount;
142 | return total ? this.metrics.errorCount / total : 0;
143 | }
144 | }
145 |
146 | const limiter = new EnhancedAdaptiveRateLimiter({
147 | baseLimit: 3,
148 | windowMs: 60000
149 | });
150 |
151 | app.get('/', async (req, res, next) => {
152 | const clientId = req.ip;
153 | const startTime = Date.now();
154 |
155 | try {
156 | const allowed = await limiter.isAllowed(clientId);
157 | if (!allowed) {
158 | return res.status(429).json({
159 | error: 'Too Many Requests',
160 | currentLimit: limiter.currentLimit,
161 | retryAfter: Math.ceil(limiter.windowMs / 1000)
162 | });
163 | }
164 |
165 | const duration = Date.now() - startTime;
166 |
167 | if (res.statusCode >= 500) {
168 | limiter.metrics.errorCount++;
169 | } else {
170 | limiter.metrics.successCount++;
171 | }
172 | limiter.metrics.avgResponseTime =
173 | (limiter.metrics.avgResponseTime + duration) / 2;
174 |
175 | res.json(limiter);
176 | } catch (error) {
177 | next(error);
178 | }
179 | });
180 |
181 | app.listen(3000, () => {
182 | console.log('Server listening on port 3000');
183 | });
184 |
--------------------------------------------------------------------------------
/examples/DependencyInjectionContainer.js:
--------------------------------------------------------------------------------
1 | class DIContainer {
2 | constructor() {
3 | this.services = new Map();
4 | this.singletons = new Map();
5 | }
6 |
7 | // Register a service with its implementation
8 | register(name, Implementation, dependencies = []) {
9 | this.services.set(name, {
10 | Implementation,
11 | dependencies,
12 | singleton: false
13 | });
14 | return this;
15 | }
16 |
17 | // Register a singleton service
18 | registerSingleton(name, Implementation, dependencies = []) {
19 | this.services.set(name, {
20 | Implementation,
21 | dependencies,
22 | singleton: true
23 | });
24 | return this;
25 | }
26 |
27 | // Resolve a service and its dependencies
28 | resolve(name) {
29 | const service = this.services.get(name);
30 | if (!service) {
31 | throw new Error(`Service ${name} not registered`);
32 | }
33 |
34 | if (service.singleton && this.singletons.has(name)) {
35 | return this.singletons.get(name);
36 | }
37 |
38 | const dependencies = service.dependencies.map(dep => this.resolve(dep));
39 | const instance = new service.Implementation(...dependencies);
40 |
41 | if (service.singleton) {
42 | this.singletons.set(name, instance);
43 | }
44 |
45 | return instance;
46 | }
47 | }
48 |
49 | class EmailService {
50 | async sendOrderConfirmation(user, productId) {
51 | // Implementation
52 | console.log('Order confirmation sent', user, productId);
53 | }
54 | }
55 |
56 | class PaymentService {
57 | async processPayment(amount, userId) {
58 | // Implementation
59 | console.log('Payment processed', amount, userId);
60 | }
61 | }
62 |
63 | class OrderService {
64 | constructor(emailService, paymentService) {
65 | this.emailService = emailService;
66 | this.paymentService = paymentService;
67 | }
68 |
69 | async createOrder(userId, productId) {
70 | const email = await this.emailService.sendOrderConfirmation(userId, productId);
71 | const payment = await this.paymentService.processPayment(100, userId);
72 | // Implementation
73 | }
74 | }
75 |
76 | const container = new DIContainer();
77 |
78 | // Register services
79 | container.registerSingleton('emailService', EmailService);
80 | container.registerSingleton('paymentService', PaymentService);
81 |
82 | // Register OrderService with its dependencies
83 | container.register('orderService', OrderService, [
84 | 'emailService',
85 | 'paymentService'
86 | ]);
87 |
88 | // Resolve the OrderService when needed
89 | const orderService = container.resolve('orderService');
90 |
91 | orderService.createOrder(1, 2);
92 |
--------------------------------------------------------------------------------
/examples/PluginManager.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 |
3 | class PluginManager {
4 | #plugins = new Map();
5 | #hooks = new Map();
6 |
7 | async registerPlugin(plugin) {
8 | // Validate plugin structure
9 | if (!plugin.name || !plugin.version) {
10 | throw new Error('Plugin must have name and version properties');
11 | }
12 |
13 | if (this.#plugins.has(plugin.name)) {
14 | throw new Error(`Plugin ${plugin.name} is already registered`);
15 | }
16 |
17 | // Initialize plugin if needed
18 | if (typeof plugin.initialize === 'function') {
19 | try {
20 | await plugin.initialize();
21 | } catch (error) {
22 | throw new Error(`Failed to initialize plugin ${plugin.name}: ${error.message}`);
23 | }
24 | }
25 |
26 | // Register plugin hooks
27 | for (const [hookName, hookFn] of Object.entries(plugin.hooks || {})) {
28 | if (!this.#hooks.has(hookName)) {
29 | this.#hooks.set(hookName, new Set());
30 | }
31 | this.#hooks.get(hookName).add(hookFn);
32 | }
33 |
34 | this.#plugins.set(plugin.name, plugin);
35 | console.log(`Plugin ${plugin.name} v${plugin.version} registered successfully`);
36 | }
37 |
38 | async executeHook(hookName, ...args) {
39 | const hooks = this.#hooks.get(hookName);
40 | if (!hooks) {
41 | return [];
42 | }
43 |
44 | const results = [];
45 | for (const hook of hooks) {
46 | try {
47 | const result = await hook(...args);
48 | results.push(result);
49 | } catch (error) {
50 | console.error(`Error executing hook ${hookName}: ${error.message}`);
51 | results.push(null);
52 | }
53 | }
54 | return results;
55 | }
56 |
57 | async unregisterPlugin(pluginName) {
58 | const plugin = this.#plugins.get(pluginName);
59 | if (!plugin) {
60 | throw new Error(`Plugin ${pluginName} is not registered`);
61 | }
62 |
63 | // Remove plugin hooks
64 | for (const [hookName, hooks] of this.#hooks.entries()) {
65 | for (const hookFn of Object.values(plugin.hooks || {})) {
66 | hooks.delete(hookFn);
67 | }
68 | if (hooks.size === 0) {
69 | this.#hooks.delete(hookName);
70 | }
71 | }
72 |
73 | // Shutdown plugin if needed
74 | if (typeof plugin.shutdown === 'function') {
75 | try {
76 | await plugin.shutdown();
77 | } catch (error) {
78 | console.error(`Error shutting down plugin ${pluginName}: ${error.message}`);
79 | }
80 | }
81 |
82 | this.#plugins.delete(pluginName);
83 | console.log(`Plugin ${pluginName} unregistered successfully`);
84 | }
85 |
86 | getPlugin(pluginName) {
87 | return this.#plugins.get(pluginName);
88 | }
89 |
90 | listPlugins() {
91 | return Array.from(this.#plugins.entries()).map(([name, plugin]) => ({
92 | name,
93 | version: plugin.version
94 | }));
95 | }
96 | }
97 |
98 | // Console Logger Plugin
99 | const consoleLoggerPlugin = {
100 | name: 'console-logger',
101 | version: '1.0.0',
102 | hooks: {
103 | 'onLog': async (level, message) => {
104 | const timestamp = new Date().toISOString();
105 | console.log(`[${timestamp}] ${level}: ${message}`);
106 | }
107 | }
108 | };
109 |
110 | // File Logger Plugin
111 | const fileLoggerPlugin = {
112 | name: 'file-logger',
113 | version: '1.0.0',
114 | initialize: async () => {
115 | // Setup file handles, create directories, etc.
116 | await fs.promises.mkdir('./logs', { recursive: true });
117 | },
118 | shutdown: async () => {
119 | // Close file handles, etc.
120 | },
121 | hooks: {
122 | 'onLog': async (level, message) => {
123 | const timestamp = new Date().toISOString();
124 | const logLine = `[${timestamp}] ${level}: ${message}\n`;
125 | await fs.promises.appendFile('./logs/app.log', logLine);
126 | }
127 | }
128 | };
129 |
130 | // Slack Notification Plugin (for error logs only)
131 | const slackNotifierPlugin = {
132 | name: 'slack-notifier',
133 | version: '1.0.0',
134 | initialize: async () => {
135 | // Initialize Slack client
136 | },
137 | hooks: {
138 | 'onLog': async (level, message) => {
139 | if (level === 'ERROR') {
140 | // Send it to Slack (implementation omitted for brevity)
141 | await sendToSlack(`🚨 Error: ${message}`);
142 | }
143 | }
144 | }
145 | };
146 |
147 | async function main() {
148 | // Create plugin manager instance
149 | const pluginManager = new PluginManager();
150 |
151 | // Register plugins
152 | await pluginManager.registerPlugin(consoleLoggerPlugin);
153 | await pluginManager.registerPlugin(fileLoggerPlugin);
154 | await pluginManager.registerPlugin(slackNotifierPlugin);
155 |
156 | // Create a logging function that uses the plugins
157 | async function log(level, message) {
158 | await pluginManager.executeHook('onLog', level, message);
159 | }
160 |
161 | // Example usage
162 | await log('INFO', 'Application started');
163 | await log('ERROR', 'Failed to connect to database');
164 |
165 | // List all registered plugins
166 | console.log('Registered plugins:', pluginManager.listPlugins());
167 |
168 | // Cleanup
169 | await pluginManager.unregisterPlugin('slack-notifier');
170 | }
171 |
172 | main().catch(console.error);
173 |
--------------------------------------------------------------------------------
/examples/RetryMechanism.js:
--------------------------------------------------------------------------------
1 | class RetryError extends Error {
2 | constructor(originalError, attempts, duration) {
3 | super(originalError.message);
4 | this.name = 'RetryError';
5 | this.originalError = originalError;
6 | this.attempts = attempts;
7 | this.duration = duration;
8 | }
9 | }
10 |
11 | class ExponentialBackoffRetry {
12 | constructor(options = {}) {
13 | this.baseDelay = options.baseDelay || 1000;
14 | this.maxDelay = options.maxDelay || 30000;
15 | this.maxRetries = options.maxRetries || 5;
16 | this.jitter = options.jitter || true;
17 | }
18 |
19 | async execute(fn) {
20 | let retries = 0;
21 |
22 | while (true) {
23 | try {
24 | return await fn();
25 | } catch (error) {
26 | if (retries >= this.maxRetries) {
27 | throw new Error(`Failed after ${retries} retries: ${error.message}`);
28 | }
29 |
30 | const delay = this.calculateDelay(retries);
31 | await this.wait(delay);
32 | retries++;
33 | }
34 | }
35 | }
36 |
37 | calculateDelay(retryCount) {
38 | // Calculate exponential delay: 2^retryCount * baseDelay
39 | let delay = Math.min(
40 | this.maxDelay,
41 | Math.pow(2, retryCount) * this.baseDelay
42 | );
43 |
44 | // Add jitter to prevent thundering herd problem
45 | if (this.jitter) {
46 | delay = delay * (0.5 + Math.random());
47 | }
48 |
49 | return delay;
50 | }
51 |
52 | wait(ms) {
53 | return new Promise(resolve => setTimeout(resolve, ms));
54 | }
55 | }
56 |
57 | class CircuitBreaker {
58 | constructor(options = {}) {
59 | this.failureThreshold = options.failureThreshold || 5;
60 | this.resetTimeout = options.resetTimeout || 60000;
61 | this.failures = 0;
62 | this.state = 'CLOSED';
63 | this.lastFailureTime = null;
64 | }
65 |
66 | async execute(fn) {
67 | if (this.state === 'OPEN') {
68 | if (Date.now() - this.lastFailureTime >= this.resetTimeout) {
69 | this.state = 'HALF_OPEN';
70 | } else {
71 | throw new Error('Circuit breaker is OPEN');
72 | }
73 | }
74 |
75 | try {
76 | const result = await fn();
77 | if (this.state === 'HALF_OPEN') {
78 | this.state = 'CLOSED';
79 | this.failures = 0;
80 | }
81 | return result;
82 | } catch (error) {
83 | this.failures++;
84 | this.lastFailureTime = Date.now();
85 |
86 | if (this.failures >= this.failureThreshold) {
87 | this.state = 'OPEN';
88 | }
89 | throw error;
90 | }
91 | }
92 | }
93 |
94 | class RetryMechanism {
95 | constructor(options = {}) {
96 | this.retrier = new ExponentialBackoffRetry(options.retry);
97 | this.circuitBreaker = new CircuitBreaker(options.circuitBreaker);
98 | this.logger = options.logger || console;
99 | }
100 |
101 | async execute(fn, context = {}) {
102 | const startTime = Date.now();
103 | let attempts = 0;
104 |
105 | try {
106 | return await this.circuitBreaker.execute(async () => {
107 | return await this.retrier.execute(async () => {
108 | attempts++;
109 | try {
110 | const result = await fn();
111 | this.logSuccess(context, attempts, startTime);
112 | return result;
113 | } catch (error) {
114 | this.logFailure(context, attempts, error);
115 | throw error;
116 | }
117 | });
118 | });
119 | } catch (error) {
120 | throw new RetryError(error, attempts, Date.now() - startTime);
121 | }
122 | }
123 |
124 | logSuccess(context, attempts, startTime) {
125 | this.logger.info({
126 | event: 'retry_success',
127 | context,
128 | attempts,
129 | duration: Date.now() - startTime
130 | });
131 | }
132 |
133 | logFailure(context, attempts, error) {
134 | this.logger.error({
135 | event: 'retry_failure',
136 | context,
137 | attempts,
138 | error: error.message
139 | });
140 | }
141 | }
142 |
143 | const retrySystem = new RetryMechanism({
144 | retry: {
145 | baseDelay: 1000,
146 | maxDelay: 30000,
147 | maxRetries: 5
148 | },
149 | circuitBreaker: {
150 | failureThreshold: 5,
151 | resetTimeout: 60000
152 | }
153 | });
154 |
155 | // Database operation with retry
156 | async function fetchUserData(userId) {
157 | return retrySystem.execute(
158 | async () => {
159 | const user = await db.users.findById(userId);
160 | if (!user) throw new Error('User not found');
161 | return user;
162 | },
163 | { operation: 'fetchUserData', userId }
164 | );
165 | }
166 |
167 | fetchUserData('user123')
168 | .then(user => console.log('User:', user))
169 | .catch(error => console.error('Error:', error));
170 |
171 |
172 | // API call with retry
173 | async function updateUserProfile(userId, data) {
174 | return retrySystem.execute(
175 | async () => {
176 | const response = await fetch(`/api/users/${userId}`, {
177 | method: 'PUT',
178 | body: JSON.stringify(data)
179 | });
180 | if (!response.ok) throw new Error('API request failed');
181 | return response.json();
182 | },
183 | { operation: 'updateUserProfile', userId }
184 | );
185 | }
186 |
187 | updateUserProfile('user123', { name: 'Alice' })
188 | .then(response => console.log('Response:', response))
189 | .catch(error => console.error('Error:', error));
190 |
--------------------------------------------------------------------------------
/examples/StreamTransformer.js:
--------------------------------------------------------------------------------
1 | const { Transform, Readable } = require('stream');
2 |
3 | class StreamTransformer {
4 | /**
5 | * Creates a transform stream with custom transformation logic
6 | * @param {Object} options - Configuration options
7 | * @param {Function} options.transform - Transformation function
8 | * @param {Function} [options.flush] - Optional flush method
9 | * @param {boolean} [options.objectMode=true] - Enable object mode
10 | */
11 | constructor(options = {}) {
12 | const {
13 | transform,
14 | flush = null,
15 | objectMode = true
16 | } = options;
17 |
18 | if (typeof transform !== 'function') {
19 | throw new Error('Transform function is required');
20 | }
21 |
22 | return new Transform({
23 | objectMode,
24 |
25 | // Transformation logic
26 | async transform(chunk, encoding, callback) {
27 | try {
28 | // Allow async transformations
29 | const result = await transform(chunk, this);
30 |
31 | // Push transformed data or multiple chunks
32 | if (Array.isArray(result)) {
33 | result.forEach(item => this.push(item));
34 | } else if (result !== null && result !== undefined) {
35 | this.push(result);
36 | }
37 |
38 | callback(null);
39 | } catch (error) {
40 | callback(error);
41 | }
42 | },
43 |
44 | // Optional flush method for end-of-stream processing
45 | async flush(callback) {
46 | try {
47 | if (flush) {
48 | const result = await flush(this);
49 | if (result) {
50 | this.push(result);
51 | }
52 | }
53 | callback(null);
54 | } catch (error) {
55 | callback(error);
56 | }
57 | }
58 | });
59 | }
60 |
61 | /**
62 | * Create a pipeline of transformers
63 | * @param {...StreamTransformer} transformers - Transformer streams
64 | * @returns {Object} Pipeline interface
65 | */
66 | static pipeline(...transformers) {
67 | return {
68 | /**
69 | * Pipe transformers to an input stream
70 | * @param {Readable} inputStream - Source stream
71 | * @returns {Readable} Transformed stream
72 | */
73 | through: (inputStream) => {
74 | return transformers.reduce(
75 | (stream, transformer) => stream.pipe(transformer),
76 | inputStream
77 | );
78 | }
79 | };
80 | }
81 | }
82 |
83 | // Practical Example Demonstrating StreamTransformer
84 | async function demonstrateStreamTransformer() {
85 | // Example 1: Simple Data Transformation
86 | const numberTransformer = new StreamTransformer({
87 | transform: (chunk) => chunk * 2
88 | });
89 |
90 | const sourceStream = new Readable({
91 | objectMode: true,
92 | read() {
93 | this.push(1);
94 | this.push(2);
95 | this.push(3);
96 | this.push(null); // End of stream
97 | }
98 | });
99 |
100 | sourceStream
101 | .pipe(numberTransformer)
102 | .on('data', (chunk) => {
103 | console.log('Doubled number:', chunk);
104 | })
105 | .on('end', () => console.log('Number transformation complete'));
106 |
107 | // Example 2: Complex Data Transformation
108 | const userDataTransformer = new StreamTransformer({
109 | transform: async (user) => {
110 | // Simulate async operation (e.g., data enrichment)
111 | await new Promise(resolve => setTimeout(resolve, 100));
112 |
113 | // Transform and filter users
114 | if (user.age >= 18) {
115 | return {
116 | ...user,
117 | category: user.age < 30 ? 'Young Adult' : 'Adult'
118 | };
119 | }
120 |
121 | // Return null to filter out underage users
122 | return null;
123 | },
124 |
125 | // Optional flush method for final processing
126 | flush: () => {
127 | console.log('Finished processing user stream');
128 | }
129 | });
130 |
131 | const usersStream = new Readable({
132 | objectMode: true,
133 | read() {
134 | this.push({ name: 'Alice', age: 25 });
135 | this.push({ name: 'Bob', age: 17 });
136 | this.push({ name: 'Charlie', age: 40 });
137 | this.push(null);
138 | }
139 | });
140 |
141 | usersStream
142 | .pipe(userDataTransformer)
143 | .on('data', (user) => {
144 | console.log('Processed User:', user);
145 | })
146 | .on('end', () => console.log('User transformation complete'));
147 |
148 | // Example 3: Pipeline Composition
149 | const uppercaseTransformer = new StreamTransformer({
150 | transform: (chunk) => chunk.toUpperCase(),
151 | objectMode: true
152 | });
153 |
154 | const filterTransformer = new StreamTransformer({
155 | transform: (chunk) => chunk.length > 3 ? chunk : null,
156 | objectMode: true
157 | });
158 |
159 | const wordsStream = new Readable({
160 | objectMode: true,
161 | read() {
162 | this.push('hello');
163 | this.push('hi');
164 | this.push('world');
165 | this.push('a');
166 | this.push(null);
167 | }
168 | });
169 |
170 | // Create a pipeline with multiple transformers
171 | const pipeline = StreamTransformer.pipeline(
172 | filterTransformer,
173 | uppercaseTransformer
174 | );
175 |
176 | pipeline
177 | .through(wordsStream)
178 | .on('data', (word) => {
179 | console.log('Processed Word:', word);
180 | })
181 | .on('end', () => console.log('Pipeline transformation complete'));
182 | }
183 |
184 | // Run the demonstration
185 | demonstrateStreamTransformer().catch(console.error);
186 |
187 | module.exports = StreamTransformer;
188 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nodeteamdev.github.io",
3 | "version": "1.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "nodeteamdev.github.io",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "express": "^4.21.2"
13 | }
14 | },
15 | "node_modules/accepts": {
16 | "version": "1.3.8",
17 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
18 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
19 | "dev": true,
20 | "dependencies": {
21 | "mime-types": "~2.1.34",
22 | "negotiator": "0.6.3"
23 | },
24 | "engines": {
25 | "node": ">= 0.6"
26 | }
27 | },
28 | "node_modules/array-flatten": {
29 | "version": "1.1.1",
30 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
31 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
32 | "dev": true
33 | },
34 | "node_modules/body-parser": {
35 | "version": "1.20.3",
36 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
37 | "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
38 | "dev": true,
39 | "dependencies": {
40 | "bytes": "3.1.2",
41 | "content-type": "~1.0.5",
42 | "debug": "2.6.9",
43 | "depd": "2.0.0",
44 | "destroy": "1.2.0",
45 | "http-errors": "2.0.0",
46 | "iconv-lite": "0.4.24",
47 | "on-finished": "2.4.1",
48 | "qs": "6.13.0",
49 | "raw-body": "2.5.2",
50 | "type-is": "~1.6.18",
51 | "unpipe": "1.0.0"
52 | },
53 | "engines": {
54 | "node": ">= 0.8",
55 | "npm": "1.2.8000 || >= 1.4.16"
56 | }
57 | },
58 | "node_modules/bytes": {
59 | "version": "3.1.2",
60 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
61 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
62 | "dev": true,
63 | "engines": {
64 | "node": ">= 0.8"
65 | }
66 | },
67 | "node_modules/call-bind-apply-helpers": {
68 | "version": "1.0.1",
69 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
70 | "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
71 | "dev": true,
72 | "dependencies": {
73 | "es-errors": "^1.3.0",
74 | "function-bind": "^1.1.2"
75 | },
76 | "engines": {
77 | "node": ">= 0.4"
78 | }
79 | },
80 | "node_modules/call-bound": {
81 | "version": "1.0.3",
82 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
83 | "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
84 | "dev": true,
85 | "dependencies": {
86 | "call-bind-apply-helpers": "^1.0.1",
87 | "get-intrinsic": "^1.2.6"
88 | },
89 | "engines": {
90 | "node": ">= 0.4"
91 | },
92 | "funding": {
93 | "url": "https://github.com/sponsors/ljharb"
94 | }
95 | },
96 | "node_modules/content-disposition": {
97 | "version": "0.5.4",
98 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
99 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
100 | "dev": true,
101 | "dependencies": {
102 | "safe-buffer": "5.2.1"
103 | },
104 | "engines": {
105 | "node": ">= 0.6"
106 | }
107 | },
108 | "node_modules/content-type": {
109 | "version": "1.0.5",
110 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
111 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
112 | "dev": true,
113 | "engines": {
114 | "node": ">= 0.6"
115 | }
116 | },
117 | "node_modules/cookie": {
118 | "version": "0.7.1",
119 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
120 | "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
121 | "dev": true,
122 | "engines": {
123 | "node": ">= 0.6"
124 | }
125 | },
126 | "node_modules/cookie-signature": {
127 | "version": "1.0.6",
128 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
129 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
130 | "dev": true
131 | },
132 | "node_modules/debug": {
133 | "version": "2.6.9",
134 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
135 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
136 | "dev": true,
137 | "dependencies": {
138 | "ms": "2.0.0"
139 | }
140 | },
141 | "node_modules/depd": {
142 | "version": "2.0.0",
143 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
144 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
145 | "dev": true,
146 | "engines": {
147 | "node": ">= 0.8"
148 | }
149 | },
150 | "node_modules/destroy": {
151 | "version": "1.2.0",
152 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
153 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
154 | "dev": true,
155 | "engines": {
156 | "node": ">= 0.8",
157 | "npm": "1.2.8000 || >= 1.4.16"
158 | }
159 | },
160 | "node_modules/dunder-proto": {
161 | "version": "1.0.1",
162 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
163 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
164 | "dev": true,
165 | "dependencies": {
166 | "call-bind-apply-helpers": "^1.0.1",
167 | "es-errors": "^1.3.0",
168 | "gopd": "^1.2.0"
169 | },
170 | "engines": {
171 | "node": ">= 0.4"
172 | }
173 | },
174 | "node_modules/ee-first": {
175 | "version": "1.1.1",
176 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
177 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
178 | "dev": true
179 | },
180 | "node_modules/encodeurl": {
181 | "version": "2.0.0",
182 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
183 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
184 | "dev": true,
185 | "engines": {
186 | "node": ">= 0.8"
187 | }
188 | },
189 | "node_modules/es-define-property": {
190 | "version": "1.0.1",
191 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
192 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
193 | "dev": true,
194 | "engines": {
195 | "node": ">= 0.4"
196 | }
197 | },
198 | "node_modules/es-errors": {
199 | "version": "1.3.0",
200 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
201 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
202 | "dev": true,
203 | "engines": {
204 | "node": ">= 0.4"
205 | }
206 | },
207 | "node_modules/es-object-atoms": {
208 | "version": "1.0.0",
209 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
210 | "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
211 | "dev": true,
212 | "dependencies": {
213 | "es-errors": "^1.3.0"
214 | },
215 | "engines": {
216 | "node": ">= 0.4"
217 | }
218 | },
219 | "node_modules/escape-html": {
220 | "version": "1.0.3",
221 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
222 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
223 | "dev": true
224 | },
225 | "node_modules/etag": {
226 | "version": "1.8.1",
227 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
228 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
229 | "dev": true,
230 | "engines": {
231 | "node": ">= 0.6"
232 | }
233 | },
234 | "node_modules/express": {
235 | "version": "4.21.2",
236 | "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
237 | "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
238 | "dev": true,
239 | "dependencies": {
240 | "accepts": "~1.3.8",
241 | "array-flatten": "1.1.1",
242 | "body-parser": "1.20.3",
243 | "content-disposition": "0.5.4",
244 | "content-type": "~1.0.4",
245 | "cookie": "0.7.1",
246 | "cookie-signature": "1.0.6",
247 | "debug": "2.6.9",
248 | "depd": "2.0.0",
249 | "encodeurl": "~2.0.0",
250 | "escape-html": "~1.0.3",
251 | "etag": "~1.8.1",
252 | "finalhandler": "1.3.1",
253 | "fresh": "0.5.2",
254 | "http-errors": "2.0.0",
255 | "merge-descriptors": "1.0.3",
256 | "methods": "~1.1.2",
257 | "on-finished": "2.4.1",
258 | "parseurl": "~1.3.3",
259 | "path-to-regexp": "0.1.12",
260 | "proxy-addr": "~2.0.7",
261 | "qs": "6.13.0",
262 | "range-parser": "~1.2.1",
263 | "safe-buffer": "5.2.1",
264 | "send": "0.19.0",
265 | "serve-static": "1.16.2",
266 | "setprototypeof": "1.2.0",
267 | "statuses": "2.0.1",
268 | "type-is": "~1.6.18",
269 | "utils-merge": "1.0.1",
270 | "vary": "~1.1.2"
271 | },
272 | "engines": {
273 | "node": ">= 0.10.0"
274 | },
275 | "funding": {
276 | "type": "opencollective",
277 | "url": "https://opencollective.com/express"
278 | }
279 | },
280 | "node_modules/finalhandler": {
281 | "version": "1.3.1",
282 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
283 | "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
284 | "dev": true,
285 | "dependencies": {
286 | "debug": "2.6.9",
287 | "encodeurl": "~2.0.0",
288 | "escape-html": "~1.0.3",
289 | "on-finished": "2.4.1",
290 | "parseurl": "~1.3.3",
291 | "statuses": "2.0.1",
292 | "unpipe": "~1.0.0"
293 | },
294 | "engines": {
295 | "node": ">= 0.8"
296 | }
297 | },
298 | "node_modules/forwarded": {
299 | "version": "0.2.0",
300 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
301 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
302 | "dev": true,
303 | "engines": {
304 | "node": ">= 0.6"
305 | }
306 | },
307 | "node_modules/fresh": {
308 | "version": "0.5.2",
309 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
310 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
311 | "dev": true,
312 | "engines": {
313 | "node": ">= 0.6"
314 | }
315 | },
316 | "node_modules/function-bind": {
317 | "version": "1.1.2",
318 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
319 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
320 | "dev": true,
321 | "funding": {
322 | "url": "https://github.com/sponsors/ljharb"
323 | }
324 | },
325 | "node_modules/get-intrinsic": {
326 | "version": "1.2.7",
327 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
328 | "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
329 | "dev": true,
330 | "dependencies": {
331 | "call-bind-apply-helpers": "^1.0.1",
332 | "es-define-property": "^1.0.1",
333 | "es-errors": "^1.3.0",
334 | "es-object-atoms": "^1.0.0",
335 | "function-bind": "^1.1.2",
336 | "get-proto": "^1.0.0",
337 | "gopd": "^1.2.0",
338 | "has-symbols": "^1.1.0",
339 | "hasown": "^2.0.2",
340 | "math-intrinsics": "^1.1.0"
341 | },
342 | "engines": {
343 | "node": ">= 0.4"
344 | },
345 | "funding": {
346 | "url": "https://github.com/sponsors/ljharb"
347 | }
348 | },
349 | "node_modules/get-proto": {
350 | "version": "1.0.1",
351 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
352 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
353 | "dev": true,
354 | "dependencies": {
355 | "dunder-proto": "^1.0.1",
356 | "es-object-atoms": "^1.0.0"
357 | },
358 | "engines": {
359 | "node": ">= 0.4"
360 | }
361 | },
362 | "node_modules/gopd": {
363 | "version": "1.2.0",
364 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
365 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
366 | "dev": true,
367 | "engines": {
368 | "node": ">= 0.4"
369 | },
370 | "funding": {
371 | "url": "https://github.com/sponsors/ljharb"
372 | }
373 | },
374 | "node_modules/has-symbols": {
375 | "version": "1.1.0",
376 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
377 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
378 | "dev": true,
379 | "engines": {
380 | "node": ">= 0.4"
381 | },
382 | "funding": {
383 | "url": "https://github.com/sponsors/ljharb"
384 | }
385 | },
386 | "node_modules/hasown": {
387 | "version": "2.0.2",
388 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
389 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
390 | "dev": true,
391 | "dependencies": {
392 | "function-bind": "^1.1.2"
393 | },
394 | "engines": {
395 | "node": ">= 0.4"
396 | }
397 | },
398 | "node_modules/http-errors": {
399 | "version": "2.0.0",
400 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
401 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
402 | "dev": true,
403 | "dependencies": {
404 | "depd": "2.0.0",
405 | "inherits": "2.0.4",
406 | "setprototypeof": "1.2.0",
407 | "statuses": "2.0.1",
408 | "toidentifier": "1.0.1"
409 | },
410 | "engines": {
411 | "node": ">= 0.8"
412 | }
413 | },
414 | "node_modules/iconv-lite": {
415 | "version": "0.4.24",
416 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
417 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
418 | "dev": true,
419 | "dependencies": {
420 | "safer-buffer": ">= 2.1.2 < 3"
421 | },
422 | "engines": {
423 | "node": ">=0.10.0"
424 | }
425 | },
426 | "node_modules/inherits": {
427 | "version": "2.0.4",
428 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
429 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
430 | "dev": true
431 | },
432 | "node_modules/ipaddr.js": {
433 | "version": "1.9.1",
434 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
435 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
436 | "dev": true,
437 | "engines": {
438 | "node": ">= 0.10"
439 | }
440 | },
441 | "node_modules/math-intrinsics": {
442 | "version": "1.1.0",
443 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
444 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
445 | "dev": true,
446 | "engines": {
447 | "node": ">= 0.4"
448 | }
449 | },
450 | "node_modules/media-typer": {
451 | "version": "0.3.0",
452 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
453 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
454 | "dev": true,
455 | "engines": {
456 | "node": ">= 0.6"
457 | }
458 | },
459 | "node_modules/merge-descriptors": {
460 | "version": "1.0.3",
461 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
462 | "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
463 | "dev": true,
464 | "funding": {
465 | "url": "https://github.com/sponsors/sindresorhus"
466 | }
467 | },
468 | "node_modules/methods": {
469 | "version": "1.1.2",
470 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
471 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
472 | "dev": true,
473 | "engines": {
474 | "node": ">= 0.6"
475 | }
476 | },
477 | "node_modules/mime": {
478 | "version": "1.6.0",
479 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
480 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
481 | "dev": true,
482 | "bin": {
483 | "mime": "cli.js"
484 | },
485 | "engines": {
486 | "node": ">=4"
487 | }
488 | },
489 | "node_modules/mime-db": {
490 | "version": "1.52.0",
491 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
492 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
493 | "dev": true,
494 | "engines": {
495 | "node": ">= 0.6"
496 | }
497 | },
498 | "node_modules/mime-types": {
499 | "version": "2.1.35",
500 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
501 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
502 | "dev": true,
503 | "dependencies": {
504 | "mime-db": "1.52.0"
505 | },
506 | "engines": {
507 | "node": ">= 0.6"
508 | }
509 | },
510 | "node_modules/ms": {
511 | "version": "2.0.0",
512 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
513 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
514 | "dev": true
515 | },
516 | "node_modules/negotiator": {
517 | "version": "0.6.3",
518 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
519 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
520 | "dev": true,
521 | "engines": {
522 | "node": ">= 0.6"
523 | }
524 | },
525 | "node_modules/object-inspect": {
526 | "version": "1.13.3",
527 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
528 | "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
529 | "dev": true,
530 | "engines": {
531 | "node": ">= 0.4"
532 | },
533 | "funding": {
534 | "url": "https://github.com/sponsors/ljharb"
535 | }
536 | },
537 | "node_modules/on-finished": {
538 | "version": "2.4.1",
539 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
540 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
541 | "dev": true,
542 | "dependencies": {
543 | "ee-first": "1.1.1"
544 | },
545 | "engines": {
546 | "node": ">= 0.8"
547 | }
548 | },
549 | "node_modules/parseurl": {
550 | "version": "1.3.3",
551 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
552 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
553 | "dev": true,
554 | "engines": {
555 | "node": ">= 0.8"
556 | }
557 | },
558 | "node_modules/path-to-regexp": {
559 | "version": "0.1.12",
560 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
561 | "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
562 | "dev": true
563 | },
564 | "node_modules/proxy-addr": {
565 | "version": "2.0.7",
566 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
567 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
568 | "dev": true,
569 | "dependencies": {
570 | "forwarded": "0.2.0",
571 | "ipaddr.js": "1.9.1"
572 | },
573 | "engines": {
574 | "node": ">= 0.10"
575 | }
576 | },
577 | "node_modules/qs": {
578 | "version": "6.13.0",
579 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
580 | "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
581 | "dev": true,
582 | "dependencies": {
583 | "side-channel": "^1.0.6"
584 | },
585 | "engines": {
586 | "node": ">=0.6"
587 | },
588 | "funding": {
589 | "url": "https://github.com/sponsors/ljharb"
590 | }
591 | },
592 | "node_modules/range-parser": {
593 | "version": "1.2.1",
594 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
595 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
596 | "dev": true,
597 | "engines": {
598 | "node": ">= 0.6"
599 | }
600 | },
601 | "node_modules/raw-body": {
602 | "version": "2.5.2",
603 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
604 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
605 | "dev": true,
606 | "dependencies": {
607 | "bytes": "3.1.2",
608 | "http-errors": "2.0.0",
609 | "iconv-lite": "0.4.24",
610 | "unpipe": "1.0.0"
611 | },
612 | "engines": {
613 | "node": ">= 0.8"
614 | }
615 | },
616 | "node_modules/safe-buffer": {
617 | "version": "5.2.1",
618 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
619 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
620 | "dev": true,
621 | "funding": [
622 | {
623 | "type": "github",
624 | "url": "https://github.com/sponsors/feross"
625 | },
626 | {
627 | "type": "patreon",
628 | "url": "https://www.patreon.com/feross"
629 | },
630 | {
631 | "type": "consulting",
632 | "url": "https://feross.org/support"
633 | }
634 | ]
635 | },
636 | "node_modules/safer-buffer": {
637 | "version": "2.1.2",
638 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
639 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
640 | "dev": true
641 | },
642 | "node_modules/send": {
643 | "version": "0.19.0",
644 | "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
645 | "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
646 | "dev": true,
647 | "dependencies": {
648 | "debug": "2.6.9",
649 | "depd": "2.0.0",
650 | "destroy": "1.2.0",
651 | "encodeurl": "~1.0.2",
652 | "escape-html": "~1.0.3",
653 | "etag": "~1.8.1",
654 | "fresh": "0.5.2",
655 | "http-errors": "2.0.0",
656 | "mime": "1.6.0",
657 | "ms": "2.1.3",
658 | "on-finished": "2.4.1",
659 | "range-parser": "~1.2.1",
660 | "statuses": "2.0.1"
661 | },
662 | "engines": {
663 | "node": ">= 0.8.0"
664 | }
665 | },
666 | "node_modules/send/node_modules/encodeurl": {
667 | "version": "1.0.2",
668 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
669 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
670 | "dev": true,
671 | "engines": {
672 | "node": ">= 0.8"
673 | }
674 | },
675 | "node_modules/send/node_modules/ms": {
676 | "version": "2.1.3",
677 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
678 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
679 | "dev": true
680 | },
681 | "node_modules/serve-static": {
682 | "version": "1.16.2",
683 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
684 | "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
685 | "dev": true,
686 | "dependencies": {
687 | "encodeurl": "~2.0.0",
688 | "escape-html": "~1.0.3",
689 | "parseurl": "~1.3.3",
690 | "send": "0.19.0"
691 | },
692 | "engines": {
693 | "node": ">= 0.8.0"
694 | }
695 | },
696 | "node_modules/setprototypeof": {
697 | "version": "1.2.0",
698 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
699 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
700 | "dev": true
701 | },
702 | "node_modules/side-channel": {
703 | "version": "1.1.0",
704 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
705 | "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
706 | "dev": true,
707 | "dependencies": {
708 | "es-errors": "^1.3.0",
709 | "object-inspect": "^1.13.3",
710 | "side-channel-list": "^1.0.0",
711 | "side-channel-map": "^1.0.1",
712 | "side-channel-weakmap": "^1.0.2"
713 | },
714 | "engines": {
715 | "node": ">= 0.4"
716 | },
717 | "funding": {
718 | "url": "https://github.com/sponsors/ljharb"
719 | }
720 | },
721 | "node_modules/side-channel-list": {
722 | "version": "1.0.0",
723 | "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
724 | "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
725 | "dev": true,
726 | "dependencies": {
727 | "es-errors": "^1.3.0",
728 | "object-inspect": "^1.13.3"
729 | },
730 | "engines": {
731 | "node": ">= 0.4"
732 | },
733 | "funding": {
734 | "url": "https://github.com/sponsors/ljharb"
735 | }
736 | },
737 | "node_modules/side-channel-map": {
738 | "version": "1.0.1",
739 | "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
740 | "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
741 | "dev": true,
742 | "dependencies": {
743 | "call-bound": "^1.0.2",
744 | "es-errors": "^1.3.0",
745 | "get-intrinsic": "^1.2.5",
746 | "object-inspect": "^1.13.3"
747 | },
748 | "engines": {
749 | "node": ">= 0.4"
750 | },
751 | "funding": {
752 | "url": "https://github.com/sponsors/ljharb"
753 | }
754 | },
755 | "node_modules/side-channel-weakmap": {
756 | "version": "1.0.2",
757 | "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
758 | "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
759 | "dev": true,
760 | "dependencies": {
761 | "call-bound": "^1.0.2",
762 | "es-errors": "^1.3.0",
763 | "get-intrinsic": "^1.2.5",
764 | "object-inspect": "^1.13.3",
765 | "side-channel-map": "^1.0.1"
766 | },
767 | "engines": {
768 | "node": ">= 0.4"
769 | },
770 | "funding": {
771 | "url": "https://github.com/sponsors/ljharb"
772 | }
773 | },
774 | "node_modules/statuses": {
775 | "version": "2.0.1",
776 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
777 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
778 | "dev": true,
779 | "engines": {
780 | "node": ">= 0.8"
781 | }
782 | },
783 | "node_modules/toidentifier": {
784 | "version": "1.0.1",
785 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
786 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
787 | "dev": true,
788 | "engines": {
789 | "node": ">=0.6"
790 | }
791 | },
792 | "node_modules/type-is": {
793 | "version": "1.6.18",
794 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
795 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
796 | "dev": true,
797 | "dependencies": {
798 | "media-typer": "0.3.0",
799 | "mime-types": "~2.1.24"
800 | },
801 | "engines": {
802 | "node": ">= 0.6"
803 | }
804 | },
805 | "node_modules/unpipe": {
806 | "version": "1.0.0",
807 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
808 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
809 | "dev": true,
810 | "engines": {
811 | "node": ">= 0.8"
812 | }
813 | },
814 | "node_modules/utils-merge": {
815 | "version": "1.0.1",
816 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
817 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
818 | "dev": true,
819 | "engines": {
820 | "node": ">= 0.4.0"
821 | }
822 | },
823 | "node_modules/vary": {
824 | "version": "1.1.2",
825 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
826 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
827 | "dev": true,
828 | "engines": {
829 | "node": ">= 0.8"
830 | }
831 | }
832 | },
833 | "dependencies": {
834 | "accepts": {
835 | "version": "1.3.8",
836 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
837 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
838 | "dev": true,
839 | "requires": {
840 | "mime-types": "~2.1.34",
841 | "negotiator": "0.6.3"
842 | }
843 | },
844 | "array-flatten": {
845 | "version": "1.1.1",
846 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
847 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
848 | "dev": true
849 | },
850 | "body-parser": {
851 | "version": "1.20.3",
852 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
853 | "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
854 | "dev": true,
855 | "requires": {
856 | "bytes": "3.1.2",
857 | "content-type": "~1.0.5",
858 | "debug": "2.6.9",
859 | "depd": "2.0.0",
860 | "destroy": "1.2.0",
861 | "http-errors": "2.0.0",
862 | "iconv-lite": "0.4.24",
863 | "on-finished": "2.4.1",
864 | "qs": "6.13.0",
865 | "raw-body": "2.5.2",
866 | "type-is": "~1.6.18",
867 | "unpipe": "1.0.0"
868 | }
869 | },
870 | "bytes": {
871 | "version": "3.1.2",
872 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
873 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
874 | "dev": true
875 | },
876 | "call-bind-apply-helpers": {
877 | "version": "1.0.1",
878 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
879 | "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
880 | "dev": true,
881 | "requires": {
882 | "es-errors": "^1.3.0",
883 | "function-bind": "^1.1.2"
884 | }
885 | },
886 | "call-bound": {
887 | "version": "1.0.3",
888 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
889 | "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
890 | "dev": true,
891 | "requires": {
892 | "call-bind-apply-helpers": "^1.0.1",
893 | "get-intrinsic": "^1.2.6"
894 | }
895 | },
896 | "content-disposition": {
897 | "version": "0.5.4",
898 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
899 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
900 | "dev": true,
901 | "requires": {
902 | "safe-buffer": "5.2.1"
903 | }
904 | },
905 | "content-type": {
906 | "version": "1.0.5",
907 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
908 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
909 | "dev": true
910 | },
911 | "cookie": {
912 | "version": "0.7.1",
913 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
914 | "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
915 | "dev": true
916 | },
917 | "cookie-signature": {
918 | "version": "1.0.6",
919 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
920 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
921 | "dev": true
922 | },
923 | "debug": {
924 | "version": "2.6.9",
925 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
926 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
927 | "dev": true,
928 | "requires": {
929 | "ms": "2.0.0"
930 | }
931 | },
932 | "depd": {
933 | "version": "2.0.0",
934 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
935 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
936 | "dev": true
937 | },
938 | "destroy": {
939 | "version": "1.2.0",
940 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
941 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
942 | "dev": true
943 | },
944 | "dunder-proto": {
945 | "version": "1.0.1",
946 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
947 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
948 | "dev": true,
949 | "requires": {
950 | "call-bind-apply-helpers": "^1.0.1",
951 | "es-errors": "^1.3.0",
952 | "gopd": "^1.2.0"
953 | }
954 | },
955 | "ee-first": {
956 | "version": "1.1.1",
957 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
958 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
959 | "dev": true
960 | },
961 | "encodeurl": {
962 | "version": "2.0.0",
963 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
964 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
965 | "dev": true
966 | },
967 | "es-define-property": {
968 | "version": "1.0.1",
969 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
970 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
971 | "dev": true
972 | },
973 | "es-errors": {
974 | "version": "1.3.0",
975 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
976 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
977 | "dev": true
978 | },
979 | "es-object-atoms": {
980 | "version": "1.0.0",
981 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
982 | "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
983 | "dev": true,
984 | "requires": {
985 | "es-errors": "^1.3.0"
986 | }
987 | },
988 | "escape-html": {
989 | "version": "1.0.3",
990 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
991 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
992 | "dev": true
993 | },
994 | "etag": {
995 | "version": "1.8.1",
996 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
997 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
998 | "dev": true
999 | },
1000 | "express": {
1001 | "version": "4.21.2",
1002 | "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
1003 | "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
1004 | "dev": true,
1005 | "requires": {
1006 | "accepts": "~1.3.8",
1007 | "array-flatten": "1.1.1",
1008 | "body-parser": "1.20.3",
1009 | "content-disposition": "0.5.4",
1010 | "content-type": "~1.0.4",
1011 | "cookie": "0.7.1",
1012 | "cookie-signature": "1.0.6",
1013 | "debug": "2.6.9",
1014 | "depd": "2.0.0",
1015 | "encodeurl": "~2.0.0",
1016 | "escape-html": "~1.0.3",
1017 | "etag": "~1.8.1",
1018 | "finalhandler": "1.3.1",
1019 | "fresh": "0.5.2",
1020 | "http-errors": "2.0.0",
1021 | "merge-descriptors": "1.0.3",
1022 | "methods": "~1.1.2",
1023 | "on-finished": "2.4.1",
1024 | "parseurl": "~1.3.3",
1025 | "path-to-regexp": "0.1.12",
1026 | "proxy-addr": "~2.0.7",
1027 | "qs": "6.13.0",
1028 | "range-parser": "~1.2.1",
1029 | "safe-buffer": "5.2.1",
1030 | "send": "0.19.0",
1031 | "serve-static": "1.16.2",
1032 | "setprototypeof": "1.2.0",
1033 | "statuses": "2.0.1",
1034 | "type-is": "~1.6.18",
1035 | "utils-merge": "1.0.1",
1036 | "vary": "~1.1.2"
1037 | }
1038 | },
1039 | "finalhandler": {
1040 | "version": "1.3.1",
1041 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
1042 | "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
1043 | "dev": true,
1044 | "requires": {
1045 | "debug": "2.6.9",
1046 | "encodeurl": "~2.0.0",
1047 | "escape-html": "~1.0.3",
1048 | "on-finished": "2.4.1",
1049 | "parseurl": "~1.3.3",
1050 | "statuses": "2.0.1",
1051 | "unpipe": "~1.0.0"
1052 | }
1053 | },
1054 | "forwarded": {
1055 | "version": "0.2.0",
1056 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
1057 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
1058 | "dev": true
1059 | },
1060 | "fresh": {
1061 | "version": "0.5.2",
1062 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
1063 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
1064 | "dev": true
1065 | },
1066 | "function-bind": {
1067 | "version": "1.1.2",
1068 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
1069 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
1070 | "dev": true
1071 | },
1072 | "get-intrinsic": {
1073 | "version": "1.2.7",
1074 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
1075 | "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
1076 | "dev": true,
1077 | "requires": {
1078 | "call-bind-apply-helpers": "^1.0.1",
1079 | "es-define-property": "^1.0.1",
1080 | "es-errors": "^1.3.0",
1081 | "es-object-atoms": "^1.0.0",
1082 | "function-bind": "^1.1.2",
1083 | "get-proto": "^1.0.0",
1084 | "gopd": "^1.2.0",
1085 | "has-symbols": "^1.1.0",
1086 | "hasown": "^2.0.2",
1087 | "math-intrinsics": "^1.1.0"
1088 | }
1089 | },
1090 | "get-proto": {
1091 | "version": "1.0.1",
1092 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
1093 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
1094 | "dev": true,
1095 | "requires": {
1096 | "dunder-proto": "^1.0.1",
1097 | "es-object-atoms": "^1.0.0"
1098 | }
1099 | },
1100 | "gopd": {
1101 | "version": "1.2.0",
1102 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
1103 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
1104 | "dev": true
1105 | },
1106 | "has-symbols": {
1107 | "version": "1.1.0",
1108 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
1109 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
1110 | "dev": true
1111 | },
1112 | "hasown": {
1113 | "version": "2.0.2",
1114 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
1115 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
1116 | "dev": true,
1117 | "requires": {
1118 | "function-bind": "^1.1.2"
1119 | }
1120 | },
1121 | "http-errors": {
1122 | "version": "2.0.0",
1123 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
1124 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
1125 | "dev": true,
1126 | "requires": {
1127 | "depd": "2.0.0",
1128 | "inherits": "2.0.4",
1129 | "setprototypeof": "1.2.0",
1130 | "statuses": "2.0.1",
1131 | "toidentifier": "1.0.1"
1132 | }
1133 | },
1134 | "iconv-lite": {
1135 | "version": "0.4.24",
1136 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
1137 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
1138 | "dev": true,
1139 | "requires": {
1140 | "safer-buffer": ">= 2.1.2 < 3"
1141 | }
1142 | },
1143 | "inherits": {
1144 | "version": "2.0.4",
1145 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
1146 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
1147 | "dev": true
1148 | },
1149 | "ipaddr.js": {
1150 | "version": "1.9.1",
1151 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
1152 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
1153 | "dev": true
1154 | },
1155 | "math-intrinsics": {
1156 | "version": "1.1.0",
1157 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
1158 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
1159 | "dev": true
1160 | },
1161 | "media-typer": {
1162 | "version": "0.3.0",
1163 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
1164 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
1165 | "dev": true
1166 | },
1167 | "merge-descriptors": {
1168 | "version": "1.0.3",
1169 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
1170 | "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
1171 | "dev": true
1172 | },
1173 | "methods": {
1174 | "version": "1.1.2",
1175 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
1176 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
1177 | "dev": true
1178 | },
1179 | "mime": {
1180 | "version": "1.6.0",
1181 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
1182 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
1183 | "dev": true
1184 | },
1185 | "mime-db": {
1186 | "version": "1.52.0",
1187 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
1188 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
1189 | "dev": true
1190 | },
1191 | "mime-types": {
1192 | "version": "2.1.35",
1193 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
1194 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
1195 | "dev": true,
1196 | "requires": {
1197 | "mime-db": "1.52.0"
1198 | }
1199 | },
1200 | "ms": {
1201 | "version": "2.0.0",
1202 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1203 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
1204 | "dev": true
1205 | },
1206 | "negotiator": {
1207 | "version": "0.6.3",
1208 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
1209 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
1210 | "dev": true
1211 | },
1212 | "object-inspect": {
1213 | "version": "1.13.3",
1214 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
1215 | "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
1216 | "dev": true
1217 | },
1218 | "on-finished": {
1219 | "version": "2.4.1",
1220 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
1221 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
1222 | "dev": true,
1223 | "requires": {
1224 | "ee-first": "1.1.1"
1225 | }
1226 | },
1227 | "parseurl": {
1228 | "version": "1.3.3",
1229 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1230 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
1231 | "dev": true
1232 | },
1233 | "path-to-regexp": {
1234 | "version": "0.1.12",
1235 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
1236 | "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
1237 | "dev": true
1238 | },
1239 | "proxy-addr": {
1240 | "version": "2.0.7",
1241 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
1242 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1243 | "dev": true,
1244 | "requires": {
1245 | "forwarded": "0.2.0",
1246 | "ipaddr.js": "1.9.1"
1247 | }
1248 | },
1249 | "qs": {
1250 | "version": "6.13.0",
1251 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
1252 | "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
1253 | "dev": true,
1254 | "requires": {
1255 | "side-channel": "^1.0.6"
1256 | }
1257 | },
1258 | "range-parser": {
1259 | "version": "1.2.1",
1260 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1261 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1262 | "dev": true
1263 | },
1264 | "raw-body": {
1265 | "version": "2.5.2",
1266 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
1267 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
1268 | "dev": true,
1269 | "requires": {
1270 | "bytes": "3.1.2",
1271 | "http-errors": "2.0.0",
1272 | "iconv-lite": "0.4.24",
1273 | "unpipe": "1.0.0"
1274 | }
1275 | },
1276 | "safe-buffer": {
1277 | "version": "5.2.1",
1278 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1279 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1280 | "dev": true
1281 | },
1282 | "safer-buffer": {
1283 | "version": "2.1.2",
1284 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1285 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
1286 | "dev": true
1287 | },
1288 | "send": {
1289 | "version": "0.19.0",
1290 | "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
1291 | "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
1292 | "dev": true,
1293 | "requires": {
1294 | "debug": "2.6.9",
1295 | "depd": "2.0.0",
1296 | "destroy": "1.2.0",
1297 | "encodeurl": "~1.0.2",
1298 | "escape-html": "~1.0.3",
1299 | "etag": "~1.8.1",
1300 | "fresh": "0.5.2",
1301 | "http-errors": "2.0.0",
1302 | "mime": "1.6.0",
1303 | "ms": "2.1.3",
1304 | "on-finished": "2.4.1",
1305 | "range-parser": "~1.2.1",
1306 | "statuses": "2.0.1"
1307 | },
1308 | "dependencies": {
1309 | "encodeurl": {
1310 | "version": "1.0.2",
1311 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
1312 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
1313 | "dev": true
1314 | },
1315 | "ms": {
1316 | "version": "2.1.3",
1317 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1318 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1319 | "dev": true
1320 | }
1321 | }
1322 | },
1323 | "serve-static": {
1324 | "version": "1.16.2",
1325 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
1326 | "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
1327 | "dev": true,
1328 | "requires": {
1329 | "encodeurl": "~2.0.0",
1330 | "escape-html": "~1.0.3",
1331 | "parseurl": "~1.3.3",
1332 | "send": "0.19.0"
1333 | }
1334 | },
1335 | "setprototypeof": {
1336 | "version": "1.2.0",
1337 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1338 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
1339 | "dev": true
1340 | },
1341 | "side-channel": {
1342 | "version": "1.1.0",
1343 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
1344 | "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
1345 | "dev": true,
1346 | "requires": {
1347 | "es-errors": "^1.3.0",
1348 | "object-inspect": "^1.13.3",
1349 | "side-channel-list": "^1.0.0",
1350 | "side-channel-map": "^1.0.1",
1351 | "side-channel-weakmap": "^1.0.2"
1352 | }
1353 | },
1354 | "side-channel-list": {
1355 | "version": "1.0.0",
1356 | "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
1357 | "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
1358 | "dev": true,
1359 | "requires": {
1360 | "es-errors": "^1.3.0",
1361 | "object-inspect": "^1.13.3"
1362 | }
1363 | },
1364 | "side-channel-map": {
1365 | "version": "1.0.1",
1366 | "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
1367 | "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
1368 | "dev": true,
1369 | "requires": {
1370 | "call-bound": "^1.0.2",
1371 | "es-errors": "^1.3.0",
1372 | "get-intrinsic": "^1.2.5",
1373 | "object-inspect": "^1.13.3"
1374 | }
1375 | },
1376 | "side-channel-weakmap": {
1377 | "version": "1.0.2",
1378 | "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
1379 | "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
1380 | "dev": true,
1381 | "requires": {
1382 | "call-bound": "^1.0.2",
1383 | "es-errors": "^1.3.0",
1384 | "get-intrinsic": "^1.2.5",
1385 | "object-inspect": "^1.13.3",
1386 | "side-channel-map": "^1.0.1"
1387 | }
1388 | },
1389 | "statuses": {
1390 | "version": "2.0.1",
1391 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1392 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1393 | "dev": true
1394 | },
1395 | "toidentifier": {
1396 | "version": "1.0.1",
1397 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1398 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1399 | "dev": true
1400 | },
1401 | "type-is": {
1402 | "version": "1.6.18",
1403 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1404 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1405 | "dev": true,
1406 | "requires": {
1407 | "media-typer": "0.3.0",
1408 | "mime-types": "~2.1.24"
1409 | }
1410 | },
1411 | "unpipe": {
1412 | "version": "1.0.0",
1413 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1414 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1415 | "dev": true
1416 | },
1417 | "utils-merge": {
1418 | "version": "1.0.1",
1419 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1420 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
1421 | "dev": true
1422 | },
1423 | "vary": {
1424 | "version": "1.1.2",
1425 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1426 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1427 | "dev": true
1428 | }
1429 | }
1430 | }
1431 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nodeteamdev.github.io",
3 | "version": "1.0.0",
4 | "description": "## Table of Contents",
5 | "main": "index.js",
6 | "directories": {
7 | "example": "examples"
8 | },
9 | "scripts": {
10 | "test": "echo \"Error: no test specified\" && exit 1",
11 | "example:1": "node ./examples/StreamTransformer.js",
12 | "example:2": "node ./examples/RetryMechanism.js",
13 | "example:3": "node ./examples/AdaptiveRateLimiter.js",
14 | "example:4": "node ./examples/DependencyInjectionContainer.js",
15 | "example:5": "node ./examples/PluginManager.js"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/nodeteamdev/nodeteamdev.github.io.git"
20 | },
21 | "keywords": [],
22 | "author": "",
23 | "license": "ISC",
24 | "bugs": {
25 | "url": "https://github.com/nodeteamdev/nodeteamdev.github.io/issues"
26 | },
27 | "homepage": "https://github.com/nodeteamdev/nodeteamdev.github.io#readme",
28 | "devDependencies": {
29 | "express": "^4.21.2"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------