├── README.md ├── high-level-design ├── Readme.md └── prerequisites │ └── 01_monoliths_vs_microservices.md └── low-level-design └── parking-lot ├── README.md ├── docs ├── class-diagram.md ├── high-level-design.excalidraw ├── high-level-design.png ├── overall-class-diagram.md ├── sequence-diagram.md └── system-requirements.md └── ts ├── .gitignore ├── README.md ├── index.ts ├── package.json ├── pnpm-lock.yaml ├── src ├── account │ ├── account.ts │ ├── admin.ts │ └── parking.attendant.ts ├── entry.panel.ts ├── exit.panel.ts ├── hourly.cost.ts ├── parking-spot │ ├── car.spot.ts │ ├── disabled.spot.ts │ ├── electric.car.spot.ts │ ├── motorcycle.spot.ts │ ├── parking.spot.ts │ └── parking.spot.type.ts ├── parking.display.board.ts ├── parking.floor.ts ├── parking.lot.ts ├── parking.ticket.ts └── vehicles │ ├── car.ts │ ├── electric.ts │ ├── motor.cycle.ts │ ├── truck.ts │ ├── vehicle.ts │ └── vehicle.type.ts ├── test ├── entry.panel.negative.spec.ts ├── entry.panel.spec.ts ├── exit.panel.spec.ts ├── parking.floor.spec.ts ├── parking.spot.spec.ts └── vehicle.spec.ts ├── tsconfig.json └── vite.config.ts /README.md: -------------------------------------------------------------------------------- 1 | ### References 2 | 3 | - [System design primer](https://github.com/donnemartin/system-design-primer) 4 | - [System design concepts](https://github.com/karanpratapsingh/system-design) 5 | - [System design preparation links and resources](https://github.com/shashank88/system_design) 6 | - [System design interview questions](https://github.com/checkcheckzz/system-design-interview) 7 | - [System design - Basics, Engineering blogs, and Products & systems ](https://github.com/codersguild/System-Design) 8 | - [System design resources](https://github.com/InterviewReady/system-design-resources) 9 | - Low level design 10 | - [Grokking object oriented design interview](https://github.com/tssovi/grokking-the-object-oriented-design-interview) 11 | - [Low level design resources](https://github.com/prasadgujar/low-level-design-primer) 12 | 13 | ### Youtube channels 14 | 15 | - [Gaurav Sen](https://www.youtube.com/@gkcs) 16 | - [MIT system design](https://www.youtube.com/playlist?list=PLrw6a1wE39_tb2fErI4-WkMbsvGQk9_UB) 17 | - [Hussein Nasser](https://www.youtube.com/@hnasr) 18 | - [ Asli Engineering by Arpit Bhayani](https://www.youtube.com/@AsliEngineering) 19 | - [sudoCODE](https://www.youtube.com/@sudocode/videos) 20 | - [Udit Agarwal](https://www.youtube.com/@anomaly2104) 21 | - [Think Software](https://www.youtube.com/@ThinkSoftware) 22 | - [Distributed Systems Guy - Bala G](https://www.youtube.com/@gopalbala12) 23 | - [Tech Dummies](https://www.youtube.com/@TechDummiesNarendraL) 24 | - [codeKarle](https://www.youtube.com/@codeKarle) 25 | - [Rachit Jain - Playlist](https://www.youtube.com/playlist?list=PLfBJlB6T2eOukvc2lrkAbeZBqUS94ji1r) 26 | -------------------------------------------------------------------------------- /high-level-design/Readme.md: -------------------------------------------------------------------------------- 1 | ### Index 2 | 3 | ###### Prerequisites 4 | 5 | -------------------------------------------------------------------------------- /high-level-design/prerequisites/01_monoliths_vs_microservices.md: -------------------------------------------------------------------------------- 1 | ### Monoliths vs Microservices 2 | 3 | A **monolithic** application is built as a single unified unit while a **microservices** architecture is a collection of smaller, independently deployable services. 4 | 5 | #### Reference 6 | 7 | [1] [Microservices vs. monolithic architecture - Atlassian](https://www.atlassian.com/microservices/microservices-architecture/microservices-vs-monolith) 8 | 9 | [2] [What’s the Difference Between Monolithic and Microservices Architecture?](https://aws.amazon.com/compare/the-difference-between-monolithic-and-microservices-architecture/) -------------------------------------------------------------------------------- /low-level-design/parking-lot/README.md: -------------------------------------------------------------------------------- 1 | ### Reference 2 | 3 | - https://www.youtube.com/watch?v=nnpT0WXifLk 4 | - https://www.youtube.com/watch?v=DSGsa0pu8-k 5 | - https://www.youtube.com/watch?v=tVRyb4HaHgw 6 | - https://www.youtube.com/watch?v=7IX84K9g23U 7 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/docs/class-diagram.md: -------------------------------------------------------------------------------- 1 | ```mermaid 2 | classDiagram 3 | class ParkingSpot { 4 | -parkingSpotID: string 5 | -isSpotAvailable: boolean 6 | -vehicle: Vehicle | null 7 | -parkingSpotType: ParkingSpotType 8 | +isSpotFree(): boolean 9 | +getParkingSpotType(): ParkingSpotType 10 | +getParkingSpotID(): string 11 | +getVehicleDetails(): Vehicle | null 12 | +assignVehicleToSpot(): void 13 | +vacateVehicleFromSpot(): void 14 | } 15 | ParkingSpot <|-- CarSpot: Extends 16 | ParkingSpot <|-- DisabledSpot: Extends 17 | ParkingSpot <|-- ElectricCarSpot: Extends 18 | ParkingSpot <|-- MotorcycleSpot: Extends 19 | class Vehicle { 20 | -registerNumber: string 21 | -type: VehicleType 22 | +getVehicleType(): VehicleType 23 | +getVehicleRegisterNumber(): string 24 | } 25 | Vehicle <|-- Car: Extends 26 | Vehicle <|-- Truck: Extends 27 | Vehicle <|-- ElectricCar: Extends 28 | Vehicle <|-- Motorbike: Extends 29 | ParkingSpot --> Vehicle: has 30 | ``` 31 | 32 | ```mermaid 33 | classDiagram 34 | class ParkingFloor { 35 | -parkingSpots: Map~ParkingSpotType, Array~ParkingSpot~~ 36 | -parkingFloorID: string 37 | -displayBoard: DisplayBoard 38 | +getParkingFloorID(): string 39 | +getListOfParkingSpots(): Map~ParkingSpotType, ParkingSpot[]~ 40 | +getAvailableSpot(vehicle: Vehicle): ParkingSpot | undefined 41 | +showDisplayBoard(): void 42 | -getSpotTypeForVehicle(vehicleType: VehicleType): ParkingSpotType 43 | +getInUseSpotID(vehicleType: VehicleType): ParkingSpot[] | undefined 44 | } 45 | class DisplayBoard { 46 | +displayMessage(parkingSpotType: ParkingSpotType, freeSpots: number): void 47 | } 48 | ParkingFloor *-- DisplayBoard 49 | ParkingFloor *-- ParkingSpot 50 | ``` 51 | 52 | ```mermaid 53 | classDiagram 54 | class ParkingLot { 55 | -instance: ParkingLot 56 | -parkingFloor: Array~ParkingFloor~ 57 | -entryPanels: Array~EntryPanel~ 58 | -exitPanels: Array~ExitPanel~ 59 | +getInstance(): ParkingLot 60 | +vacateParkingSpot(parkingSpotID: string): ParkingSpot | undefined 61 | +getListOfParkingFloor(): ParkingFloor[] 62 | +getListOfEntryPanel(): EntryPanel[] 63 | +getListOfExitPanel(): ExitPanel[] 64 | } 65 | class EntryPanel { 66 | -entryPanelID: string 67 | +getEntryPanelID(): string 68 | +getParkingTicket(vehicle: Vehicle): ParkingTicket 69 | -generateParkingTicket(vehicle: Vehicle, parkingFloorID: string, parkingSpotID: string) 70 | } 71 | class ExitPanel { 72 | -exitPanelID: string 73 | +getExitPanelID(): string 74 | +checkout(parkingTicket: ParkingTicket): ParkingTicket 75 | -calculatePrice(parkingSpotType: ParkingSpotType, duration: number): number 76 | -calculateDurationInHours(parkingTicket: ParkingTicket): number 77 | } 78 | class ParkingTicket { 79 | -parkingTicketID: string; 80 | -vehicleType: VehicleType; 81 | -vehicleRegisterNumber: string; 82 | -parkingSpotID: string; 83 | -parkingFloorID: string; 84 | -startTime: number; 85 | -endTime: number; 86 | -amount: number; 87 | +setStartTime(currentDateTime: Date): this 88 | +getStartTime(): Date 89 | +getEndTime(): Date 90 | +getAmount(): number 91 | +setEndTime(currentDateTime: Date): this 92 | +setAmount(amount: number): this 93 | +getVehicleType(): VehicleType 94 | +getParkingSpotID(): string 95 | +getParkingFloorID(): string 96 | +getVehicleRegisterNumber(): string 97 | } 98 | class HourlyCost { 99 | -hourlyCosts: Map~ParkingSpotType, number~ 100 | +getCost(parkingSpotType: ParkingSpotType): number 101 | } 102 | ParkingLot *-- EntryPanel 103 | ParkingLot *-- ExitPanel 104 | ParkingLot *-- ParkingFloor 105 | EntryPanel *-- ParkingTicket 106 | ExitPanel *-- ParkingTicket 107 | ExitPanel *-- Payment 108 | ExitPanel --> HourlyCost: has 109 | ``` 110 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/docs/high-level-design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madhank93/learn-system-design/18c38c11e283385b33deb07264553aa67415cb5f/low-level-design/parking-lot/docs/high-level-design.png -------------------------------------------------------------------------------- /low-level-design/parking-lot/docs/overall-class-diagram.md: -------------------------------------------------------------------------------- 1 | ```mermaid 2 | classDiagram 3 | class ParkingSpot { 4 | -parkingSpotID: string 5 | -isSpotAvailable: boolean 6 | -vehicle: Vehicle | null 7 | -parkingSpotType: ParkingSpotType 8 | +isSpotFree(): boolean 9 | +getParkingSpotType(): ParkingSpotType 10 | +getParkingSpotID(): string 11 | +getVehicleDetails(): Vehicle | null 12 | +assignVehicleToSpot(): void 13 | +vacateVehicleFromSpot(): void 14 | } 15 | ParkingSpot <|-- CarSpot: Extends 16 | ParkingSpot <|-- DisabledSpot: Extends 17 | ParkingSpot <|-- ElectricCarSpot: Extends 18 | ParkingSpot <|-- MotorcycleSpot: Extends 19 | class Vehicle { 20 | -registerNumber: string 21 | -type: VehicleType 22 | +getVehicleType(): VehicleType 23 | +getVehicleRegisterNumber(): string 24 | } 25 | Vehicle <|-- Car: Extends 26 | Vehicle <|-- Truck: Extends 27 | Vehicle <|-- ElectricCar: Extends 28 | Vehicle <|-- Motorbike: Extends 29 | ParkingSpot --> Vehicle: has 30 | class ParkingFloor { 31 | -parkingSpots: Map~ParkingSpotType, Array~ParkingSpot~~ 32 | -parkingFloorID: string 33 | -displayBoard: DisplayBoard 34 | +getParkingFloorID(): string 35 | +getListOfParkingSpots(): Map~ParkingSpotType, ParkingSpot[]~ 36 | +getAvailableSpot(vehicle: Vehicle): ParkingSpot | undefined 37 | +showDisplayBoard(): void 38 | -getSpotTypeForVehicle(vehicleType: VehicleType): ParkingSpotType 39 | +getInUseSpotID(vehicleType: VehicleType): ParkingSpot[] | undefined 40 | } 41 | class DisplayBoard { 42 | +displayMessage(parkingSpotType: ParkingSpotType, freeSpots: number): void 43 | } 44 | ParkingFloor *-- DisplayBoard 45 | ParkingFloor *-- ParkingSpot 46 | class ParkingLot { 47 | -instance: ParkingLot 48 | -parkingFloor: Array~ParkingFloor~ 49 | -entryPanels: Array~EntryPanel~ 50 | -exitPanels: Array~ExitPanel~ 51 | +getInstance(): ParkingLot 52 | +vacateParkingSpot(parkingSpotID: string): ParkingSpot | undefined 53 | +getListOfParkingFloor(): ParkingFloor[] 54 | +getListOfEntryPanel(): EntryPanel[] 55 | +getListOfExitPanel(): ExitPanel[] 56 | } 57 | class EntryPanel { 58 | -entryPanelID: string 59 | +getEntryPanelID(): string 60 | +getParkingTicket(vehicle: Vehicle): ParkingTicket 61 | -generateParkingTicket(vehicle: Vehicle, parkingFloorID: string, parkingSpotID: string) 62 | } 63 | class ExitPanel { 64 | -exitPanelID: string 65 | +getExitPanelID(): string 66 | +checkout(parkingTicket: ParkingTicket): ParkingTicket 67 | -calculatePrice(parkingSpotType: ParkingSpotType, duration: number): number 68 | -calculateDurationInHours(parkingTicket: ParkingTicket): number 69 | } 70 | class ParkingTicket { 71 | -parkingTicketID: string; 72 | -vehicleType: VehicleType; 73 | -vehicleRegisterNumber: string; 74 | -parkingSpotID: string; 75 | -parkingFloorID: string; 76 | -startTime: number; 77 | -endTime: number; 78 | -amount: number; 79 | +setStartTime(currentDateTime: Date): this 80 | +getStartTime(): Date 81 | +getEndTime(): Date 82 | +getAmount(): number 83 | +setEndTime(currentDateTime: Date): this 84 | +setAmount(amount: number): this 85 | +getVehicleType(): VehicleType 86 | +getParkingSpotID(): string 87 | +getParkingFloorID(): string 88 | +getVehicleRegisterNumber(): string 89 | } 90 | class HourlyCost { 91 | -hourlyCosts: Map~ParkingSpotType, number~ 92 | +getCost(parkingSpotType: ParkingSpotType): number 93 | } 94 | ParkingLot *-- EntryPanel 95 | ParkingLot *-- ExitPanel 96 | ParkingLot *-- ParkingFloor 97 | EntryPanel *-- ParkingTicket 98 | ExitPanel *-- ParkingTicket 99 | ExitPanel *-- Payment 100 | ExitPanel --> HourlyCost: has 101 | 102 | class ParkingSpotType{ 103 | <> 104 | Disabled 105 | Compact 106 | Large 107 | Motorcycle 108 | ElectricCar 109 | } 110 | class VehicleType{ 111 | <> 112 | Car 113 | Motorcycle 114 | Truck 115 | ElectricCar 116 | Van 117 | } 118 | ``` 119 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/docs/sequence-diagram.md: -------------------------------------------------------------------------------- 1 | ```mermaid 2 | sequenceDiagram 3 | actor Customer 4 | rect rgb(138, 154, 91) 5 | alt positive scenario 6 | Customer->>EntryPanel: Have to park a vehicle 7 | rect rgb(255, 87, 51) 8 | alt check availability 9 | EntryPanel->>ParkingLot: Can this vehicle be parked ? 10 | ParkingLot->>ParkingFloor: Is floor can accommodate ? 11 | ParkingFloor->>ParkingSpot: Is spot available ? 12 | ParkingSpot->>EntryPanel: Can be parked 13 | end 14 | end 15 | EntryPanel->>Customer: Collect Parking Ticket 16 | end 17 | end 18 | actor Admin 19 | Admin-->>ParkingLot: Adding Parking floor 20 | Admin-->>ParkingLot: Adding Parking spot 21 | ``` 22 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/docs/system-requirements.md: -------------------------------------------------------------------------------- 1 | ### Systems Requirement: 2 | 3 | - The parking lot should have multiple floors where customers can park their cars. 4 | - The parking lot should have multiple entry and exit points. 5 | - Customers can collect a parking ticket from the entry points and can pay the parking fee at the exit points to the parking attendant or automated exit panel 6 | - Customers can pay via both cash and credit cards. 7 | - The system should not allow more vehicles than the maximum capacity of the parking lot. If the parking is full, the system should be able to show a message at the entrance panel and on the parking display board on the ground floor. 8 | - Each parking floor will have many parking spots. The system should support multiple types of parking spots such as Compact, Large, Disabled, Motorcycle, etc. 9 | - The system should support parking for different types of vehicles like car, truck, van, motorcycle, etc. 10 | - Each parking floor should have a display board showing any free parking spot for each spot type. 11 | - The system should support a per-hour parking fee model. For example, customers have to pay some amount based on the Vehicle type. 12 | - Admins should be able to add parking floors and parking spot. 13 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | html -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | node --experimental-specifier-resolution=node --loader ts-node/esm "/home/madhank/Desktop/git/learn-system-design/low-level-design/parking-lot/ts/index.ts" 3 | ``` 4 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/index.ts: -------------------------------------------------------------------------------- 1 | import Admin from "./src/account/admin"; 2 | import { EntryPanel } from "./src/entry.panel"; 3 | import { ExitPanel } from "./src/exit.panel"; 4 | import { CarSpot } from "./src/parking-spot/car.spot"; 5 | import { MotorcycleSpot } from "./src/parking-spot/motorcycle.spot"; 6 | import { ParkingFloor } from "./src/parking.floor"; 7 | import { ParkingLot } from "./src/parking.lot"; 8 | import { Car } from "./src/vehicles/car"; 9 | import { MotorCycle } from "./src/vehicles/motor.cycle"; 10 | 11 | // Crating parking lot instance 12 | const parkingLot = ParkingLot.getInstance(); 13 | 14 | // Creating Admin account 15 | const admin = new Admin("Jack", "Pass12345"); 16 | 17 | // Adding Parking floors 18 | const parkingFloor1 = new ParkingFloor("PF1001"); 19 | const parkingFloor2 = new ParkingFloor("PF1002"); 20 | admin.addParkingFloor(parkingFloor1); 21 | admin.addParkingFloor(parkingFloor2); 22 | 23 | // Adding Entry panels 24 | const entryPanel1 = new EntryPanel("ENT001"); 25 | const entryPanel2 = new EntryPanel("ENT002"); 26 | admin.addEntrancePanel(entryPanel1); 27 | admin.addEntrancePanel(entryPanel2); 28 | 29 | // Adding Exit panels 30 | const exitPanel1 = new ExitPanel("EXT001"); 31 | const exitPanel2 = new ExitPanel("EXT002"); 32 | admin.addExitPanel(exitPanel1); 33 | admin.addExitPanel(exitPanel2); 34 | 35 | const firstFloorID = parkingLot 36 | .getListOfParkingFloor() 37 | .at(0) 38 | ?.getParkingFloorID(); 39 | 40 | // Adding Parking spots to the floor 41 | const carParkingSpot1 = new CarSpot("CPS1001"); 42 | admin.addParkingSpot(firstFloorID!, carParkingSpot1); 43 | const carParkingSpot2 = new CarSpot("CPS1002"); 44 | admin.addParkingSpot(firstFloorID!, carParkingSpot2); 45 | const bikeParkingSpot1 = new MotorcycleSpot("MSP1001"); 46 | admin.addParkingSpot(firstFloorID!, bikeParkingSpot1); 47 | const bikeParkingSpot2 = new MotorcycleSpot("MSP1002"); 48 | admin.addParkingSpot(firstFloorID!, bikeParkingSpot2); 49 | 50 | // Creating vehicles 51 | const car1 = new Car("TN23890"); 52 | const car2 = new Car("TN99234"); 53 | const bike = new MotorCycle("TN34909"); 54 | 55 | // Call the display board 56 | parkingFloor1.showDisplayBoard(); 57 | 58 | // Park the vehicle 59 | let ticketForCar1 = entryPanel1.getParkingTicket(car1); 60 | console.log("*** Parking ticket for car1 *** \n", ticketForCar1); 61 | 62 | // Checkout the vehicle 63 | ticketForCar1 = exitPanel2.checkout(ticketForCar1); 64 | console.log("*** Parking ticket for car1 *** \n", ticketForCar1); 65 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parking-lot-system", 3 | "version": "1.0.0", 4 | "description": "Parking lot system - low level system design", 5 | "type": "module", 6 | "scripts": { 7 | "test": "vitest", 8 | "coverage": "vitest coverage", 9 | "index": "node --loader ts-node/esm index.ts" 10 | }, 11 | "keywords": [], 12 | "author": "Madhan K", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@types/node": "^18.11.18", 16 | "ts-node": "^10.9.1", 17 | "typescript": "^4.9.4" 18 | }, 19 | "devDependencies": { 20 | "vitest": "^0.27.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | '@types/node': 9 | specifier: ^18.11.18 10 | version: 18.11.18 11 | ts-node: 12 | specifier: ^10.9.1 13 | version: 10.9.1(@types/node@18.11.18)(typescript@4.9.4) 14 | typescript: 15 | specifier: ^4.9.4 16 | version: 4.9.4 17 | 18 | devDependencies: 19 | vitest: 20 | specifier: ^0.27.1 21 | version: 0.27.1 22 | 23 | packages: 24 | 25 | /@cspotcode/source-map-support@0.8.1: 26 | resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} 27 | engines: {node: '>=12'} 28 | dependencies: 29 | '@jridgewell/trace-mapping': 0.3.9 30 | dev: false 31 | 32 | /@esbuild/android-arm64@0.16.17: 33 | resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} 34 | engines: {node: '>=12'} 35 | cpu: [arm64] 36 | os: [android] 37 | requiresBuild: true 38 | dev: true 39 | optional: true 40 | 41 | /@esbuild/android-arm@0.16.17: 42 | resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==} 43 | engines: {node: '>=12'} 44 | cpu: [arm] 45 | os: [android] 46 | requiresBuild: true 47 | dev: true 48 | optional: true 49 | 50 | /@esbuild/android-x64@0.16.17: 51 | resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==} 52 | engines: {node: '>=12'} 53 | cpu: [x64] 54 | os: [android] 55 | requiresBuild: true 56 | dev: true 57 | optional: true 58 | 59 | /@esbuild/darwin-arm64@0.16.17: 60 | resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} 61 | engines: {node: '>=12'} 62 | cpu: [arm64] 63 | os: [darwin] 64 | requiresBuild: true 65 | dev: true 66 | optional: true 67 | 68 | /@esbuild/darwin-x64@0.16.17: 69 | resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} 70 | engines: {node: '>=12'} 71 | cpu: [x64] 72 | os: [darwin] 73 | requiresBuild: true 74 | dev: true 75 | optional: true 76 | 77 | /@esbuild/freebsd-arm64@0.16.17: 78 | resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} 79 | engines: {node: '>=12'} 80 | cpu: [arm64] 81 | os: [freebsd] 82 | requiresBuild: true 83 | dev: true 84 | optional: true 85 | 86 | /@esbuild/freebsd-x64@0.16.17: 87 | resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} 88 | engines: {node: '>=12'} 89 | cpu: [x64] 90 | os: [freebsd] 91 | requiresBuild: true 92 | dev: true 93 | optional: true 94 | 95 | /@esbuild/linux-arm64@0.16.17: 96 | resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} 97 | engines: {node: '>=12'} 98 | cpu: [arm64] 99 | os: [linux] 100 | requiresBuild: true 101 | dev: true 102 | optional: true 103 | 104 | /@esbuild/linux-arm@0.16.17: 105 | resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} 106 | engines: {node: '>=12'} 107 | cpu: [arm] 108 | os: [linux] 109 | requiresBuild: true 110 | dev: true 111 | optional: true 112 | 113 | /@esbuild/linux-ia32@0.16.17: 114 | resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==} 115 | engines: {node: '>=12'} 116 | cpu: [ia32] 117 | os: [linux] 118 | requiresBuild: true 119 | dev: true 120 | optional: true 121 | 122 | /@esbuild/linux-loong64@0.16.17: 123 | resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==} 124 | engines: {node: '>=12'} 125 | cpu: [loong64] 126 | os: [linux] 127 | requiresBuild: true 128 | dev: true 129 | optional: true 130 | 131 | /@esbuild/linux-mips64el@0.16.17: 132 | resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==} 133 | engines: {node: '>=12'} 134 | cpu: [mips64el] 135 | os: [linux] 136 | requiresBuild: true 137 | dev: true 138 | optional: true 139 | 140 | /@esbuild/linux-ppc64@0.16.17: 141 | resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==} 142 | engines: {node: '>=12'} 143 | cpu: [ppc64] 144 | os: [linux] 145 | requiresBuild: true 146 | dev: true 147 | optional: true 148 | 149 | /@esbuild/linux-riscv64@0.16.17: 150 | resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==} 151 | engines: {node: '>=12'} 152 | cpu: [riscv64] 153 | os: [linux] 154 | requiresBuild: true 155 | dev: true 156 | optional: true 157 | 158 | /@esbuild/linux-s390x@0.16.17: 159 | resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==} 160 | engines: {node: '>=12'} 161 | cpu: [s390x] 162 | os: [linux] 163 | requiresBuild: true 164 | dev: true 165 | optional: true 166 | 167 | /@esbuild/linux-x64@0.16.17: 168 | resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==} 169 | engines: {node: '>=12'} 170 | cpu: [x64] 171 | os: [linux] 172 | requiresBuild: true 173 | dev: true 174 | optional: true 175 | 176 | /@esbuild/netbsd-x64@0.16.17: 177 | resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==} 178 | engines: {node: '>=12'} 179 | cpu: [x64] 180 | os: [netbsd] 181 | requiresBuild: true 182 | dev: true 183 | optional: true 184 | 185 | /@esbuild/openbsd-x64@0.16.17: 186 | resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==} 187 | engines: {node: '>=12'} 188 | cpu: [x64] 189 | os: [openbsd] 190 | requiresBuild: true 191 | dev: true 192 | optional: true 193 | 194 | /@esbuild/sunos-x64@0.16.17: 195 | resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==} 196 | engines: {node: '>=12'} 197 | cpu: [x64] 198 | os: [sunos] 199 | requiresBuild: true 200 | dev: true 201 | optional: true 202 | 203 | /@esbuild/win32-arm64@0.16.17: 204 | resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==} 205 | engines: {node: '>=12'} 206 | cpu: [arm64] 207 | os: [win32] 208 | requiresBuild: true 209 | dev: true 210 | optional: true 211 | 212 | /@esbuild/win32-ia32@0.16.17: 213 | resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==} 214 | engines: {node: '>=12'} 215 | cpu: [ia32] 216 | os: [win32] 217 | requiresBuild: true 218 | dev: true 219 | optional: true 220 | 221 | /@esbuild/win32-x64@0.16.17: 222 | resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==} 223 | engines: {node: '>=12'} 224 | cpu: [x64] 225 | os: [win32] 226 | requiresBuild: true 227 | dev: true 228 | optional: true 229 | 230 | /@jridgewell/resolve-uri@3.1.0: 231 | resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} 232 | engines: {node: '>=6.0.0'} 233 | dev: false 234 | 235 | /@jridgewell/sourcemap-codec@1.4.14: 236 | resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} 237 | dev: false 238 | 239 | /@jridgewell/trace-mapping@0.3.9: 240 | resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} 241 | dependencies: 242 | '@jridgewell/resolve-uri': 3.1.0 243 | '@jridgewell/sourcemap-codec': 1.4.14 244 | dev: false 245 | 246 | /@tsconfig/node10@1.0.9: 247 | resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} 248 | dev: false 249 | 250 | /@tsconfig/node12@1.0.11: 251 | resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} 252 | dev: false 253 | 254 | /@tsconfig/node14@1.0.3: 255 | resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} 256 | dev: false 257 | 258 | /@tsconfig/node16@1.0.3: 259 | resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} 260 | dev: false 261 | 262 | /@types/chai-subset@1.3.3: 263 | resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} 264 | dependencies: 265 | '@types/chai': 4.3.4 266 | dev: true 267 | 268 | /@types/chai@4.3.4: 269 | resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} 270 | dev: true 271 | 272 | /@types/node@18.11.18: 273 | resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} 274 | 275 | /acorn-walk@8.2.0: 276 | resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} 277 | engines: {node: '>=0.4.0'} 278 | 279 | /acorn@8.8.1: 280 | resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} 281 | engines: {node: '>=0.4.0'} 282 | hasBin: true 283 | 284 | /arg@4.1.3: 285 | resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} 286 | dev: false 287 | 288 | /assertion-error@1.1.0: 289 | resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} 290 | dev: true 291 | 292 | /buffer-from@1.1.2: 293 | resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} 294 | dev: true 295 | 296 | /cac@6.7.14: 297 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 298 | engines: {node: '>=8'} 299 | dev: true 300 | 301 | /chai@4.3.7: 302 | resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} 303 | engines: {node: '>=4'} 304 | dependencies: 305 | assertion-error: 1.1.0 306 | check-error: 1.0.2 307 | deep-eql: 4.1.3 308 | get-func-name: 2.0.0 309 | loupe: 2.3.6 310 | pathval: 1.1.1 311 | type-detect: 4.0.8 312 | dev: true 313 | 314 | /check-error@1.0.2: 315 | resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} 316 | dev: true 317 | 318 | /create-require@1.1.1: 319 | resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} 320 | dev: false 321 | 322 | /debug@4.3.4: 323 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 324 | engines: {node: '>=6.0'} 325 | peerDependencies: 326 | supports-color: '*' 327 | peerDependenciesMeta: 328 | supports-color: 329 | optional: true 330 | dependencies: 331 | ms: 2.1.2 332 | dev: true 333 | 334 | /deep-eql@4.1.3: 335 | resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} 336 | engines: {node: '>=6'} 337 | dependencies: 338 | type-detect: 4.0.8 339 | dev: true 340 | 341 | /diff@4.0.2: 342 | resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} 343 | engines: {node: '>=0.3.1'} 344 | dev: false 345 | 346 | /esbuild@0.16.17: 347 | resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==} 348 | engines: {node: '>=12'} 349 | hasBin: true 350 | requiresBuild: true 351 | optionalDependencies: 352 | '@esbuild/android-arm': 0.16.17 353 | '@esbuild/android-arm64': 0.16.17 354 | '@esbuild/android-x64': 0.16.17 355 | '@esbuild/darwin-arm64': 0.16.17 356 | '@esbuild/darwin-x64': 0.16.17 357 | '@esbuild/freebsd-arm64': 0.16.17 358 | '@esbuild/freebsd-x64': 0.16.17 359 | '@esbuild/linux-arm': 0.16.17 360 | '@esbuild/linux-arm64': 0.16.17 361 | '@esbuild/linux-ia32': 0.16.17 362 | '@esbuild/linux-loong64': 0.16.17 363 | '@esbuild/linux-mips64el': 0.16.17 364 | '@esbuild/linux-ppc64': 0.16.17 365 | '@esbuild/linux-riscv64': 0.16.17 366 | '@esbuild/linux-s390x': 0.16.17 367 | '@esbuild/linux-x64': 0.16.17 368 | '@esbuild/netbsd-x64': 0.16.17 369 | '@esbuild/openbsd-x64': 0.16.17 370 | '@esbuild/sunos-x64': 0.16.17 371 | '@esbuild/win32-arm64': 0.16.17 372 | '@esbuild/win32-ia32': 0.16.17 373 | '@esbuild/win32-x64': 0.16.17 374 | dev: true 375 | 376 | /fsevents@2.3.2: 377 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 378 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 379 | os: [darwin] 380 | requiresBuild: true 381 | dev: true 382 | optional: true 383 | 384 | /function-bind@1.1.1: 385 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} 386 | dev: true 387 | 388 | /get-func-name@2.0.0: 389 | resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} 390 | dev: true 391 | 392 | /has@1.0.3: 393 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} 394 | engines: {node: '>= 0.4.0'} 395 | dependencies: 396 | function-bind: 1.1.1 397 | dev: true 398 | 399 | /is-core-module@2.11.0: 400 | resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} 401 | dependencies: 402 | has: 1.0.3 403 | dev: true 404 | 405 | /jsonc-parser@3.2.0: 406 | resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} 407 | dev: true 408 | 409 | /local-pkg@0.4.2: 410 | resolution: {integrity: sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==} 411 | engines: {node: '>=14'} 412 | dev: true 413 | 414 | /loupe@2.3.6: 415 | resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} 416 | dependencies: 417 | get-func-name: 2.0.0 418 | dev: true 419 | 420 | /make-error@1.3.6: 421 | resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} 422 | dev: false 423 | 424 | /mlly@1.1.0: 425 | resolution: {integrity: sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==} 426 | dependencies: 427 | acorn: 8.8.1 428 | pathe: 1.0.0 429 | pkg-types: 1.0.1 430 | ufo: 1.0.1 431 | dev: true 432 | 433 | /ms@2.1.2: 434 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 435 | dev: true 436 | 437 | /nanoid@3.3.4: 438 | resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} 439 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 440 | hasBin: true 441 | dev: true 442 | 443 | /path-parse@1.0.7: 444 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 445 | dev: true 446 | 447 | /pathe@0.2.0: 448 | resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==} 449 | dev: true 450 | 451 | /pathe@1.0.0: 452 | resolution: {integrity: sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg==} 453 | dev: true 454 | 455 | /pathval@1.1.1: 456 | resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} 457 | dev: true 458 | 459 | /picocolors@1.0.0: 460 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 461 | dev: true 462 | 463 | /pkg-types@1.0.1: 464 | resolution: {integrity: sha512-jHv9HB+Ho7dj6ItwppRDDl0iZRYBD0jsakHXtFgoLr+cHSF6xC+QL54sJmWxyGxOLYSHm0afhXhXcQDQqH9z8g==} 465 | dependencies: 466 | jsonc-parser: 3.2.0 467 | mlly: 1.1.0 468 | pathe: 1.0.0 469 | dev: true 470 | 471 | /postcss@8.4.21: 472 | resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} 473 | engines: {node: ^10 || ^12 || >=14} 474 | dependencies: 475 | nanoid: 3.3.4 476 | picocolors: 1.0.0 477 | source-map-js: 1.0.2 478 | dev: true 479 | 480 | /resolve@1.22.1: 481 | resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} 482 | hasBin: true 483 | dependencies: 484 | is-core-module: 2.11.0 485 | path-parse: 1.0.7 486 | supports-preserve-symlinks-flag: 1.0.0 487 | dev: true 488 | 489 | /rollup@3.10.0: 490 | resolution: {integrity: sha512-JmRYz44NjC1MjVF2VKxc0M1a97vn+cDxeqWmnwyAF4FvpjK8YFdHpaqvQB+3IxCvX05vJxKZkoMDU8TShhmJVA==} 491 | engines: {node: '>=14.18.0', npm: '>=8.0.0'} 492 | hasBin: true 493 | optionalDependencies: 494 | fsevents: 2.3.2 495 | dev: true 496 | 497 | /siginfo@2.0.0: 498 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 499 | dev: true 500 | 501 | /source-map-js@1.0.2: 502 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 503 | engines: {node: '>=0.10.0'} 504 | dev: true 505 | 506 | /source-map-support@0.5.21: 507 | resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} 508 | dependencies: 509 | buffer-from: 1.1.2 510 | source-map: 0.6.1 511 | dev: true 512 | 513 | /source-map@0.6.1: 514 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 515 | engines: {node: '>=0.10.0'} 516 | dev: true 517 | 518 | /stackback@0.0.2: 519 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 520 | dev: true 521 | 522 | /strip-literal@1.0.0: 523 | resolution: {integrity: sha512-5o4LsH1lzBzO9UFH63AJ2ad2/S2AVx6NtjOcaz+VTT2h1RiRvbipW72z8M/lxEhcPHDBQwpDrnTF7sXy/7OwCQ==} 524 | dependencies: 525 | acorn: 8.8.1 526 | dev: true 527 | 528 | /supports-preserve-symlinks-flag@1.0.0: 529 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 530 | engines: {node: '>= 0.4'} 531 | dev: true 532 | 533 | /tinybench@2.3.1: 534 | resolution: {integrity: sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==} 535 | dev: true 536 | 537 | /tinypool@0.3.0: 538 | resolution: {integrity: sha512-NX5KeqHOBZU6Bc0xj9Vr5Szbb1j8tUHIeD18s41aDJaPeC5QTdEhK0SpdpUrZlj2nv5cctNcSjaKNanXlfcVEQ==} 539 | engines: {node: '>=14.0.0'} 540 | dev: true 541 | 542 | /tinyspy@1.0.2: 543 | resolution: {integrity: sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==} 544 | engines: {node: '>=14.0.0'} 545 | dev: true 546 | 547 | /ts-node@10.9.1(@types/node@18.11.18)(typescript@4.9.4): 548 | resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} 549 | hasBin: true 550 | peerDependencies: 551 | '@swc/core': '>=1.2.50' 552 | '@swc/wasm': '>=1.2.50' 553 | '@types/node': '*' 554 | typescript: '>=2.7' 555 | peerDependenciesMeta: 556 | '@swc/core': 557 | optional: true 558 | '@swc/wasm': 559 | optional: true 560 | dependencies: 561 | '@cspotcode/source-map-support': 0.8.1 562 | '@tsconfig/node10': 1.0.9 563 | '@tsconfig/node12': 1.0.11 564 | '@tsconfig/node14': 1.0.3 565 | '@tsconfig/node16': 1.0.3 566 | '@types/node': 18.11.18 567 | acorn: 8.8.1 568 | acorn-walk: 8.2.0 569 | arg: 4.1.3 570 | create-require: 1.1.1 571 | diff: 4.0.2 572 | make-error: 1.3.6 573 | typescript: 4.9.4 574 | v8-compile-cache-lib: 3.0.1 575 | yn: 3.1.1 576 | dev: false 577 | 578 | /type-detect@4.0.8: 579 | resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} 580 | engines: {node: '>=4'} 581 | dev: true 582 | 583 | /typescript@4.9.4: 584 | resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} 585 | engines: {node: '>=4.2.0'} 586 | hasBin: true 587 | dev: false 588 | 589 | /ufo@1.0.1: 590 | resolution: {integrity: sha512-boAm74ubXHY7KJQZLlXrtMz52qFvpsbOxDcZOnw/Wf+LS4Mmyu7JxmzD4tDLtUQtmZECypJ0FrCz4QIe6dvKRA==} 591 | dev: true 592 | 593 | /v8-compile-cache-lib@3.0.1: 594 | resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} 595 | dev: false 596 | 597 | /vite-node@0.27.1(@types/node@18.11.18): 598 | resolution: {integrity: sha512-d6+ue/3NzsfndWaPbYh/bFkHbmAWfDXI4B874zRx+WREnG6CUHUbBC8lKaRYZjeR6gCPN5m1aVNNRXBYICA9XA==} 599 | engines: {node: '>=v14.16.0'} 600 | hasBin: true 601 | dependencies: 602 | cac: 6.7.14 603 | debug: 4.3.4 604 | mlly: 1.1.0 605 | pathe: 0.2.0 606 | picocolors: 1.0.0 607 | source-map: 0.6.1 608 | source-map-support: 0.5.21 609 | vite: 4.0.4(@types/node@18.11.18) 610 | transitivePeerDependencies: 611 | - '@types/node' 612 | - less 613 | - sass 614 | - stylus 615 | - sugarss 616 | - supports-color 617 | - terser 618 | dev: true 619 | 620 | /vite@4.0.4(@types/node@18.11.18): 621 | resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} 622 | engines: {node: ^14.18.0 || >=16.0.0} 623 | hasBin: true 624 | peerDependencies: 625 | '@types/node': '>= 14' 626 | less: '*' 627 | sass: '*' 628 | stylus: '*' 629 | sugarss: '*' 630 | terser: ^5.4.0 631 | peerDependenciesMeta: 632 | '@types/node': 633 | optional: true 634 | less: 635 | optional: true 636 | sass: 637 | optional: true 638 | stylus: 639 | optional: true 640 | sugarss: 641 | optional: true 642 | terser: 643 | optional: true 644 | dependencies: 645 | '@types/node': 18.11.18 646 | esbuild: 0.16.17 647 | postcss: 8.4.21 648 | resolve: 1.22.1 649 | rollup: 3.10.0 650 | optionalDependencies: 651 | fsevents: 2.3.2 652 | dev: true 653 | 654 | /vitest@0.27.1: 655 | resolution: {integrity: sha512-1sIpQ1DVFTEn7c1ici1XHcVfdU4nKiBmPtPAtGKJJJLuJjojTv/OHGgcf69P57alM4ty8V4NMv+7Yoi5Cxqx9g==} 656 | engines: {node: '>=v14.16.0'} 657 | hasBin: true 658 | peerDependencies: 659 | '@edge-runtime/vm': '*' 660 | '@vitest/browser': '*' 661 | '@vitest/ui': '*' 662 | happy-dom: '*' 663 | jsdom: '*' 664 | peerDependenciesMeta: 665 | '@edge-runtime/vm': 666 | optional: true 667 | '@vitest/browser': 668 | optional: true 669 | '@vitest/ui': 670 | optional: true 671 | happy-dom: 672 | optional: true 673 | jsdom: 674 | optional: true 675 | dependencies: 676 | '@types/chai': 4.3.4 677 | '@types/chai-subset': 1.3.3 678 | '@types/node': 18.11.18 679 | acorn: 8.8.1 680 | acorn-walk: 8.2.0 681 | cac: 6.7.14 682 | chai: 4.3.7 683 | debug: 4.3.4 684 | local-pkg: 0.4.2 685 | picocolors: 1.0.0 686 | source-map: 0.6.1 687 | strip-literal: 1.0.0 688 | tinybench: 2.3.1 689 | tinypool: 0.3.0 690 | tinyspy: 1.0.2 691 | vite: 4.0.4(@types/node@18.11.18) 692 | vite-node: 0.27.1(@types/node@18.11.18) 693 | why-is-node-running: 2.2.2 694 | transitivePeerDependencies: 695 | - less 696 | - sass 697 | - stylus 698 | - sugarss 699 | - supports-color 700 | - terser 701 | dev: true 702 | 703 | /why-is-node-running@2.2.2: 704 | resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} 705 | engines: {node: '>=8'} 706 | hasBin: true 707 | dependencies: 708 | siginfo: 2.0.0 709 | stackback: 0.0.2 710 | dev: true 711 | 712 | /yn@3.1.1: 713 | resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} 714 | engines: {node: '>=6'} 715 | dev: false 716 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/account/account.ts: -------------------------------------------------------------------------------- 1 | export abstract class Account { 2 | private username: string; 3 | private password: string; 4 | 5 | constructor(username: string, password: string) { 6 | this.username = username; 7 | this.password = password; 8 | } 9 | 10 | public resetPassword(newPassword: string) { 11 | this.password = newPassword; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/account/admin.ts: -------------------------------------------------------------------------------- 1 | import { EntryPanel } from "../entry.panel"; 2 | import { ExitPanel } from "../exit.panel"; 3 | import { ParkingSpot } from "../parking-spot/parking.spot"; 4 | import { ParkingFloor } from "../parking.floor"; 5 | import { ParkingLot } from "../parking.lot"; 6 | import { Account } from "./account"; 7 | 8 | export default class Admin extends Account { 9 | constructor(username: string, password: string) { 10 | super(username, password); 11 | } 12 | 13 | public addParkingFloor(parkingFloor: ParkingFloor): boolean { 14 | const floor = ParkingLot.getInstance() 15 | .getListOfParkingFloor() 16 | .find( 17 | (pf) => pf.getParkingFloorID() === parkingFloor.getParkingFloorID() 18 | ); 19 | if (floor === undefined) { 20 | ParkingLot.getInstance().getListOfParkingFloor().push(parkingFloor); 21 | return true; 22 | } 23 | return false; 24 | } 25 | 26 | public addParkingSpot(parkingFloorID: string, parkingSpot: ParkingSpot) { 27 | const floor = ParkingLot.getInstance() 28 | .getListOfParkingFloor() 29 | .find((pf) => pf.getParkingFloorID() === parkingFloorID); 30 | 31 | if (floor === undefined) { 32 | throw new Error("Invalid floor"); 33 | } 34 | 35 | const spot = floor 36 | .getListOfParkingSpots() 37 | .get(parkingSpot.getParkingSpotType()) 38 | ?.find( 39 | (spot) => spot.getParkingSpotID() === parkingSpot.getParkingSpotID() 40 | ); 41 | if (spot !== undefined) { 42 | return; 43 | } 44 | floor 45 | .getListOfParkingSpots() 46 | .get(parkingSpot.getParkingSpotType()) 47 | ?.push(parkingSpot); 48 | } 49 | 50 | public addEntrancePanel(entryPanel: EntryPanel) { 51 | const listOfEntryPanel = ParkingLot.getInstance().getListOfEntryPanel(); 52 | const result = listOfEntryPanel.find( 53 | (panel) => panel.getEntryPanelID() === entryPanel.getEntryPanelID() 54 | ); 55 | 56 | if (result === undefined) { 57 | listOfEntryPanel.push(entryPanel); 58 | return true; 59 | } 60 | return false; 61 | } 62 | 63 | public addExitPanel(exitPanel: ExitPanel) { 64 | const listOfExistPanel = ParkingLot.getInstance().getListOfExitPanel(); 65 | const result = listOfExistPanel.find( 66 | (panel) => panel.getExitPanelID() === exitPanel.getExitPanelID() 67 | ); 68 | 69 | if (result === undefined) { 70 | listOfExistPanel.push(exitPanel); 71 | return true; 72 | } 73 | return false; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/account/parking.attendant.ts: -------------------------------------------------------------------------------- 1 | import { Account } from "./account"; 2 | 3 | export class ParkingAttendant extends Account { 4 | constructor(username: string, password: string) { 5 | super(username, password); 6 | } 7 | 8 | public processTicket(): boolean { 9 | return false; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/entry.panel.ts: -------------------------------------------------------------------------------- 1 | import { ParkingLot } from "./parking.lot"; 2 | import { ParkingTicket } from "./parking.ticket"; 3 | import { Vehicle } from "./vehicles/vehicle"; 4 | 5 | export class EntryPanel { 6 | private entryPanelID: string; 7 | 8 | constructor(entryPanelID: string) { 9 | this.entryPanelID = entryPanelID; 10 | } 11 | 12 | public getEntryPanelID() { 13 | return this.entryPanelID; 14 | } 15 | 16 | public getParkingTicket(vehicle: Vehicle) { 17 | const parkingFloor = ParkingLot.getInstance() 18 | .getListOfParkingFloor() 19 | .find((floor) => floor.canPark(vehicle)); 20 | 21 | if (parkingFloor === undefined) { 22 | throw new Error( 23 | `Parking is unsupported for this ${vehicle.getVehicleType()} type` 24 | ); 25 | } 26 | 27 | const spot = parkingFloor.getAvailableSpot(vehicle); 28 | if (spot === undefined) { 29 | throw new Error(`No spots are available for ${vehicle.getVehicleType()}`); 30 | } 31 | 32 | const ticket = this.generateParkingTicket( 33 | vehicle, 34 | parkingFloor.getParkingFloorID(), 35 | spot.getParkingSpotID() 36 | ); 37 | 38 | spot.assignVehicleToSpot(vehicle); 39 | return ticket; 40 | } 41 | 42 | private generateParkingTicket( 43 | vehicle: Vehicle, 44 | parkingFloorID: string, 45 | parkingSpotID: string 46 | ) { 47 | return new ParkingTicket( 48 | vehicle.getVehicleType(), 49 | vehicle.getVehicleRegisterNumber(), 50 | parkingFloorID, 51 | parkingSpotID 52 | ).setStartTime(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/exit.panel.ts: -------------------------------------------------------------------------------- 1 | import { HourlyCost } from "./hourly.cost"; 2 | import { ParkingSpotType } from "./parking-spot/parking.spot.type"; 3 | import { ParkingLot } from "./parking.lot"; 4 | import { ParkingTicket } from "./parking.ticket"; 5 | export class ExitPanel { 6 | private exitPanelID: string; 7 | 8 | constructor(entryPanelID: string) { 9 | this.exitPanelID = entryPanelID; 10 | } 11 | 12 | public getExitPanelID() { 13 | return this.exitPanelID; 14 | } 15 | 16 | public checkout(parkingTicket: ParkingTicket) { 17 | const parkingSpotID = parkingTicket.getParkingSpotID(); 18 | const totalDurationInHours = this.calculateDurationInHours(parkingTicket); 19 | const vacatedSpot = 20 | ParkingLot.getInstance().vacateParkingSpot(parkingSpotID); 21 | 22 | if (vacatedSpot === undefined) { 23 | throw new Error("Unable to find the Parking Spot for the given ID"); 24 | } 25 | 26 | const totalAmount = this.calculatePrice( 27 | vacatedSpot.getParkingSpotType(), 28 | totalDurationInHours 29 | ); 30 | 31 | parkingTicket.setAmount(totalAmount); 32 | return parkingTicket; 33 | } 34 | 35 | private calculatePrice(parkingSpotType: ParkingSpotType, duration: number) { 36 | const cost = new HourlyCost().getCost(parkingSpotType); 37 | return duration === 0 ? 1 * cost : duration * cost; 38 | } 39 | 40 | private calculateDurationInHours(parkingTicket: ParkingTicket) { 41 | const endTime = parkingTicket.setEndTime().getEndTime(); 42 | return Math.round( 43 | Math.abs(parkingTicket.getStartTime().valueOf() - endTime.valueOf()) / 44 | 36e5 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/hourly.cost.ts: -------------------------------------------------------------------------------- 1 | import { ParkingSpotType } from "./parking-spot/parking.spot.type"; 2 | 3 | export class HourlyCost { 4 | private hourlyCosts: Map = new Map(); 5 | 6 | constructor() { 7 | this.hourlyCosts.set(ParkingSpotType.Compact, 25); 8 | this.hourlyCosts.set(ParkingSpotType.Disabled, 15); 9 | this.hourlyCosts.set(ParkingSpotType.ElectricCar, 35); 10 | this.hourlyCosts.set(ParkingSpotType.Large, 45); 11 | this.hourlyCosts.set(ParkingSpotType.Motorcycle, 15); 12 | } 13 | 14 | public getCost(parkingSpotType: ParkingSpotType) { 15 | if (this.hourlyCosts.has(parkingSpotType)) { 16 | return this.hourlyCosts.get(parkingSpotType)!; 17 | } else { 18 | throw new Error( 19 | `Hourly cost is not associated with ${parkingSpotType} type` 20 | ); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/parking-spot/car.spot.ts: -------------------------------------------------------------------------------- 1 | import { ParkingSpot } from "./parking.spot"; 2 | import { ParkingSpotType } from "./parking.spot.type"; 3 | 4 | export class CarSpot extends ParkingSpot { 5 | constructor(id: string) { 6 | super(id, ParkingSpotType.Compact); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/parking-spot/disabled.spot.ts: -------------------------------------------------------------------------------- 1 | import { ParkingSpot } from "./parking.spot"; 2 | import { ParkingSpotType } from "./parking.spot.type"; 3 | 4 | export class DisabledSpot extends ParkingSpot { 5 | constructor(id: string) { 6 | super(id, ParkingSpotType.Disabled); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/parking-spot/electric.car.spot.ts: -------------------------------------------------------------------------------- 1 | import { ParkingSpot } from "./parking.spot"; 2 | import { ParkingSpotType } from "./parking.spot.type"; 3 | 4 | export class ElectricCarSpot extends ParkingSpot { 5 | constructor(id: string) { 6 | super(id, ParkingSpotType.ElectricCar); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/parking-spot/motorcycle.spot.ts: -------------------------------------------------------------------------------- 1 | import { ParkingSpot } from "./parking.spot"; 2 | import { ParkingSpotType } from "./parking.spot.type"; 3 | 4 | export class MotorcycleSpot extends ParkingSpot { 5 | constructor(id: string) { 6 | super(id, ParkingSpotType.Motorcycle); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/parking-spot/parking.spot.ts: -------------------------------------------------------------------------------- 1 | import { ParkingSpotType } from "./parking.spot.type"; 2 | import { Vehicle } from "../vehicles/vehicle"; 3 | 4 | export abstract class ParkingSpot { 5 | private parkingSpotID: string; 6 | private isSpotAvailable: boolean; 7 | private vehicle: Vehicle | null; 8 | private parkingSpotType: ParkingSpotType; 9 | 10 | constructor(parkingSpotId: string, parkingSpotType: ParkingSpotType) { 11 | this.isSpotAvailable = true; 12 | this.parkingSpotID = parkingSpotId; 13 | this.vehicle = null; 14 | this.parkingSpotType = parkingSpotType; 15 | } 16 | 17 | public isSpotFree(): boolean { 18 | return this.isSpotAvailable; 19 | } 20 | 21 | public getParkingSpotType() { 22 | return this.parkingSpotType; 23 | } 24 | 25 | public getParkingSpotID() { 26 | return this.parkingSpotID; 27 | } 28 | 29 | public getVehicleDetails() { 30 | return this.vehicle; 31 | } 32 | 33 | public assignVehicleToSpot(vehicle: Vehicle) { 34 | if (!this.isSpotAvailable) { 35 | throw new Error(`No spots are available for ${vehicle.getVehicleType()}`); 36 | } 37 | this.vehicle = vehicle; 38 | this.isSpotAvailable = false; 39 | } 40 | 41 | public vacateVehicleFromSpot() { 42 | this.vehicle = null; 43 | this.isSpotAvailable = true; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/parking-spot/parking.spot.type.ts: -------------------------------------------------------------------------------- 1 | export enum ParkingSpotType { 2 | Disabled = "Disabled", 3 | Compact = "Compact", 4 | Large = "Large", 5 | Motorcycle = "Motorcycle", 6 | ElectricCar = "ElectricCar", 7 | } 8 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/parking.display.board.ts: -------------------------------------------------------------------------------- 1 | import { ParkingSpotType } from "./parking-spot/parking.spot.type"; 2 | 3 | export class DisplayBoard { 4 | public displayMessage(parkingSpotType: ParkingSpotType, freeSpots: number) { 5 | let message = `${parkingSpotType} spots free: ${freeSpots}`; 6 | console.log(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/parking.floor.ts: -------------------------------------------------------------------------------- 1 | import { DisplayBoard } from "./parking.display.board"; 2 | import { ParkingSpot } from "./parking-spot/parking.spot"; 3 | import { ParkingSpotType } from "./parking-spot/parking.spot.type"; 4 | import { Vehicle } from "./vehicles/vehicle"; 5 | import { VehicleType } from "./vehicles/vehicle.type"; 6 | 7 | export class ParkingFloor { 8 | private parkingSpots: Map> = new Map(); 9 | private parkingFloorID: string; 10 | private displayBoard: DisplayBoard; 11 | 12 | constructor(parkingFloorID: string) { 13 | this.parkingSpots.set(ParkingSpotType.Compact, new Array()); 14 | this.parkingSpots.set(ParkingSpotType.Motorcycle, new Array()); 15 | this.parkingSpots.set(ParkingSpotType.Large, new Array()); 16 | this.parkingSpots.set( 17 | ParkingSpotType.ElectricCar, 18 | new Array() 19 | ); 20 | this.parkingFloorID = parkingFloorID; 21 | this.displayBoard = new DisplayBoard(); 22 | } 23 | 24 | public getParkingFloorID() { 25 | return this.parkingFloorID; 26 | } 27 | 28 | public getListOfParkingSpots() { 29 | return this.parkingSpots; 30 | } 31 | 32 | public getAvailableSpot(vehicle: Vehicle) { 33 | return this.parkingSpots 34 | .get(this.getSpotTypeForVehicle(vehicle.getVehicleType())) 35 | ?.find((spot) => spot.isSpotFree()); 36 | } 37 | 38 | public showDisplayBoard() { 39 | for (let spotType of this.parkingSpots.keys()) { 40 | const freeSpots = 41 | this.parkingSpots.get(spotType)?.filter((spot) => spot.isSpotFree()) ?? 42 | []; 43 | this.displayBoard.displayMessage(spotType, freeSpots.length); 44 | } 45 | } 46 | 47 | private getSpotTypeForVehicle(vehicleType: VehicleType): ParkingSpotType { 48 | switch (vehicleType) { 49 | case VehicleType.Car: 50 | return ParkingSpotType.Compact; 51 | case VehicleType.Motorcycle: 52 | return ParkingSpotType.Motorcycle; 53 | default: 54 | return ParkingSpotType.Large; 55 | } 56 | } 57 | 58 | public getInUseSpotID(vehicleType: VehicleType) { 59 | return this.parkingSpots 60 | .get(this.getSpotTypeForVehicle(vehicleType)) 61 | ?.filter((spot) => !spot.isSpotFree) 62 | .map((occSpot) => occSpot); 63 | } 64 | 65 | public canPark(vehicle: Vehicle) { 66 | const parkingSpotType = this.getSpotTypeForVehicle( 67 | vehicle.getVehicleType() 68 | ); 69 | 70 | if (this.parkingSpots.get(parkingSpotType)?.length) { 71 | return true; 72 | } 73 | 74 | return false; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/parking.lot.ts: -------------------------------------------------------------------------------- 1 | import { EntryPanel } from "./entry.panel"; 2 | import { ExitPanel } from "./exit.panel"; 3 | import { ParkingFloor } from "./parking.floor"; 4 | 5 | export class ParkingLot { 6 | private static instance: ParkingLot; 7 | private parkingFloor: Array; 8 | private entryPanels: Array; 9 | private exitPanels: Array; 10 | 11 | private constructor() { 12 | this.parkingFloor = new Array(); 13 | this.entryPanels = new Array(); 14 | this.exitPanels = new Array(); 15 | } 16 | 17 | public static getInstance() { 18 | if (!ParkingLot.instance) { 19 | return (this.instance = new ParkingLot()); 20 | } 21 | return this.instance; 22 | } 23 | 24 | public vacateParkingSpot(parkingSpotID: string) { 25 | for (let floor of this.parkingFloor) { 26 | for (let spots of floor.getListOfParkingSpots().values()) { 27 | const vacatedSpot = spots.find((spot) => { 28 | if (spot.getParkingSpotID() === parkingSpotID) { 29 | spot.vacateVehicleFromSpot(); 30 | return spot; 31 | } 32 | }); 33 | return vacatedSpot; 34 | } 35 | } 36 | } 37 | 38 | public getListOfParkingFloor() { 39 | return this.parkingFloor; 40 | } 41 | 42 | public getListOfEntryPanel() { 43 | return this.entryPanels; 44 | } 45 | 46 | public getListOfExitPanel() { 47 | return this.exitPanels; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/parking.ticket.ts: -------------------------------------------------------------------------------- 1 | import crypto from "node:crypto"; 2 | import { VehicleType } from "./vehicles/vehicle.type"; 3 | 4 | export class ParkingTicket { 5 | private parkingTicketID: string; 6 | private vehicleType: VehicleType; 7 | private vehicleRegisterNumber: string; 8 | private parkingSpotID: string; 9 | private parkingFloorID: string; 10 | private startTime!: Date; 11 | private endTime!: Date; 12 | private amount!: number; 13 | 14 | constructor( 15 | vehicleType: VehicleType, 16 | vehicleRegisterNumber: string, 17 | parkingFloorID: string, 18 | parkingSpotID: string 19 | ) { 20 | this.vehicleType = vehicleType; 21 | this.vehicleRegisterNumber = vehicleRegisterNumber; 22 | this.parkingFloorID = parkingFloorID; 23 | this.parkingSpotID = parkingSpotID; 24 | this.parkingTicketID = crypto.randomUUID(); 25 | } 26 | 27 | public setStartTime(currentDateTime: Date = new Date()) { 28 | this.startTime = currentDateTime; 29 | return this; 30 | } 31 | 32 | public getStartTime() { 33 | return this.startTime; 34 | } 35 | 36 | public getEndTime() { 37 | return this.endTime; 38 | } 39 | 40 | public getAmount() { 41 | return this.amount; 42 | } 43 | 44 | public setEndTime(currentDateTime: Date = new Date()) { 45 | this.endTime = currentDateTime; 46 | return this; 47 | } 48 | 49 | public setAmount(amount: number) { 50 | this.amount = amount; 51 | return this; 52 | } 53 | 54 | public getVehicleType() { 55 | return this.vehicleType; 56 | } 57 | 58 | public getParkingSpotID() { 59 | return this.parkingSpotID; 60 | } 61 | 62 | public getParkingFloorID() { 63 | return this.parkingFloorID; 64 | } 65 | 66 | public getVehicleRegisterNumber() { 67 | return this.vehicleRegisterNumber; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/vehicles/car.ts: -------------------------------------------------------------------------------- 1 | import { Vehicle } from "./vehicle"; 2 | import { VehicleType } from "./vehicle.type"; 3 | 4 | export class Car extends Vehicle { 5 | constructor(vehicleRegisterNumber: string) { 6 | super(vehicleRegisterNumber, VehicleType.Car); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/vehicles/electric.ts: -------------------------------------------------------------------------------- 1 | import { Vehicle } from "./vehicle"; 2 | import { VehicleType } from "./vehicle.type"; 3 | 4 | export class ElectricCar extends Vehicle { 5 | constructor(vehicleRegisterNumber: string) { 6 | super(vehicleRegisterNumber, VehicleType.ElectricCar); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/vehicles/motor.cycle.ts: -------------------------------------------------------------------------------- 1 | import { Vehicle } from "./vehicle"; 2 | import { VehicleType } from "./vehicle.type"; 3 | 4 | export class MotorCycle extends Vehicle { 5 | constructor(vehicleRegisterNumber: string) { 6 | super(vehicleRegisterNumber, VehicleType.Motorcycle); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/vehicles/truck.ts: -------------------------------------------------------------------------------- 1 | import { Vehicle } from "./vehicle"; 2 | import { VehicleType } from "./vehicle.type"; 3 | 4 | export class Truck extends Vehicle { 5 | constructor(vehicleRegisterNumber: string) { 6 | super(vehicleRegisterNumber, VehicleType.Truck); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/vehicles/vehicle.ts: -------------------------------------------------------------------------------- 1 | import { VehicleType } from "./vehicle.type"; 2 | 3 | export abstract class Vehicle { 4 | private registerNumber: string; 5 | private type: VehicleType; 6 | 7 | constructor(vehicleRegisterNumber: string, vehicleType: VehicleType) { 8 | this.registerNumber = vehicleRegisterNumber; 9 | this.type = vehicleType; 10 | } 11 | 12 | public getVehicleType() { 13 | return this.type; 14 | } 15 | 16 | public getVehicleRegisterNumber() { 17 | return this.registerNumber; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/src/vehicles/vehicle.type.ts: -------------------------------------------------------------------------------- 1 | export enum VehicleType { 2 | Car = "Car", 3 | Motorcycle = "Motorcycle", 4 | Truck = "Truck", 5 | ElectricCar = "ElectricCar", 6 | Van = "Van", 7 | } 8 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/test/entry.panel.negative.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import Admin from "../src/account/admin"; 3 | import { EntryPanel } from "../src/entry.panel"; 4 | import { MotorcycleSpot } from "../src/parking-spot/motorcycle.spot"; 5 | import { ParkingFloor } from "../src/parking.floor"; 6 | import { Car } from "../src/vehicles/car"; 7 | import { MotorCycle } from "../src/vehicles/motor.cycle"; 8 | 9 | // TODO: Keep tests related to entry panel in a single file once test level isolation implemented 10 | describe("Entry panel negative tests cases", () => { 11 | it("Verify its throws an error when parking lot doesn't have spot for that vehicle type", () => { 12 | const car = new Car("TN112345"); 13 | const entryPanel = new EntryPanel("Panel001"); 14 | const floor = new ParkingFloor("F1001"); 15 | const bikeSpot = new MotorcycleSpot("BK1001"); 16 | 17 | const admin = new Admin("admin", "12345"); 18 | admin.addParkingFloor(floor); 19 | 20 | floor 21 | .getListOfParkingSpots() 22 | .get(bikeSpot.getParkingSpotType()) 23 | ?.push(bikeSpot); 24 | 25 | expect(() => entryPanel.getParkingTicket(car)).toThrowError( 26 | "Parking is unsupported for this Car type" 27 | ); 28 | }); 29 | 30 | it("Verify its throws an error when parking floor doesn't have spot", () => { 31 | const bike = new MotorCycle("TN112345"); 32 | const entryPanel = new EntryPanel("Panel001"); 33 | const floor = new ParkingFloor("F1001"); 34 | const bikeSpot = new MotorcycleSpot("BK1001"); 35 | 36 | const admin = new Admin("admin", "12345"); 37 | admin.addParkingFloor(floor); 38 | 39 | floor 40 | .getListOfParkingSpots() 41 | .get(bikeSpot.getParkingSpotType()) 42 | ?.push(bikeSpot); 43 | 44 | entryPanel.getParkingTicket(bike); 45 | 46 | expect(() => entryPanel.getParkingTicket(bike)).toThrowError( 47 | "No spots are available for Motorcycle" 48 | ); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/test/entry.panel.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import Admin from "../src/account/admin"; 3 | import { EntryPanel } from "../src/entry.panel"; 4 | import { CarSpot } from "../src/parking-spot/car.spot"; 5 | import { ParkingFloor } from "../src/parking.floor"; 6 | import { Car } from "../src/vehicles/car"; 7 | 8 | describe("Entry panel tests cases", () => { 9 | it("Verify able to generate parking ticket", () => { 10 | const car = new Car("TN112345"); 11 | const entryPanel = new EntryPanel("Panel001"); 12 | const firstFloor = new ParkingFloor("F001"); 13 | const carSpot = new CarSpot("PS1001"); 14 | 15 | const admin = new Admin("admin", "12345"); 16 | admin.addParkingFloor(firstFloor); 17 | 18 | firstFloor 19 | .getListOfParkingSpots() 20 | .get(carSpot.getParkingSpotType()) 21 | ?.push(carSpot); 22 | 23 | const ticket = entryPanel.getParkingTicket(car); 24 | 25 | const ticketExpected = { 26 | vehicleType: "Car", 27 | vehicleRegisterNumber: "TN112345", 28 | parkingSpotID: "PS1001", 29 | parkingFloorID: "F001", 30 | endTime: undefined, 31 | amount: undefined, 32 | }; 33 | expect(ticket).toMatchObject(ticketExpected); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/test/exit.panel.spec.ts: -------------------------------------------------------------------------------- 1 | import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; 2 | import Admin from "../src/account/admin"; 3 | import { EntryPanel } from "../src/entry.panel"; 4 | import { ExitPanel } from "../src/exit.panel"; 5 | import { CarSpot } from "../src/parking-spot/car.spot"; 6 | import { ParkingFloor } from "../src/parking.floor"; 7 | import { Car } from "../src/vehicles/car"; 8 | 9 | describe("Exit panel tests cases", () => { 10 | beforeEach(() => { 11 | vi.useFakeTimers(); 12 | }); 13 | 14 | afterEach(() => { 15 | vi.useRealTimers(); 16 | }); 17 | 18 | it("Verify check out process of the vehicle", () => { 19 | const car = new Car("TN112345"); 20 | const entryPanel = new EntryPanel("ENT001"); 21 | const exitPanel = new ExitPanel("EXT001"); 22 | const firstFloor = new ParkingFloor("F001"); 23 | const carSpot = new CarSpot("PS1001"); 24 | const startDate = new Date(2023, 0, 13, 10); 25 | 26 | vi.setSystemTime(startDate); 27 | 28 | const admin = new Admin("admin", "12345"); 29 | admin.addParkingFloor(firstFloor); 30 | firstFloor 31 | .getListOfParkingSpots() 32 | .get(carSpot.getParkingSpotType()) 33 | ?.push(carSpot); 34 | let ticket = entryPanel.getParkingTicket(car); 35 | 36 | vi.advanceTimersByTime(7200000); 37 | ticket = exitPanel.checkout(ticket); 38 | 39 | expect(ticket.getAmount()).toEqual(50); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/test/parking.floor.spec.ts: -------------------------------------------------------------------------------- 1 | import { beforeEach, describe, expect, expectTypeOf, it } from "vitest"; 2 | import { CarSpot } from "../src/parking-spot/car.spot"; 3 | import { ParkingSpotType } from "../src/parking-spot/parking.spot.type"; 4 | import { ParkingFloor } from "../src/parking.floor"; 5 | import { Car } from "../src/vehicles/car"; 6 | 7 | describe("Parking floor test cases", () => { 8 | let firstFloor: ParkingFloor; 9 | let secondFloor: ParkingFloor; 10 | 11 | beforeEach(() => { 12 | firstFloor = new ParkingFloor("F001"); 13 | secondFloor = new ParkingFloor("F002"); 14 | }); 15 | 16 | it("Verify the parking floor ID", () => { 17 | expect(firstFloor.getParkingFloorID()).toEqual("F001"); 18 | expect(secondFloor.getParkingFloorID()).toEqual("F002"); 19 | }); 20 | 21 | it("Verify available parking types on a floor", () => { 22 | const spotTypesOfFirstFloor = [ 23 | ...firstFloor.getListOfParkingSpots().keys(), 24 | ]; 25 | const spotTypesOfSecondFloor = [ 26 | ...secondFloor.getListOfParkingSpots().keys(), 27 | ]; 28 | 29 | const expectedSpotTypes = [ 30 | ParkingSpotType.Compact, 31 | ParkingSpotType.Motorcycle, 32 | ParkingSpotType.Large, 33 | ParkingSpotType.ElectricCar, 34 | ]; 35 | expect(spotTypesOfFirstFloor).toEqual(expectedSpotTypes); 36 | expect(spotTypesOfSecondFloor).toEqual(expectedSpotTypes); 37 | expectTypeOf(spotTypesOfFirstFloor).items.toEqualTypeOf(); 38 | expectTypeOf(spotTypesOfSecondFloor).items.toEqualTypeOf(); 39 | }); 40 | 41 | it("Verify initial size of the parking spot on the floor", () => { 42 | const spotTypesSizeOfFirstFloor = [ 43 | ...firstFloor.getListOfParkingSpots().values(), 44 | ].flatMap((spot) => spot.length); 45 | const spotTypesSizeOfSecondFloor = [ 46 | ...secondFloor.getListOfParkingSpots().values(), 47 | ].flatMap((spot) => spot.length); 48 | 49 | const expectedSpotTypesSize = [0, 0, 0, 0]; 50 | 51 | expect(spotTypesSizeOfFirstFloor).toEqual(expectedSpotTypesSize); 52 | expect(spotTypesSizeOfSecondFloor).toEqual(expectedSpotTypesSize); 53 | }); 54 | 55 | it("Verify able to add parking spots to a floor", () => { 56 | const carSpot = new CarSpot("PS1001"); 57 | firstFloor 58 | .getListOfParkingSpots() 59 | .get(carSpot.getParkingSpotType()) 60 | ?.push(carSpot); 61 | 62 | expect( 63 | firstFloor.getListOfParkingSpots().get(ParkingSpotType.Compact)?.length 64 | ).toEqual(1); 65 | }); 66 | 67 | it("Verify it returns undefined when spots are unavailable for a vehicle", () => { 68 | const car = new Car("TN10010"); 69 | const availableSpot = firstFloor.getAvailableSpot(car); 70 | 71 | expect(availableSpot).toBeUndefined(); 72 | }); 73 | 74 | it("Verify it returns available parking spot for a vehicle", () => { 75 | const car = new Car("TN10010"); 76 | const firstCarSpot = new CarSpot("PS1001"); 77 | const secondCarSpot = new CarSpot("PS1002"); 78 | 79 | const carParkingSpot = firstFloor 80 | .getListOfParkingSpots() 81 | .get(firstCarSpot.getParkingSpotType()); 82 | 83 | carParkingSpot?.push(firstCarSpot); 84 | carParkingSpot?.push(secondCarSpot); 85 | 86 | let availableSpot = firstFloor.getAvailableSpot(car); 87 | 88 | expect(availableSpot).toBeDefined(); 89 | expect(availableSpot).toEqual(firstCarSpot); 90 | expect(availableSpot?.getParkingSpotID()).toEqual("PS1001"); 91 | 92 | firstCarSpot.assignVehicleToSpot(car); 93 | availableSpot = firstFloor.getAvailableSpot(car); 94 | 95 | expect(availableSpot?.getParkingSpotID()).toEqual("PS1002"); 96 | }); 97 | 98 | it("Verify able to park the vehicle when spot is available", () => { 99 | const car = new Car("TN10010"); 100 | const carSpot = new CarSpot("PS1001"); 101 | firstFloor 102 | .getListOfParkingSpots() 103 | .get(carSpot.getParkingSpotType()) 104 | ?.push(carSpot); 105 | 106 | const isSpotAvailable = firstFloor.canPark(car); 107 | 108 | expect(isSpotAvailable).true; 109 | }); 110 | }); 111 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/test/parking.spot.spec.ts: -------------------------------------------------------------------------------- 1 | import { beforeAll, beforeEach, describe, expect, it } from "vitest"; 2 | import { CarSpot } from "../src/parking-spot/car.spot"; 3 | import { MotorcycleSpot } from "../src/parking-spot/motorcycle.spot"; 4 | import { ParkingSpot } from "../src/parking-spot/parking.spot"; 5 | import { ParkingSpotType } from "../src/parking-spot/parking.spot.type"; 6 | import { Car } from "../src/vehicles/car"; 7 | import { MotorCycle } from "../src/vehicles/motor.cycle"; 8 | import { Vehicle } from "../src/vehicles/vehicle"; 9 | import { VehicleType } from "../src/vehicles/vehicle.type"; 10 | 11 | describe("Parking spots test cases", () => { 12 | let carSpot: ParkingSpot; 13 | let bikeSpot: ParkingSpot; 14 | 15 | beforeEach(() => { 16 | carSpot = new CarSpot("2F001"); 17 | bikeSpot = new MotorcycleSpot("1F010"); 18 | }); 19 | 20 | it("Verify parking spot instances", () => { 21 | expect(carSpot).toBeInstanceOf(CarSpot); 22 | expect(bikeSpot).toBeInstanceOf(MotorcycleSpot); 23 | }); 24 | 25 | it("Verify parking spot types", () => { 26 | expect(carSpot.getParkingSpotType()).toEqual(ParkingSpotType.Compact); 27 | expect(bikeSpot.getParkingSpotType()).toEqual(ParkingSpotType.Motorcycle); 28 | }); 29 | 30 | it("Verify parking spot id", () => { 31 | expect(carSpot.getParkingSpotID()).toEqual("2F001"); 32 | expect(bikeSpot.getParkingSpotID()).toEqual("1F010"); 33 | }); 34 | 35 | it("Verify parking spot availability when its free", () => { 36 | expect(carSpot.isSpotFree()).toEqual(true); 37 | expect(bikeSpot.isSpotFree()).toEqual(true); 38 | }); 39 | 40 | it("Verify parking spot availability when its occupied", () => { 41 | const car = new Car("TN12345"); 42 | const bike = new MotorCycle("TN10005"); 43 | 44 | carSpot.assignVehicleToSpot(car); 45 | bikeSpot.assignVehicleToSpot(bike); 46 | 47 | expect(carSpot.isSpotFree()).toEqual(false); 48 | expect(bikeSpot.isSpotFree()).toEqual(false); 49 | }); 50 | 51 | it("Verify parking spot availability when occupied spot is vacated", () => { 52 | const car = new Car("TN12345"); 53 | const bike = new MotorCycle("TN10005"); 54 | 55 | carSpot.assignVehicleToSpot(car); 56 | bikeSpot.assignVehicleToSpot(bike); 57 | 58 | carSpot.vacateVehicleFromSpot(); 59 | bikeSpot.vacateVehicleFromSpot(); 60 | 61 | expect(carSpot.isSpotFree()).toEqual(true); 62 | expect(bikeSpot.isSpotFree()).toEqual(true); 63 | }); 64 | 65 | it("Verify whether its able to park vehicle on the occupied spot", () => { 66 | const car = new Car("TN12345"); 67 | carSpot.assignVehicleToSpot(car); 68 | 69 | expect(() => carSpot.assignVehicleToSpot(car)).toThrowError( 70 | "No spots are available for Car" 71 | ); 72 | }); 73 | 74 | it("Verify details of the vehicle parked in the spot", () => { 75 | const car = new Car("TN12345"); 76 | carSpot.assignVehicleToSpot(car); 77 | const parkedVehicle = carSpot.getVehicleDetails()!; 78 | 79 | expect(parkedVehicle).toBeInstanceOf(Vehicle); 80 | expect(parkedVehicle.getVehicleRegisterNumber()).toEqual("TN12345"); 81 | expect(parkedVehicle.getVehicleType()).toEqual(VehicleType.Car); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/test/vehicle.spec.ts: -------------------------------------------------------------------------------- 1 | import { beforeAll, describe, expect, it } from "vitest"; 2 | 3 | import { Car } from "../src/vehicles/car"; 4 | import { MotorCycle } from "../src/vehicles/motor.cycle"; 5 | import { Vehicle } from "../src/vehicles/vehicle"; 6 | import { VehicleType } from "../src/vehicles/vehicle.type"; 7 | 8 | describe("Vehicle specify test cases", () => { 9 | let car: Vehicle; 10 | let bike: Vehicle; 11 | 12 | beforeAll(() => { 13 | car = new Car("TN12345"); 14 | bike = new MotorCycle("TN10005"); 15 | }); 16 | 17 | it("Verify vehicle instance type", () => { 18 | expect(car).toBeInstanceOf(Car); 19 | expect(bike).toBeInstanceOf(MotorCycle); 20 | }); 21 | 22 | it("Verify vehicle register number", () => { 23 | expect(car.getVehicleRegisterNumber()).toEqual("TN12345"); 24 | expect(bike.getVehicleRegisterNumber()).toEqual("TN10005"); 25 | }); 26 | 27 | it("Verify vehicle type", () => { 28 | expect(car.getVehicleType()).toEqual(VehicleType.Car); 29 | expect(bike.getVehicleType()).toEqual(VehicleType.Motorcycle); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "lib": ["es6"], 6 | "allowJs": true, 7 | "rootDir": "./", 8 | "strict": true, 9 | "types": ["node"], 10 | "noImplicitAny": true, 11 | "esModuleInterop": true, 12 | "sourceMap": true, 13 | "moduleResolution": "Node", 14 | "allowSyntheticDefaultImports": true 15 | }, 16 | "ts-node": { 17 | "esm": true, 18 | "experimentalSpecifierResolution": "node" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /low-level-design/parking-lot/ts/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | includeSource: ["src/**/*.ts", "test/**/*.ts"], 6 | coverage: { 7 | reporter: ["text", "html"], 8 | }, 9 | }, 10 | }); 11 | --------------------------------------------------------------------------------