### Development Server Setup with Inert Source: https://github.com/hapijs/inert/blob/master/_autodocs/configuration.md Configure a Hapi.js server for development using Inert. This setup includes a smaller ETags cache and enables directory listing for easier local testing. ```javascript const Hapi = require('@hapi/hapi'); const Inert = require('@hapi/inert'); const server = new Hapi.Server({ port: 3000, routes: { files: { relativeTo: __dirname } }, plugins: { inert: { etagsCacheMaxSize: 1000 // Smaller cache in dev } } }); await server.register(Inert); server.route({ method: 'GET', path: '/{param*}', handler: { directory: { path: './public', index: true, listing: true, etagMethod: 'simple' // Faster for dev } } }); ``` -------------------------------- ### Example HTML Directory Listing Output Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/directory.md This is an example of the auto-generated HTML output for a directory listing, including file links and parent directory navigation. ```html Directory: /downloads/subfolder/

Directory: /downloads/subfolder/

``` -------------------------------- ### Pre-Compressed Files Setup Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/file.md Configures the file handler to look for and serve pre-compressed versions of files based on the client's Accept-Encoding header. ```javascript handler: { file: { path: './app.js', lookupCompressed: true, lookupMap: { gzip: '.gz', brotli: '.br' } } } ``` -------------------------------- ### Example: Requesting a Non-Existent File Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md Demonstrates how to make a request to a file that does not exist and checks the resulting status code and error message. ```javascript // Request to non-existent file const res = await server.inject({ method: 'GET', url: '/files/does-not-exist.pdf' }); // res.statusCode === 404 // res.result.statusCode === 404 // res.result.message === 'Not Found' ``` -------------------------------- ### Byte Range Options Example Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/file.md Specifies byte ranges for serving a portion of a file, useful for large media files. ```javascript handler: { file: { path: './large-video.mp4', start: 1000000, // Skip first 1MB end: 2000000 // Stop after 2MB total } } ``` -------------------------------- ### Basic Static File Server Setup Source: https://github.com/hapijs/inert/blob/master/API.md Sets up a Hapi server to serve static files from a 'public' directory. Requires registering the Inert plugin. ```javascript const Path = require('path'); const Hapi = require('@hapi/hapi'); const Inert = require('@hapi/inert'); const server = new Hapi.Server({ port: 3000, routes: { files: { relativeTo: Path.join(__dirname, 'public') } } }); const provision = async () => { await server.register(Inert); server.route({ method: 'GET', path: '/{param*}', handler: { directory: { path: '.', redirectToSlash: true, index: true, } } }); await server.start(); console.log('Server running at:', server.info.uri); }; provision(); ``` -------------------------------- ### ReplyFileHandlerOptions Configuration Source: https://github.com/hapijs/inert/blob/master/_autodocs/types.md Example of configuring options for the reply.file() method to serve a PDF with custom download settings. ```javascript const handler = (request, h) => { return h.file('./reports/report.pdf', { mode: 'attachment', filename: `report-${request.params.id}.pdf`, etagMethod: 'simple' }); }; server.route({ method: 'GET', path: '/report/{id}', config: { files: { relativeTo: __dirname } }, handler }); ``` -------------------------------- ### Directory Handler Route Definition Examples Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/directory.md Examples of how to define routes using the directory handler with different path matching strategies. ```APIDOC ## GET /public/{param*} ### Description Serves static files from the './public' directory, matching any depth of path. ### Method GET ### Endpoint /public/{param*} ### Parameters #### Path Parameters - **param*** (string) - Required - Captures the requested file/path at any depth. ### Request Example ```json { "example": "No request body for GET requests" } ``` ### Response #### Success Response (200) - **file content** (binary) - The content of the requested static file. #### Response Example ```json { "example": "[Binary file content]" } ``` ## GET /downloads/{file} ### Description Serves static files from the './downloads' directory, matching only a single level path. ### Method GET ### Endpoint /downloads/{file} ### Parameters #### Path Parameters - **file** (string) - Required - Captures the requested file name. ### Request Example ```json { "example": "No request body for GET requests" } ``` ### Response #### Success Response (200) - **file content** (binary) - The content of the requested static file. #### Response Example ```json { "example": "[Binary file content]" } ``` ## GET /files/{path*3} ### Description Serves static files from the './files' directory, matching exactly 3 levels of path. ### Method GET ### Endpoint /files/{path*3} ### Parameters #### Path Parameters - **path*3** (string) - Required - Captures the requested file/path up to 3 levels deep. ### Request Example ```json { "example": "No request body for GET requests" } ``` ### Response #### Success Response (200) - **file content** (binary) - The content of the requested static file. #### Response Example ```json { "example": "[Binary file content]" } ``` ``` -------------------------------- ### File Handler Configuration Validation Examples Source: https://github.com/hapijs/inert/blob/master/_autodocs/configuration.md Illustrates common validation errors for the file handler configuration. Ensure 'path' is provided, 'filename' includes 'mode', 'start' is not greater than 'end', and 'etagMethod' is valid. ```javascript // Path is required handler: { file: { /* missing path */ } } // Error ``` ```javascript // Filename requires mode handler: { file: { path: './', filename: 'test.pdf' } } // Error ``` ```javascript // start must be <= end handler: { file: { path: './', start: 100, end: 50 } } // Error ``` ```javascript // etagMethod must be 'hash', 'simple', or false handler: { file: { path: './', etagMethod: 'invalid' } } // Error ``` -------------------------------- ### Production Server Setup with Inert Source: https://github.com/hapijs/inert/blob/master/_autodocs/configuration.md Configure a Hapi.js server for production using Inert. This setup uses a larger ETags cache, enables compression for responses over 1KB, and uses SHA1 ETags for distributed deployments. ```javascript const Hapi = require('@hapi/hapi'); const Inert = require('@hapi/inert'); const server = new Hapi.Server({ port: 3000, routes: { files: { relativeTo: __dirname } }, compression: { minBytes: 1024 }, plugins: { inert: { etagsCacheMaxSize: 50000 // Large cache for production } } }); await server.register(Inert); server.route({ method: 'GET', path: '/api/static/{path*}', handler: { directory: { path: './dist', index: ['index.html'], listing: false, lookupCompressed: true, etagMethod: 'hash' // SHA1 for distributed deployment } } }); ``` -------------------------------- ### Example of Simple ETag Computation Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/etag.md Provides an example of the resulting ETag string based on specific file size and modification time values. ```javascript // File: 4096 bytes, modified at 1686000000000ms // ETag: "1000-1896db26e8" ``` -------------------------------- ### Install @hapi/inert Plugin Source: https://github.com/hapijs/inert/blob/master/_autodocs/README.md Install the @hapi/inert plugin using npm. This command downloads and installs the package into your project's node_modules directory. ```bash npm install @hapi/inert ``` -------------------------------- ### HTTP 206 Partial Content Request Example Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/file.md Demonstrates a client request for a byte range and the corresponding server response for partial content. ```http GET /file HTTP/1.1 Range: bytes=0-1023 ``` ```http HTTP/1.1 206 Partial Content Content-Range: bytes 0-1023/5000 Content-Length: 1024 ``` -------------------------------- ### Registering Inert and Serving Files/Directories Source: https://github.com/hapijs/inert/blob/master/_autodocs/START_HERE.md This example demonstrates how to register the @hapi/inert plugin and configure routes to serve a single file as an attachment and a directory of files with directory listing enabled. ```javascript const Hapi = require('@hapi/hapi'); const Inert = require('@hapi/inert'); const server = new Hapi.Server({ port: 3000, routes: { files: { relativeTo: __dirname } } }); // Register plugin await server.register(Inert); // Serve single file server.route({ method: 'GET', path: '/download', handler: { file: { path: './files/report.pdf', mode: 'attachment' // Forces download } } }); // Serve directory server.route({ method: 'GET', path: '/public/{param*}', handler: { directory: { path: './public', index: true, listing: true } } }); await server.start(); ``` -------------------------------- ### Hapi Server Setup with Inert Plugin Source: https://github.com/hapijs/inert/blob/master/_autodocs/README.md Register the @hapi/inert plugin with a Hapi server and configure routes for serving static files and directories. Ensure routes have file access configured relative to __dirname. ```javascript const Hapi = require('@hapi/hapi'); const Inert = require('@hapi/inert'); const server = new Hapi.Server({ port: 3000, routes: { files: { relativeTo: __dirname } } }); await server.register(Inert); // Serve single file server.route({ method: 'GET', path: '/download/{filename}', handler: { file: { path: (request) => `./downloads/${request.params.filename}`, mode: 'attachment' } } }); // Serve directory server.route({ method: 'GET', path: '/public/{param*}', handler: { directory: { path: './public', index: true, listing: true } } }); await server.start(); ``` -------------------------------- ### Example: Requesting a Directory with File Handler Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md Demonstrates configuring a route to use the file handler with a path that is actually a directory, triggering a 403 error. ```javascript // Request to directory with file handler server.route({ method: 'GET', path: '/file', handler: { file: './public' // Path is a directory! } }); const res = await server.inject('/file'); // res.statusCode === 403 // res.result.data.code === 'EISDIR' ``` -------------------------------- ### Invalid File Handler: End Less Than Start Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md This example demonstrates an invalid configuration for the file handler where 'end' is less than 'start', resulting in a validation error. ```javascript handler: { file: { path: './', start: 100, end: 50 // Error: end must be >= start } } ``` -------------------------------- ### Serve a Dynamic File Based on Request Parameters Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/file.md This example shows how to dynamically determine the file path to serve based on request parameters. The `relativeTo` option in `config.files` is useful for resolving paths relative to the current file. ```javascript server.route({ method: 'GET', path: '/user/{id}/download', config: { files: { relativeTo: __dirname } }, handler: { file: (request) => { return `./user-data/${request.params.id}/export.pdf`; } } }); ``` -------------------------------- ### Path Confinement Example Source: https://github.com/hapijs/inert/blob/master/_autodocs/REFERENCE_GUIDE.md Demonstrates how to use the 'confine' option in the file handler to prevent directory traversal. The resolved path must be within the specified confine directory. ```javascript handler: { file: { path: '../../etc/passwd', // Blocked! confine: './public' } } ``` -------------------------------- ### Toolkit Method: reply.file() Usage Example Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/file.md Use the `reply.file()` toolkit method within a route handler to serve a file with specified options like mode, filename, and etagMethod. ```javascript const Hapi = require('@hapi/hapi'); const Inert = require('@hapi/inert'); const Path = require('path'); const server = new Hapi.Server({ port: 3000 }); await server.register(Inert); server.route({ method: 'GET', path: '/resume', config: { files: { relativeTo: __dirname } }, handler: (request, h) => { return h.file('resumes/my-resume.pdf', { mode: 'attachment', filename: 'resume-2024.pdf', etagMethod: 'hash' }); } }); await server.start(); ``` -------------------------------- ### Register Inert Plugin with Server Configuration Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/index.md Example of registering the Inert plugin with custom ETag cache size configuration. ```javascript const Hapi = require('@hapi/hapi'); const Inert = require('@hapi/inert'); const server = new Hapi.Server({ port: 3000, plugins: { inert: { etagsCacheMaxSize: 5000 // Custom ETag cache size } } }); await server.register(Inert); // Now define routes using file and directory handlers server.route({ method: 'GET', path: '/static/{param*}', handler: { directory: { path: './public', index: true, listing: true } } }); ``` -------------------------------- ### Memory-Constrained Server Setup with Inert Source: https://github.com/hapijs/inert/blob/master/_autodocs/configuration.md Configure a Hapi.js server with Inert for memory-constrained environments. This setup disables ETags caching entirely and uses simple ETags to minimize memory usage. ```javascript const server = new Hapi.Server({ port: 3000, plugins: { inert: { etagsCacheMaxSize: 0 // Disable caching } } }); await server.register(Inert); // Use simple ETags (no caching needed) server.route({ method: 'GET', path: '/{param*}', handler: { directory: { path: './public', etagMethod: 'simple' // Fast, no cache } } }); ``` -------------------------------- ### Static Website Configuration Source: https://github.com/hapijs/inert/blob/master/_autodocs/INDEX.md Configure Inert to serve a static website. This setup uses a file handler for the main index file and a directory handler for other assets. ```plaintext File Handler: ./public/index.html Directory Handler: ./public/* with index: true ``` -------------------------------- ### DirectoryHandlerRouteObject Configuration Source: https://github.com/hapijs/inert/blob/master/_autodocs/types.md Example of configuring the directory handler to serve static files from a 'public' directory with specific settings. ```javascript server.route({ method: 'GET', path: '/public/{param*}', handler: { directory: { path: './public', index: ['index.html', 'default.html'], listing: true, showHidden: false, redirectToSlash: true, lookupCompressed: true, etagMethod: 'hash' } } }); ``` -------------------------------- ### Configure Server Compression for Inert Source: https://github.com/hapijs/inert/blob/master/_autodocs/configuration.md Set server-level compression options to influence how Inert handles compressed files and response compression. This example sets a minimum byte size for compression. ```javascript const server = new Hapi.Server({ compression: { minBytes: 1024 // Only compress responses > 1KB } }); ``` -------------------------------- ### OptionalRegistrationOptions Usage Example Source: https://github.com/hapijs/inert/blob/master/_autodocs/types.md Shows how to configure the inert plugin during server registration to limit the ETag cache size. Setting etagsCacheMaxSize to 0 disables ETag caching. ```javascript const server = new Hapi.Server({ port: 3000, plugins: { inert: { etagsCacheMaxSize: 5000 // Store up to 5000 ETags } } }); await server.register(Inert); ``` -------------------------------- ### File Handler Route Definition Examples Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/file.md Demonstrates three ways to configure the file handler in a Hapi route: using a simple string path, a function that returns a path, or a configuration object with advanced options. ```APIDOC ## GET /logo ### Description Serves a static file located at a predefined path. ### Method GET ### Endpoint /logo ### Parameters #### Request Body This endpoint does not accept a request body. ### Request Example This endpoint does not require a request body. ### Response #### Success Response (200) Serves the file specified in the handler configuration. #### Response Example (Binary content of the file) --- ## GET /user/{id}/avatar ### Description Serves a user-specific avatar file where the path is determined dynamically by a function. ### Method GET ### Endpoint /user/{id}/avatar ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the user whose avatar is to be served. #### Request Body This endpoint does not accept a request body. ### Request Example This endpoint does not require a request body. ### Response #### Success Response (200) Serves the user's avatar file. #### Response Example (Binary content of the avatar file) --- ## GET /download/{filename} ### Description Serves a file from the download directory with options for custom filename, content disposition, and confinement. ### Method GET ### Endpoint /download/{filename} ### Parameters #### Path Parameters - **filename** (string) - Required - The name of the file to download. #### Request Body This endpoint does not accept a request body. ### Request Example This endpoint does not require a request body. ### Response #### Success Response (200) Serves the specified file, potentially with a custom filename and as an attachment. #### Response Example (Binary content of the file, potentially named 'custom-name.pdf') ``` -------------------------------- ### File Handler with Simple ETags to Avoid Hashing Errors Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md This example configures the file handler to use 'simple' ETags, which avoids potential errors associated with ETag hash computation. ```javascript handler: { file: { path: './volatile.txt', etagMethod: 'simple' // No hashing, no errors } } ``` -------------------------------- ### Example: Requesting a Protected File Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md Demonstrates making a request to a file that the process does not have permission to access and verifies the resulting 403 Forbidden status code. ```javascript // Request to protected file const res = await server.inject({ method: 'GET', url: '/files/root-only.txt' }); // res.statusCode === 403 // res.result.message === 'Forbidden' ``` -------------------------------- ### RequestHandler Usage Example Source: https://github.com/hapijs/inert/blob/master/_autodocs/types.md Demonstrates how to use the RequestHandler type to dynamically resolve a file path based on request parameters. This handler is then used within a server route configuration. ```javascript const pathResolver: RequestHandler = (request) => { return `/files/${request.params.userId}/document.pdf`; }; server.route({ method: 'GET', path: '/user/{userId}/files', handler: { file: pathResolver } }); ``` -------------------------------- ### FileHandlerRouteObject Usage Example Source: https://github.com/hapijs/inert/blob/master/_autodocs/types.md Illustrates configuring the file handler with dynamic path resolution, attachment mode, confinement to a specific directory, and ETag hashing. Relative paths are resolved from the route's files.relativeTo setting. ```javascript server.route({ method: 'GET', path: '/download/{filename}', config: { files: { relativeTo: __dirname } }, handler: { file: { path: (request) => `./downloads/${request.params.filename}`, mode: 'attachment', confine: './downloads', etagMethod: 'hash' } } }); ``` -------------------------------- ### Error Data Example for Permission Denied Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md Shows the error data structure for permission denied errors, including the path and the specific file system error code (e.g., 'EACCES' or 'EPERM'). ```javascript { path: '/path/to/protected/file.txt', code: 'EACCES' // or 'EPERM' } ``` -------------------------------- ### API Static Assets Configuration Source: https://github.com/hapijs/inert/blob/master/_autodocs/INDEX.md Configure Inert to serve static assets for an API. This setup uses a directory handler with an index file and pre-compressed assets, suitable for distributed deployments. ```plaintext Directory Handler: ./dist Index: ['index.html'] Pre-compressed: true ETag: 'hash' (distributed deployment) ``` -------------------------------- ### Directory Handler Request Path Processing Example Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/directory.md Illustrates how the directory handler resolves request paths by appending them to the base directory path defined in the route handler. Security measures like blocking absolute paths and path traversal are enforced. ```javascript // Route: GET /files/{path*} // Handler path: './downloads' Request: GET /files/docs/readme.txt Resolved: ./downloads/docs/readme.txt Request: GET /files/subfolder/ Resolved: ./downloads/subfolder/ ``` -------------------------------- ### Basic hapi.js Server with Inert Source: https://github.com/hapijs/inert/blob/master/_autodocs/REFERENCE_GUIDE.md Register the Inert plugin and configure routes to serve a single file and a directory. Ensure routes.files.relativeTo is set for relative path resolution. ```javascript const Hapi = require('@hapi/hapi'); const Inert = require('@hapi/inert'); const server = new Hapi.Server({ port: 3000, routes: { files: { relativeTo: __dirname } } }); // Register the plugin await server.register(Inert); // Serve a single file server.route({ method: 'GET', path: '/style.css', handler: { file: './public/style.css' } }); // Serve a directory server.route({ method: 'GET', path: '/public/{param*}', handler: { directory: { path: './public', index: true, listing: true } } }); await server.start(); ``` -------------------------------- ### Serve a Simple Static File Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/file.md Use this configuration to serve a static file directly from the file system. Ensure the file path is correct relative to the server's working directory. ```javascript server.route({ method: 'GET', path: '/style.css', handler: { file: './public/style.css' } }); ``` -------------------------------- ### Use Toolkit Method for File Serving with Options Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/file.md This snippet demonstrates using the `h.file()` toolkit method within a handler to serve a file. It allows for specifying options like `mode` and `etagMethod` directly in the handler function. ```javascript server.route({ method: 'GET', path: '/api/export', config: { files: { relativeTo: __dirname } }, handler: (request, h) => { const reportFile = request.query.format === 'csv' ? './exports/report.csv' : './exports/report.xlsx'; return h.file(reportFile, { mode: 'attachment', etagMethod: 'simple' }); } }); ``` -------------------------------- ### Get File Stats Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/fs.md Retrieves statistics for an open file descriptor, including its size. This function wraps Node.js's fs.fstat(). ```javascript const fd = await exports.open('./file.txt', 'r'); const stats = await exports.fstat(fd); console.log(`Size: ${stats.size}`); await exports.close(fd); ``` -------------------------------- ### Multiple Custom Index Files Configuration Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/directory.md Configure the directory handler to try multiple custom index files in order and serve the first one that exists. ```javascript handler: { directory: { index: ['index.html', 'index.htm', 'default.html'] } } ``` -------------------------------- ### Path Attack Prevention Example Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/file.md The confine security feature prevents directory traversal attacks by validating resolved paths against the confine directory. ```javascript // These are blocked when confine is './public' '../../../etc/passwd' // Escapes confine './public-secret/file' // Sibling directory with same prefix ``` -------------------------------- ### Module Architecture Overview Source: https://github.com/hapijs/inert/blob/master/_autodocs/README.md Illustrates the internal structure of the @hapi/inert module, showing the purpose of key files. ```text @hapi/inert ├── lib/index.js → Plugin registration ├── lib/file.js → File handler ├── lib/directory.js → Directory handler ├── lib/etag.js → ETag generation & caching └── lib/fs.js → File system wrapper ``` -------------------------------- ### Basic Static Site Serving Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/directory.md Serve static files from a specified directory. Ensure the 'public' directory exists and contains your site's assets. The 'index: true' option serves an index file (e.g., index.html) if present. ```javascript const Hapi = require('@hapi/hapi'); const Inert = require('@hapi/inert'); const server = new Hapi.Server({ port: 3000 }); await server.register(Inert); server.route({ method: 'GET', path: '/{param*}', handler: { directory: { path: './public', index: true } } }); await server.start(); ``` -------------------------------- ### Handle Failed File Hash Error Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md This example demonstrates handling a 500 error during ETag hash computation, which can happen if the file is deleted while hashing. ```javascript // File deleted during ETag hash computation: handler: { file: { path: './temp-file.txt', etagMethod: 'hash' // Compute hash } } // If file deleted during hash: // res.statusCode === 500 // res.result.message === 'Failed to hash file' ``` -------------------------------- ### Error Data Example for Is Directory Error Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md Shows the error data for when the file handler is used on a path that is a directory, including the path and the 'EISDIR' code. ```javascript { path: '/path/to/directory', code: 'EISDIR' } ``` -------------------------------- ### Enable Pre-Compressed Files Lookup Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/directory.md Configure the directory handler to look for pre-compressed versions of files (e.g., '.gz', '.br') based on the client's 'Accept-Encoding' header. This optimizes delivery of static assets. ```javascript handler: { directory: { path: './public', lookupCompressed: true, lookupMap: { gzip: '.gz', brotli: '.br' } } } ``` -------------------------------- ### Directory Handler: Allow Access to Hidden Files Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md Enable the `showHidden` option in the directory handler to allow access to files and directories starting with a dot. ```javascript handler: { directory: { path: './public', showHidden: true // Allow hidden files } } ``` -------------------------------- ### Disable Hidden Files Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/directory.md Configure the directory handler to prevent serving or listing files and directories that start with a dot ('.'). This is the default behavior for security. ```javascript handler: { directory: { showHidden: false // Default: blocks .htaccess, .env, etc. } } ``` -------------------------------- ### Serve Multiple Asset Directories Source: https://github.com/hapijs/inert/blob/master/_autodocs/REFERENCE_GUIDE.md Configure a route to serve assets from multiple directories, with a fallback mechanism to subsequent directories if the file is not found in the primary one. ```javascript server.route({ method: 'GET', path: '/assets/{path*}', handler: { directory: { path: [ './assets', // Primary './legacy/assets', // Fallback './defaults' // Final fallback ] } } }); ``` -------------------------------- ### Directory Handler with Multiple Fallback Directories Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md This configuration shows how to set up a directory handler with multiple fallback paths to serve files from different locations if the primary path fails. ```javascript handler: { directory: { path: [ './public', // Primary './legacy/public', // Fallback './defaults' // Final fallback ] } } ``` -------------------------------- ### Open File and Get File Descriptor Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/fs.md Opens a file with a specified mode and returns its file descriptor. This is a wrapper around Node.js's fs.open(). ```javascript const fd = await exports.open('./file.txt', 'r'); ``` -------------------------------- ### Get File Stats Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/fs.md Opens a file, retrieves its metadata (stats), logs the size and modification time, and then closes the file. Assumes the file is open. ```javascript const file = new File('./data.txt'); await file.open('r'); const stats = await file.stat(); console.log(`File size: ${stats.size} bytes`); console.log(`Modified: ${stats.mtime}`); file.close(); ``` -------------------------------- ### Custom Content Encoding Lookup Map Example Source: https://github.com/hapijs/inert/blob/master/_autodocs/types.md Define a custom mapping between content encoding names and their corresponding file extensions for the Inert file handler. ```javascript lookupMap: { gzip: '.gz', brotli: '.br', 'custom-encoding': '.custom' } ``` -------------------------------- ### Basic Static Site Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/directory.md Serves files from a 'public' directory with index.html support. ```APIDOC ## GET /{param*} ### Description Serves files from a specified directory, with support for index files. ### Method GET ### Endpoint `/{param*}` ### Handler Configuration ```json { "directory": { "path": "./public", "index": true } } ``` ``` -------------------------------- ### Multiple Paths (Fallback) Configuration Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/directory.md Configure the directory handler to attempt serving files from multiple directories in a specified order. The handler tries subsequent directories if a file is not found in the preceding ones, with the first match being served. ```javascript handler: { directory: { path: ['./public', './legacy-public', './fallback'] } } ``` -------------------------------- ### Open and Get File Stats Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/fs.md Opens a file and immediately retrieves its statistics. If the stat operation fails, the file is closed. Handles errors during opening or stat operations. ```javascript const file = new File('./config.json'); try { const stats = await file.openStat('r'); console.log(`File is ${stats.size} bytes`); // File is now open and ready to read file.close(); } catch (err) { // File not opened } ``` -------------------------------- ### Toolkit Method: reply.file() Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/file.md Provides a convenience method within route handlers to serve files with various options. ```APIDOC ## reply.file(path, options) ### Description Serves a file using the toolkit method, allowing for options like custom filename, content disposition, and ETag generation. ### Method Signature `reply.file(path: string, options?: ReplyFileHandlerOptions): ResponseObject` ### Parameters #### Path Parameters - **path** (string) - Required - Relative or absolute file path. #### Query Parameters This method does not directly use query parameters. #### Request Body This method does not accept a request body. #### Options (ReplyFileHandlerOptions) - **confine** (boolean | string) - Optional - Restrict file serving to a directory. Defaults to `true`. - **filename** (string) - Optional - Custom filename for Content-Disposition header. Only valid when `mode` is set. - **mode** ('attachment' | 'inline' | false) - Optional - Include Content-Disposition header. `'attachment'` triggers download, `'inline'` displays in browser. Defaults to `false`. - **lookupCompressed** (boolean) - Optional - Search for pre-compressed versions (e.g., `.gz` files). Defaults to `false`. - **lookupMap** ({ [encoding: string]: string }) - Optional - Maps content encodings to file extensions for compressed file lookup. Defaults to `{ gzip: '.gz' }`. - **etagMethod** ('hash' | 'simple' | false) - Optional - ETag computation method. Defaults to `'hash'`. - **start** (number) - Optional - Byte offset to start reading from. Defaults to `0`. - **end** (number) - Optional - Byte offset to stop reading (inclusive). If not set, reads to end of file. Must be >= start. ### Request Example ```javascript return h.file('resumes/my-resume.pdf', { mode: 'attachment', filename: 'resume-2024.pdf', etagMethod: 'hash' }); ``` ### Response #### Success Response (200) Response configured to serve the file. #### Response Example (Binary content of the file) ``` -------------------------------- ### Invalid File Handler: Filename without Mode Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md This code example shows an invalid configuration for the file handler where 'filename' is provided without a 'mode', leading to a validation error. ```javascript handler: { file: { path: './', filename: 'test.pdf' // Error: mode required } } // Throws validation error during route registration ``` -------------------------------- ### Example: Path Escapes Confine Directory Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md Illustrates a route configuration where the requested file path attempts to escape the specified 'confine' directory, triggering a 403 Forbidden error. ```javascript // Confine to ./public, but request escapes server.route({ method: 'GET', path: '/file', handler: { file: { path: '../../etc/passwd', // Escapes confine confine: './public' } } }); const res = await server.inject('/file'); // res.statusCode === 403 // res.result.message === 'Forbidden' ``` -------------------------------- ### Serve Pre-Compressed Assets Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/file.md This configuration allows Inert to automatically serve pre-compressed assets (like gzip or brotli) if available, based on the `Accept-Encoding` header. The `lookupCompressed` option should be set to `true`. ```javascript server.route({ method: 'GET', path: '/app/{path*}', config: { files: { relativeTo: __dirname } }, handler: { file: { path: (request) => `./dist/${request.params.path || 'index.html'}`, lookupCompressed: true, etagMethod: 'hash' } } }); ``` -------------------------------- ### Error Data Example for File Not Found Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md Illustrates the structure of error data when a requested file is not found or a path component is not a directory. This data includes the path that caused the error. ```javascript { path: '/path/to/missing/file.txt' } ``` -------------------------------- ### Multiple Fallback Directories Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/directory.md Configure multiple directories to serve assets, with a fallback mechanism. The handler checks directories in the order they are provided, serving from the first one that contains the requested file. ```javascript server.route({ method: 'GET', path: '/assets/{path*}', handler: { directory: { path: [ './public/assets', // Try primary first './legacy/assets', // Fall back to legacy './defaults' // Final fallback ], lookupCompressed: true } } }); ``` -------------------------------- ### Directory Handler Configuration Validation Examples Source: https://github.com/hapijs/inert/blob/master/_autodocs/configuration.md Shows validation errors for the directory handler. 'Path' is mandatory, 'defaultExtension' must be alphanumeric, and the route path requires a trailing parameter. ```javascript // Path is required handler: { directory: { /* missing path */ } } // Error ``` ```javascript // defaultExtension must be alphanumeric handler: { directory: { path: './', defaultExtension: 'my-ext' } } // Error ``` ```javascript // Route must end with parameter server.route({ path: '/files', // Missing trailing parameter handler: { directory: { path: './' } } // Error }); ``` -------------------------------- ### Serve Public Website Source: https://github.com/hapijs/inert/blob/master/_autodocs/REFERENCE_GUIDE.md Configure a route to serve static files for a public website. Specifies the directory to serve from and the default index file. ```javascript server.route({ method: 'GET', path: '/{param*}', config: { files: { relativeTo: __dirname } }, handler: { directory: { path: './public', index: ['index.html'], redirectToSlash: true, lookupCompressed: true } } }); ``` -------------------------------- ### Enable Pre-compression Lookup Source: https://github.com/hapijs/inert/blob/master/_autodocs/INDEX.md Configure Inert to look for pre-compressed assets (e.g., .gz files). This reduces transfer size by serving compressed files directly. ```javascript lookupCompressed: true ``` -------------------------------- ### open(path, mode) Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/fs.md Opens a file at the specified path with the given mode and returns its file descriptor. ```APIDOC ## open(path, mode) ### Description Open a file and return the file descriptor. ### Parameters #### Path Parameters * **path** (`string`) - Required - The path to the file. * **mode** (`string`) - Required - The mode to open the file in (e.g., 'r', 'w'). #### Query Parameters * None #### Request Body * None ### Request Example ```javascript const fd = await exports.open('./file.txt', 'r'); ``` ### Response #### Success Response (200) * **File Descriptor** (`number`) - The file descriptor for the opened file. #### Response Example ```javascript 123 ``` ``` -------------------------------- ### createReadStream (File Instance Method) Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/fs.md Creates a readable stream for a file associated with a File instance. Allows specifying stream options like start, end, and autoClose behavior. ```APIDOC ## createReadStream(options) ### Description Create a readable stream for the open file. ### Parameters #### Path Parameters * None #### Query Parameters * None #### Request Body * **options** (`object`) - Optional - Stream options. Defaults to `{}`. * **options.fd** (`number`) - Optional - File descriptor (automatically set). * **options.start** (`number`) - Optional - Byte offset to start reading. Defaults to `0`. * **options.end** (`number`) - Optional - Byte offset to stop reading (inclusive). * **options.autoClose** (`boolean`) - Optional - Close fd when stream ends. Defaults to `true`. ### Request Example ```javascript const file = new File('./large-file.bin'); await file.open('r'); const stream = file.createReadStream({ start: 1000, end: 2000, autoClose: true }); stream.on('data', (chunk) => { console.log(`Read ${chunk.length} bytes`); }); stream.on('end', () => { console.log('Done reading'); }); stream.on('error', (err) => { console.error('Stream error:', err); }); ``` ### Response #### Success Response * **ReadStream** (`object`) - Node.js readable stream #### Response Example ```javascript // A Node.js readable stream object ``` ``` -------------------------------- ### Inert Toolkit Decorations Source: https://github.com/hapijs/inert/blob/master/_autodocs/REFERENCE_GUIDE.md Shows how to use the 'h.file' function within a handler to serve a file, with optional parameters. ```javascript // Available in handlers via second parameter server.route({ handler: (request, h) => { return h.file('./path.txt', options); // Serve file } }); ``` -------------------------------- ### File Handler Byte Range Validation Source: https://github.com/hapijs/inert/blob/master/_autodocs/REFERENCE_GUIDE.md The 'start' option must be less than or equal to the 'end' option for byte range requests. This shows invalid and valid byte range configurations. ```javascript // Wrong: handler: { file: { path: './file.txt', start: 100, end: 50 // end < start! } } // Right: handler: { file: { path: './file.txt', start: 50, end: 100 } } ``` -------------------------------- ### Plugin Registration Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/index.md Demonstrates how to register the @hapi/inert plugin with a hapi server. ```APIDOC ## Plugin Registration ### Description Registers the inert plugin with a hapi server. The plugin decorates the server with two handler types (`file` and `directory`) and adds a `file()` method to the response toolkit. It optionally sets up an ETag cache based on server configuration. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Server Configuration Options | Option | Type | Default | Description | |---|---|---|---| | etagsCacheMaxSize | `number` | 10000 | Maximum number of ETags to cache. Set to 0 to disable caching. | ### Usage Example ```javascript const Hapi = require('@hapi/hapi'); const Inert = require('@hapi/inert'); const server = new Hapi.Server({ port: 3000, plugins: { inert: { etagsCacheMaxSize: 5000 // Custom ETag cache size } } }); await server.register(Inert); // Now define routes using file and directory handlers server.route({ method: 'GET', path: '/static/{param*}', handler: { directory: { path: './public', index: true, listing: true } } }); ``` ``` -------------------------------- ### Handle Failed File Stat Error Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md This snippet illustrates handling a 500 error when attempting to get file metadata (stat), which can occur if the file is deleted between open and stat operations. ```javascript // File deleted between opening and stats: // res.statusCode === 500 // res.result.message === 'Failed to stat file' ``` -------------------------------- ### reply.file() Configuration Source: https://github.com/hapijs/inert/blob/master/_autodocs/types.md Configuration options for the `reply.file()` toolkit method, used for serving a single file. It allows customization of download behavior, filename, ETag generation, and byte range requests. ```APIDOC ## ReplyFileHandlerOptions Configuration options for the `reply.file()` toolkit method. ### Properties | Property | Type | Required | Default | Description | |---|---|---|---|---| | confine | `boolean \| string` | No | `true` | Directory confinement. Defaults to route's `files.relativeTo`. | | filename | `string` | No | Basename of path | Custom filename for download. Only used with `mode` set. | | mode | `false \| 'attachment' \| 'inline'` | No | `false` | Content-Disposition header. `attachment` triggers download, `inline` displays in browser. | | lookupCompressed | `boolean` | No | `false` | Look for pre-compressed versions matching Accept-Encoding. | | lookupMap | `{ [index: string]: string }` | No | `{ gzip: '.gz' }` | Maps content encodings to file extensions. | | etagMethod | `'hash' \| 'simple' \| false` | No | `'hash'` | ETag generation method. `hash` = SHA1, `simple` = size/mtime, `false` = disabled. | | start | `number` | No | `0` | Byte offset to start reading from. | | end | `number` | No | — | Byte offset to stop reading (inclusive). If not set, reads to EOF. | ### Usage Example: ```javascript const handler = (request, h) => { return h.file('./reports/report.pdf', { mode: 'attachment', filename: `report-${request.params.id}.pdf`, etagMethod: 'simple' }); }; server.route({ method: 'GET', path: '/report/{id}', config: { files: { relativeTo: __dirname } }, handler }); ``` ``` -------------------------------- ### Error Conversion Map Example Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/fs.md Illustrates the mapping of file system error codes to hapi Boom errors, including status codes for common errors like 'not found' and 'forbidden'. ```javascript notFound: new Set(['ENOENT', 'ENOTDIR']) ``` -------------------------------- ### Promisified File System Methods Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/fs.md Lists the file system methods that are promisified using Util.promisify() for easier asynchronous handling. ```javascript ['open', 'close', 'fstat', 'readdir'] ``` -------------------------------- ### Invalid Directory Handler: Route Missing Trailing Parameter Source: https://github.com/hapijs/inert/blob/master/_autodocs/errors.md This example illustrates an invalid configuration for the directory handler where the route path does not end with a parameter (e.g., '{param*}'), causing an assertion error. ```javascript server.route({ method: 'GET', path: '/files', // Missing {param*}! handler: { directory: { path: './public' } } }); // Throws assertion error: "must end with a parameter" ``` -------------------------------- ### Distributed Deployment with Hash ETag Consistency Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/etag.md Demonstrates how hash-based ETags ensure consistency across multiple server instances in a distributed deployment, as they depend on file content. ```javascript // Server A const serverA = new Hapi.Server({ plugins: { inert: { etagsCacheMaxSize: 10000 } } }); // Server B const serverB = new Hapi.Server({ plugins: { inert: { etagsCacheMaxSize: 10000 } } }); // Both servers: same file = same ETag // Because hash is based on file content, not server state ``` -------------------------------- ### Download Directory with Listings Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/directory.md Serves files from a 'downloads' directory and enables directory listings. ```APIDOC ## GET /downloads/{file*} ### Description Serves files from the 'downloads' directory and allows directory listings to be viewed. ### Method GET ### Endpoint `/downloads/{file*}` ### Handler Configuration ```json { "directory": { "path": "./downloads", "index": false, "listing": true, "showHidden": false } } ``` ``` -------------------------------- ### Default Index File Configuration Source: https://github.com/hapijs/inert/blob/master/_autodocs/api-reference/directory.md Enable the directory handler to automatically serve an index file when a directory is requested. By default, it looks for 'index.html'. ```javascript handler: { directory: { index: true // Looks for 'index.html' } } ``` -------------------------------- ### File Handler Configuration Options Source: https://github.com/hapijs/inert/blob/master/_autodocs/INDEX.md Details the route-level configuration options specific to the file handler. ```javascript Route-Level File Handler - path (string | function, **required**) - confine (boolean | string, default true) - filename (string) - mode ('attachment' | 'inline' | false) - lookupCompressed (boolean) - lookupMap (object) - etagMethod ('hash' | 'simple' | false) - start (number, default 0) - end (number) ```