Welcome to the final phase of our Jurassic Park adventure! In this exercise we'll combine all the techniques we've learned so far to create a complete application for monitoring and managing the Jurassic Park ecosystem. This is our "grand finale" before moving on to structuring larger projects.
We'll create the "JurassicTracker" application β a system for monitoring, tracking, and managing dinosaurs in the park. Our application will use:
Before we start implementing, let's plan the structure:
1jurassic-tracker/
2βββ index.js # Main application file
3βββ modules/
4β βββ api.js # API communication module
5β βββ dinosaurs.js # Dinosaur management module
6β βββ enclosures.js # Enclosure management module
7β βββ security.js # Security systems module
8β βββ monitoring.js # Park monitoring module
9β βββ utils.js # Utility tools
10β βββ errors/ # Error class directory
11β βββ index.js # Exports all errors
12β βββ APIError.js # API-related errors
13β βββ DinoError.js # Dinosaur-related errors
14β βββ SecurityError.js # Security-related errors
15βββ data/
16 βββ config.js # Application configurationLet's implement each of the modules. Remember that in practice these files would live in separate files according to the above structure.
First let's define our custom error classes:
1// errors/APIError.js
2export class APIError extends Error {
3 constructor(message, statusCode, endpoint) {
4 super(message);
5 this.name = 'APIError';
6 this.statusCode = statusCode;
7 this.endpoint = endpoint;
8 this.timestamp = new Date();
9 }
10
11 isServerError() {
12 return this.statusCode >= 500;
13 }
14
15 isClientError() {
16 return this.statusCode >= 400 && this.statusCode < 500;
17 }
18}
19
20// errors/DinoError.js
21export class DinoError extends Error {
22 constructor(message, dinoId, species) {
23 super(message);
24 this.name = 'DinoError';
25 this.dinoId = dinoId;
26 this.species = species;
27 this.timestamp = new Date();
28 }
29}
30
31export class DinoNotFoundError extends DinoError {
32 constructor(dinoId) {
33 super(`Dinosaur with ID ${dinoId} was not found`, dinoId, null);
34 this.name = 'DinoNotFoundError';
35 }
36}
37
38export class DinoBehaviorError extends DinoError {
39 constructor(message, dinoId, species, behaviorType, dangerLevel) {
40 super(message, dinoId, species);
41 this.name = 'DinoBehaviorError';
42 this.behaviorType = behaviorType;
43 this.dangerLevel = dangerLevel; // 1-10
44 }
45
46 requiresIntervention() {
47 return this.dangerLevel >= 7;
48 }
49}
50
51// errors/SecurityError.js
52export class SecurityError extends Error {
53 constructor(message, sector, systemType) {
54 super(message);
55 this.name = 'SecurityError';
56 this.sector = sector;
57 this.systemType = systemType;
58 this.timestamp = new Date();
59 }
60}
61
62export class EnclosureBreachError extends SecurityError {
63 constructor(sector, dinoSpecies, count) {
64 super(`Enclosure breach in sector ${sector}: ${count} ${dinoSpecies} specimens`, sector, 'enclosure');
65 this.name = 'EnclosureBreachError';
66 this.dinoSpecies = dinoSpecies;
67 this.count = count;
68 }
69}
70
71// errors/index.js
72export * from './APIError.js';
73export * from './DinoError.js';
74export * from './SecurityError.js';Our API module handles communication with the (hypothetical) Jurassic Park server:
1// modules/api.js
2import { APIError } from '../errors/index.js';
3
4const API_DELAY = 500; // ms - simulate network delay
5const API_ERROR_RATE = 0.1; // 10% chance of API error for realism
6
7// Simulated dinosaur database
8let dinosaursData = [
9 { id: 'trx1', name: 'Rex', species: 'Tyrannosaurus', enclosureId: 'enc1', health: 98, lastFed: new Date(Date.now() - 3600000), behavior: 'calm' },
10 { id: 'rap1', name: 'Blue', species: 'Velociraptor', enclosureId: 'enc2', health: 95, lastFed: new Date(Date.now() - 7200000), behavior: 'agitated' },
11 { id: 'rap2', name: 'Delta', species: 'Velociraptor', enclosureId: 'enc2', health: 92, lastFed: new Date(Date.now() - 7200000), behavior: 'hunting' },
12 { id: 'tri1', name: 'Sarah', species: 'Triceratops', enclosureId: 'enc3', health: 88, lastFed: new Date(Date.now() - 1800000), behavior: 'sleeping' },
13 { id: 'bro1', name: 'Bronty', species: 'Brontosaurus', enclosureId: 'enc4', health: 100, lastFed: new Date(Date.now() - 900000), behavior: 'grazing' },
14];
15
16// Helper function simulating network delay
17const simulateNetworkDelay = () => {
18 return new Promise(resolve => setTimeout(resolve, API_DELAY * (0.5 + Math.random())));
19};
20
21// Helper function simulating API errors
22const simulateApiError = (endpoint) => {
23 if (Math.random() < API_ERROR_RATE) {
24 const errorTypes = [
25 { statusCode: 500, message: 'Internal Server Error' },
26 { statusCode: 503, message: 'Service Unavailable' },
27 { statusCode: 429, message: 'Too Many Requests' },
28 { statusCode: 403, message: 'Forbidden - Insufficient permissions' }
29 ];
30 const errorType = errorTypes[Math.floor(Math.random() * errorTypes.length)];
31 throw new APIError(`API Error: ${errorType.message}`, errorType.statusCode, endpoint);
32 }
33};
34
35// API for fetching dinosaur data
36export async function getDinosaurs() {
37 const endpoint = '/api/dinosaurs';
38 try {
39 await simulateNetworkDelay();
40 simulateApiError(endpoint);
41 return [...dinosaursData]; // Return a copy to avoid mutation
42 } catch (error) {
43 if (error instanceof APIError) throw error;
44 throw new APIError('Error fetching dinosaur data', 500, endpoint);
45 }
46}
47
48export async function getDinosaurById(id) {
49 const endpoint = `/api/dinosaurs/${id}`;
50 try {
51 await simulateNetworkDelay();
52 simulateApiError(endpoint);
53 const dino = dinosaursData.find(d => d.id === id);
54 if (!dino) {
55 throw new APIError(`Dinosaur with ID ${id} not found`, 404, endpoint);
56 }
57 return { ...dino };
58 } catch (error) {
59 if (error instanceof APIError) throw error;
60 throw new APIError('Error fetching dinosaur', 500, endpoint);
61 }
62}This module manages dinosaur data and behavior:
1// modules/dinosaurs.js
2import { getDinosaurs, getDinosaurById, updateDinosaur, logEvent } from './api.js';
3import { DinoNotFoundError, DinoBehaviorError } from '../errors/index.js';
4
5// Assess dinosaur behavior risk
6function assessBehaviorRisk(dino) {
7 const dangerousSpecies = ['Tyrannosaurus', 'Velociraptor', 'Spinosaurus'];
8 const riskBehaviors = {
9 'calm': 1,
10 'grazing': 1,
11 'sleeping': 2,
12 'exploring': 4,
13 'agitated': 6,
14 'hunting': 7,
15 'aggressive': 9
16 };
17
18 // Base risk depends on species
19 let baseRisk = dangerousSpecies.includes(dino.species) ? 5 : 2;
20
21 // Risk modification based on behavior
22 const behaviorRisk = riskBehaviors[dino.behavior] || 3;
23
24 // Risk modification based on time since last feeding
25 const hoursSinceLastFed = (Date.now() - new Date(dino.lastFed).getTime()) / 3600000;
26 const hungerRisk = Math.min(5, Math.floor(hoursSinceLastFed / 3));
27
28 // Total risk level (1-10)
29 const totalRisk = Math.min(10, baseRisk + behaviorRisk + hungerRisk);
30
31 return {
32 risk: totalRisk,
33 factors: {
34 species: baseRisk,
35 behavior: behaviorRisk,
36 hunger: hungerRisk
37 }
38 };
39}
40
41// Get all dinosaurs
42export async function getAllDinosaurs() {
43 try {
44 const dinosaurs = await getDinosaurs();
45
46 // Enrich data with risk assessment
47 return dinosaurs.map(dino => ({
48 ...dino,
49 riskAssessment: assessBehaviorRisk(dino)
50 }));
51 } catch (error) {
52 console.error('Error fetching dinosaur list:', error);
53 throw error;
54 }
55}
56
57// Get a specific dinosaur by ID
58export async function getDinosaur(id) {
59 try {
60 const dino = await getDinosaurById(id);
61 return {
62 ...dino,
63 riskAssessment: assessBehaviorRisk(dino)
64 };
65 } catch (error) {
66 if (error.statusCode === 404) {
67 throw new DinoNotFoundError(id);
68 }
69 console.error(`Error fetching dinosaur ${id}:`, error);
70 throw error;
71 }
72}
73
74// Feed a dinosaur
75export async function feedDinosaur(id) {
76 try {
77 const dino = await getDinosaur(id);
78
79 // Update dinosaur state
80 const updatedDino = await updateDinosaur(id, {
81 lastFed: new Date(),
82 health: Math.min(100, dino.health + 5),
83 behavior: 'calm' // Dinosaurs are calmer after feeding
84 });
85
86 // Log the event
87 await logEvent('FEEDING', {
88 dinoId: id,
89 species: dino.species,
90 name: dino.name,
91 previousFeedingTime: dino.lastFed
92 });
93
94 return {
95 ...updatedDino,
96 riskAssessment: assessBehaviorRisk(updatedDino)
97 };
98 } catch (error) {
99 console.error(`Error feeding dinosaur ${id}:`, error);
100 throw error;
101 }
102}
103
104// Monitor dinosaur behavior
105export async function monitorBehavior(id) {
106 try {
107 const dino = await getDinosaur(id);
108 const { risk, factors } = assessBehaviorRisk(dino);
109
110 // Check if behavior requires intervention
111 if (risk >= 8) {
112 const error = new DinoBehaviorError(
113 `Dangerous behavior from dinosaur ${dino.name}`,
114 id,
115 dino.species,
116 dino.behavior,
117 risk
118 );
119
120 // Log the high-risk event
121 await logEvent('HIGH_RISK_BEHAVIOR', {
122 dinoId: id,
123 species: dino.species,
124 name: dino.name,
125 behavior: dino.behavior,
126 risk,
127 factors
128 });
129
130 throw error;
131 }
132
133 // For medium risk, just log
134 if (risk >= 5) {
135 await logEvent('MEDIUM_RISK_BEHAVIOR', {
136 dinoId: id,
137 species: dino.species,
138 name: dino.name,
139 behavior: dino.behavior,
140 risk,
141 factors
142 });
143 }
144
145 return { dino, risk, factors };
146 } catch (error) {
147 if (error instanceof DinoBehaviorError) {
148 throw error; // Re-throw our custom error
149 }
150 console.error(`Error monitoring dinosaur ${id}:`, error);
151 throw error;
152 }
153}
154
155// Simulate behavior change (used by the monitoring module)
156export async function simulateBehaviorChange() {
157 try {
158 const allDinos = await getAllDinosaurs();
159 const randomIndex = Math.floor(Math.random() * allDinos.length);
160 const targetDino = allDinos[randomIndex];
161
162 // Possible behaviors depending on species and last feeding
163 const carnivores = ['Tyrannosaurus', 'Velociraptor', 'Spinosaurus'];
164
165 let behaviorPool;
166 if (carnivores.includes(targetDino.species)) {
167 // Predators have higher chance of aggressive behavior
168 behaviorPool = ['calm', 'sleeping', 'exploring', 'agitated', 'hunting', 'aggressive'];
169 } else {
170 // Herbivores are generally calmer
171 behaviorPool = ['calm', 'grazing', 'sleeping', 'exploring', 'agitated'];
172 }
173
174 // Hungry dinosaurs are more aggressive
175 const hoursSinceLastFed = (Date.now() - new Date(targetDino.lastFed).getTime()) / 3600000;
176 if (hoursSinceLastFed > 8) {
177 if (carnivores.includes(targetDino.species)) {
178 behaviorPool = ['agitated', 'hunting', 'aggressive'];
179 } else {
180 behaviorPool = ['agitated', 'exploring'];
181 }
182 }
183
184 // Random behavior selection
185 const newBehavior = behaviorPool[Math.floor(Math.random() * behaviorPool.length)];
186
187 // Update dinosaur state
188 const updatedDino = await updateDinosaur(targetDino.id, {
189 behavior: newBehavior
190 });
191
192 await logEvent('BEHAVIOR_CHANGE', {
193 dinoId: targetDino.id,
194 species: targetDino.species,
195 name: targetDino.name,
196 previousBehavior: targetDino.behavior,
197 newBehavior
198 });
199
200 return {
201 ...updatedDino,
202 riskAssessment: assessBehaviorRisk(updatedDino)
203 };
204 } catch (error) {
205 console.error('Error simulating behavior change:', error);
206 throw error;
207 }
208}This module manages enclosures and their security status:
1// modules/enclosures.js
2import { getEnclosures, getEnclosureById, updateEnclosure, logEvent } from './api.js';
3import { SecurityError, EnclosureBreachError } from '../errors/index.js';
4import { getAllDinosaurs } from './dinosaurs.js';
5
6// Assess enclosure security status
7function assessSecurityStatus(enclosure, dinosaurs) {
8 // Check how many dinosaurs are in the enclosure
9 const enclosureDinosaurs = dinosaurs.filter(dino => dino.enclosureId === enclosure.id);
10
11 // Check for carnivores
12 const carnivores = enclosureDinosaurs.filter(dino =>
13 ['Tyrannosaurus', 'Velociraptor', 'Spinosaurus'].includes(dino.species)
14 );
15
16 // Check total risk
17 const totalRisk = enclosureDinosaurs.reduce((sum, dino) =>
18 sum + (dino.riskAssessment ? dino.riskAssessment.risk : 3), 0);
19
20 // Power status
21 const powerStatus = enclosure.powerStatus === 'active' ? 10 :
22 enclosure.powerStatus === 'backup' ? 5 : 0;
23
24 // Time since last inspection (in days)
25 const daysSinceInspection = (Date.now() - new Date(enclosure.lastInspection).getTime()) / (24 * 3600000);
26 const inspectionScore = daysSinceInspection < 1 ? 10 :
27 daysSinceInspection < 3 ? 7 :
28 daysSinceInspection < 7 ? 4 : 1;
29
30 // Calculate overall security score (0-100)
31 const securityScore = Math.max(0, Math.min(100,
32 enclosure.securityLevel * 10 - totalRisk + powerStatus + inspectionScore
33 ));
34
35 return {
36 id: enclosure.id,
37 name: enclosure.name,
38 sector: enclosure.sector,
39 dinosaurCount: enclosureDinosaurs.length,
40 carnivoreCount: carnivores.length,
41 securityLevel: enclosure.securityLevel,
42 powerStatus: enclosure.powerStatus,
43 securityScore,
44 status: securityScore >= 80 ? 'secure' :
45 securityScore >= 60 ? 'normal' :
46 securityScore >= 40 ? 'warning' : 'danger',
47 dinosaurs: enclosureDinosaurs.map(d => ({
48 id: d.id,
49 name: d.name,
50 species: d.species,
51 risk: d.riskAssessment ? d.riskAssessment.risk : 3
52 }))
53 };
54}
55
56// Get all enclosures with security assessment
57export async function getAllEnclosures() {
58 try {
59 const [enclosures, dinosaurs] = await Promise.all([
60 getEnclosures(),
61 getAllDinosaurs()
62 ]);
63
64 // Enrich data with security assessment
65 return enclosures.map(enclosure =>
66 assessSecurityStatus(enclosure, dinosaurs)
67 );
68 } catch (error) {
69 console.error('Error fetching enclosure list:', error);
70 throw error;
71 }
72}
73
74// Inspect an enclosure
75export async function inspectEnclosure(id) {
76 try {
77 const enclosure = await getEnclosureById(id);
78
79 // Update last inspection date
80 const updatedEnclosure = await updateEnclosure(id, {
81 lastInspection: new Date()
82 });
83
84 // Log the event
85 await logEvent('ENCLOSURE_INSPECTION', {
86 enclosureId: id,
87 name: enclosure.name,
88 sector: enclosure.sector,
89 previousInspection: enclosure.lastInspection
90 });
91
92 // Fetch dinosaurs to update security assessment
93 const dinosaurs = await getAllDinosaurs();
94
95 return assessSecurityStatus(updatedEnclosure, dinosaurs);
96 } catch (error) {
97 console.error(`Error inspecting enclosure ${id}:`, error);
98 throw error;
99 }
100}
101
102// Simulate power failure
103export async function simulatePowerFailure() {
104 try {
105 const enclosures = await getEnclosures();
106
107 // Select a random enclosure
108 const randomIndex = Math.floor(Math.random() * enclosures.length);
109 const targetEnclosure = enclosures[randomIndex];
110
111 if (targetEnclosure.powerStatus !== 'active') return;
112
113 // 70% chance of backup power, 30% chance of total failure
114 const newPowerStatus = Math.random() < 0.7 ? 'backup' : 'failure';
115
116 // Update enclosure state
117 const updatedEnclosure = await updateEnclosure(targetEnclosure.id, {
118 powerStatus: newPowerStatus
119 });
120
121 await logEvent('POWER_FAILURE', {
122 enclosureId: targetEnclosure.id,
123 name: targetEnclosure.name,
124 sector: targetEnclosure.sector,
125 powerStatus: newPowerStatus
126 });
127
128 // Check if failure could lead to an enclosure breach
129 if (newPowerStatus === 'failure') {
130 const dinosaurs = await getAllDinosaurs();
131 const enclosureDinos = dinosaurs.filter(dino => dino.enclosureId === targetEnclosure.id);
132
133 const dangerousSpecies = ['Tyrannosaurus', 'Velociraptor', 'Spinosaurus'];
134 const hasDangerousDinos = enclosureDinos.some(dino =>
135 dangerousSpecies.includes(dino.species)
136 );
137
138 if (hasDangerousDinos && targetEnclosure.securityLevel >= 8) {
139 const breachingSpecies = enclosureDinos.find(dino =>
140 dangerousSpecies.includes(dino.species)
141 ).species;
142
143 const breachingCount = enclosureDinos.filter(dino =>
144 dino.species === breachingSpecies
145 ).length;
146
147 throw new EnclosureBreachError(
148 targetEnclosure.sector,
149 breachingSpecies,
150 breachingCount
151 );
152 }
153 }
154
155 const dinosaurs = await getAllDinosaurs();
156 return assessSecurityStatus(updatedEnclosure, dinosaurs);
157 } catch (error) {
158 if (error instanceof EnclosureBreachError) {
159 await logEvent('ENCLOSURE_BREACH', {
160 sector: error.sector,
161 species: error.dinoSpecies,
162 count: error.count,
163 timestamp: error.timestamp
164 });
165 throw error;
166 }
167
168 console.error('Error simulating power failure:', error);
169 throw error;
170 }
171}
172
173// Repair power system
174export async function repairPowerSystem(id) {
175 try {
176 const enclosure = await getEnclosureById(id);
177
178 if (enclosure.powerStatus === 'active') {
179 throw new SecurityError(
180 'Power system is already active',
181 enclosure.sector,
182 'power'
183 );
184 }
185
186 // Update power status
187 const updatedEnclosure = await updateEnclosure(id, {
188 powerStatus: 'active'
189 });
190
191 await logEvent('POWER_RESTORED', {
192 enclosureId: id,
193 name: enclosure.name,
194 sector: enclosure.sector,
195 previousStatus: enclosure.powerStatus
196 });
197
198 const dinosaurs = await getAllDinosaurs();
199
200 return assessSecurityStatus(updatedEnclosure, dinosaurs);
201 } catch (error) {
202 console.error(`Error repairing power system for enclosure ${id}:`, error);
203 throw error;
204 }
205}1// modules/utils.js
2export function formatDate(date) {
3 return new Intl.DateTimeFormat('en-US', {
4 day: '2-digit', month: '2-digit', year: 'numeric',
5 hour: '2-digit', minute: '2-digit'
6 }).format(new Date(date));
7}
8
9export function formatTimeAgo(date) {
10 const diff = Date.now() - new Date(date).getTime();
11 const minutes = Math.floor(diff / 60000);
12 const hours = Math.floor(diff / 3600000);
13
14 if (minutes < 1) return 'just now';
15 if (minutes < 60) return `${minutes} min ago`;
16 if (hours < 24) return `${hours} h ago`;
17 return `${Math.floor(hours / 24)} days ago`;
18}
19
20export async function retry(fn, maxAttempts = 3, delay = 1000) {
21 let lastError;
22 for (let attempt = 1; attempt <= maxAttempts; attempt++) {
23 try {
24 return await fn();
25 } catch (error) {
26 lastError = error;
27 if (attempt < maxAttempts) {
28 console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
29 await new Promise(resolve => setTimeout(resolve, delay));
30 }
31 }
32 }
33 throw lastError;
34}
35
36export function memoize(fn) {
37 const cache = new Map();
38 return async function(...args) {
39 const key = JSON.stringify(args);
40 if (cache.has(key)) {
41 return cache.get(key);
42 }
43 const result = await fn(...args);
44 cache.set(key, result);
45 return result;
46 };
47}1// modules/monitoring.js
2import { getDinosaurs } from './api.js';
3import { DinoBehaviorError, EnclosureBreachError } from '../errors/index.js';
4import { formatTimeAgo } from './utils.js';
5
6class ParkMonitor {
7 constructor() {
8 this.intervals = [];
9 this.alertHandlers = [];
10 this.isRunning = false;
11 }
12
13 onAlert(handler) {
14 this.alertHandlers.push(handler);
15 return () => {
16 this.alertHandlers = this.alertHandlers.filter(h => h !== handler);
17 };
18 }
19
20 _fireAlert(alert) {
21 this.alertHandlers.forEach(handler => handler(alert));
22 }
23
24 async checkDinosaurStatuses() {
25 try {
26 const dinosaurs = await getDinosaurs();
27
28 for (const dino of dinosaurs) {
29 const hoursSinceFeeding = (Date.now() - new Date(dino.lastFed).getTime()) / 3600000;
30
31 if (hoursSinceFeeding > 8) {
32 this._fireAlert({
33 type: 'feeding',
34 severity: hoursSinceFeeding > 12 ? 'critical' : 'warning',
35 message: `${dino.name} hasn't been fed for ${formatTimeAgo(dino.lastFed)}`,
36 dinoId: dino.id
37 });
38 }
39
40 if (dino.behavior === 'escaped') {
41 this._fireAlert({
42 type: 'security',
43 severity: 'critical',
44 message: `BREACH: ${dino.name} (${dino.species}) has escaped!`,
45 dinoId: dino.id
46 });
47 }
48 }
49 } catch (error) {
50 console.error('Monitoring check failed:', error.message);
51 }
52 }
53
54 start(intervalMs = 30000) {
55 if (this.isRunning) return;
56 this.isRunning = true;
57 console.log('Park monitoring started');
58
59 const id = setInterval(() => {
60 this.checkDinosaurStatuses();
61 }, intervalMs);
62
63 this.intervals.push(id);
64 this.checkDinosaurStatuses(); // Run immediately
65 }
66
67 stop() {
68 this.intervals.forEach(id => clearInterval(id));
69 this.intervals = [];
70 this.isRunning = false;
71 console.log('Park monitoring stopped');
72 }
73}
74
75export const monitor = new ParkMonitor();Application configuration file:
1// data/config.js
2
3export const appConfig = {
4 // General application settings
5 appName: 'Jurassic Tracker',
6 version: '1.0.0',
7
8 // Monitoring settings
9 monitoring: {
10 dinoBehaviorInterval: 60000, // Dinosaur behavior monitoring interval (ms)
11 enclosureSecurityInterval: 90000, // Enclosure security monitoring interval (ms)
12 autoStart: true // Automatically start monitoring on startup
13 },
14
15 // API settings
16 api: {
17 baseUrl: 'https://jurassic-park-api.example.com', // In reality we'd use a real URL
18 timeout: 10000, // API request timeout (ms)
19 retryCount: 3 // Number of retry attempts for failed requests
20 },
21
22 // Dinosaur risk assessment
23 dinorisk: {
24 dangerousSpecies: ['Tyrannosaurus', 'Velociraptor', 'Spinosaurus', 'Allosaurus', 'Dilophosaurus'],
25 behaviorRiskMatrix: {
26 'calm': { base: 1, factor: 0.8 },
27 'grazing': { base: 1, factor: 0.9 },
28 'sleeping': { base: 2, factor: 0.7 },
29 'exploring': { base: 4, factor: 1.0 },
30 'agitated': { base: 6, factor: 1.2 },
31 'hunting': { base: 7, factor: 1.5 },
32 'aggressive': { base: 9, factor: 2.0 }
33 },
34 hungerRiskHours: 3, // Hours after which hunger starts affecting risk
35 hungerRiskFactor: 0.5 // Hunger impact factor on risk
36 },
37
38 // Security levels for notifications
39 security: {
40 alertThresholds: {
41 low: 3,
42 medium: 5,
43 high: 7,
44 critical: 9
45 },
46 autoResolveAlerts: false // Whether to automatically mark alerts as resolved
47 }
48};
49
50export default appConfig;1// index.js
2import { getDinosaurs } from './modules/api.js';
3import { monitor } from './modules/monitoring.js';
4import { retry } from './modules/utils.js';
5
6async function initialize() {
7 console.log('=== JurassicTracker Starting ===');
8
9 // Set up alert handling
10 const unsubAlert = monitor.onAlert((alert) => {
11 const icon = alert.severity === 'critical' ? 'π¨' : 'β οΈ';
12 console.log(`${icon} [${alert.type.toUpperCase()}] ${alert.message}`);
13 });
14
15 try {
16 // Fetch initial data with retry logic
17 const dinosaurs = await retry(() => getDinosaurs(), 3, 1000);
18 console.log(`Loaded ${dinosaurs.length} dinosaurs`);
19
20 // Start background monitoring
21 monitor.start(10000); // Check every 10 seconds
22
23 console.log('=== JurassicTracker Ready ===');
24
25 // Cleanup on shutdown
26 const cleanup = () => {
27 monitor.stop();
28 unsubAlert();
29 console.log('JurassicTracker shut down gracefully');
30 };
31
32 process.on('SIGINT', cleanup);
33 process.on('SIGTERM', cleanup);
34
35 } catch (error) {
36 console.error('Fatal initialization error:', error.message);
37 process.exit(1);
38 }
39}
40
41initialize();In our JurassicTracker application we used all the most important techniques learned in this module:
This application shows how to combine all the learned techniques into a functional, modular, and fault-tolerant system.
In the next exercise we'll delve into structuring code in even larger JavaScript projects, extending the techniques and patterns we've learned.