rest_api_faker 0.0.0 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -26
- package/bin/api-faker.js +0 -0
- package/dist/cli.js +4 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
Created with ❤️ for front-end developers who need a quick back-end for prototyping and mocking.
|
|
6
6
|
|
|
7
|
-
[](https://github.com/hamidmayeli/rest_api_faker/actions/workflows/ci.yml)
|
|
8
|
+
[](https://codecov.io/gh/hamidmayeli/rest_api_faker)
|
|
9
9
|
[]()
|
|
10
10
|
[]()
|
|
11
11
|
[]()
|
|
@@ -81,13 +81,13 @@ pnpm add -g rest_api_faker
|
|
|
81
81
|
**2. Start the server:**
|
|
82
82
|
|
|
83
83
|
```bash
|
|
84
|
-
|
|
84
|
+
rest_api_faker db.json
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
Or with watch mode to auto-reload on file changes:
|
|
88
88
|
|
|
89
89
|
```bash
|
|
90
|
-
|
|
90
|
+
rest_api_faker --watch db.json
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
**3. Access your API:**
|
|
@@ -110,10 +110,10 @@ That's it! 🎉 You now have a fully functional REST API.
|
|
|
110
110
|
### CLI Options
|
|
111
111
|
|
|
112
112
|
```bash
|
|
113
|
-
|
|
113
|
+
rest_api_faker [options] <source>
|
|
114
114
|
|
|
115
115
|
Options:
|
|
116
|
-
-c, --config <path> Path to config file (default: "
|
|
116
|
+
-c, --config <path> Path to config file (default: "rest_api_faker.json")
|
|
117
117
|
-p, --port <number> Set port (default: 3000)
|
|
118
118
|
-H, --host <string> Set host (default: "localhost")
|
|
119
119
|
-w, --watch Watch file(s) for changes
|
|
@@ -133,11 +133,11 @@ Options:
|
|
|
133
133
|
-v, --version Show version number
|
|
134
134
|
|
|
135
135
|
Examples:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
rest_api_faker db.json Start with db.json
|
|
137
|
+
rest_api_faker --watch db.json Start with auto-reload
|
|
138
|
+
rest_api_faker --port 4000 db.json Start on port 4000
|
|
139
|
+
rest_api_faker file.js Use a JavaScript file
|
|
140
|
+
rest_api_faker --routes routes.json Use custom routes
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
### Routes
|
|
@@ -301,7 +301,7 @@ POST /posts/1/comments
|
|
|
301
301
|
|
|
302
302
|
## Configuration
|
|
303
303
|
|
|
304
|
-
You can use a configuration file instead of CLI options. Create an `
|
|
304
|
+
You can use a configuration file instead of CLI options. Create an `rest_api_faker.json` file:
|
|
305
305
|
|
|
306
306
|
```json
|
|
307
307
|
{
|
|
@@ -324,13 +324,13 @@ You can use a configuration file instead of CLI options. Create an `api-faker.js
|
|
|
324
324
|
Then simply run:
|
|
325
325
|
|
|
326
326
|
```bash
|
|
327
|
-
|
|
327
|
+
rest_api_faker db.json
|
|
328
328
|
```
|
|
329
329
|
|
|
330
330
|
**Using a custom config file:**
|
|
331
331
|
|
|
332
332
|
```bash
|
|
333
|
-
|
|
333
|
+
rest_api_faker db.json --config my-config.json
|
|
334
334
|
```
|
|
335
335
|
|
|
336
336
|
**Note:** CLI arguments override config file values.
|
|
@@ -353,7 +353,7 @@ Create a `routes.json` file with URL rewrite rules:
|
|
|
353
353
|
Use it:
|
|
354
354
|
|
|
355
355
|
```bash
|
|
356
|
-
|
|
356
|
+
rest_api_faker db.json --routes routes.json
|
|
357
357
|
```
|
|
358
358
|
|
|
359
359
|
Now you can access:
|
|
@@ -383,7 +383,7 @@ module.exports = function (req, res, next) {
|
|
|
383
383
|
Use it:
|
|
384
384
|
|
|
385
385
|
```bash
|
|
386
|
-
|
|
386
|
+
rest_api_faker db.json --middlewares middleware.js
|
|
387
387
|
```
|
|
388
388
|
|
|
389
389
|
See [examples/README.md](examples/README.md) for more examples.
|
|
@@ -394,19 +394,19 @@ See [examples/README.md](examples/README.md) for more examples.
|
|
|
394
394
|
|
|
395
395
|
```bash
|
|
396
396
|
# Start with default settings
|
|
397
|
-
|
|
397
|
+
rest_api_faker db.json
|
|
398
398
|
|
|
399
399
|
# Start with watch mode
|
|
400
|
-
|
|
400
|
+
rest_api_faker --watch db.json
|
|
401
401
|
|
|
402
402
|
# Start on a different port
|
|
403
|
-
|
|
403
|
+
rest_api_faker --port 4000 db.json
|
|
404
404
|
|
|
405
405
|
# Read-only mode (only GET requests)
|
|
406
|
-
|
|
406
|
+
rest_api_faker --read-only db.json
|
|
407
407
|
|
|
408
408
|
# Add delay to all responses (useful for testing loading states)
|
|
409
|
-
|
|
409
|
+
rest_api_faker --delay 1000 db.json
|
|
410
410
|
```
|
|
411
411
|
|
|
412
412
|
### Using JavaScript for Dynamic Data
|
|
@@ -445,7 +445,7 @@ module.exports = function () {
|
|
|
445
445
|
Start the server:
|
|
446
446
|
|
|
447
447
|
```bash
|
|
448
|
-
|
|
448
|
+
rest_api_faker db.js
|
|
449
449
|
```
|
|
450
450
|
|
|
451
451
|
**Tip:** Use libraries like [Faker.js](https://fakerjs.dev/) for more realistic data.
|
|
@@ -479,7 +479,7 @@ module.exports = function (req, res, next) {
|
|
|
479
479
|
Use it:
|
|
480
480
|
|
|
481
481
|
```bash
|
|
482
|
-
|
|
482
|
+
rest_api_faker db.json --middlewares auth-middleware.js
|
|
483
483
|
```
|
|
484
484
|
|
|
485
485
|
Now all POST, PUT, PATCH, DELETE requests require the `Authorization: Bearer secret-token` header.
|
|
@@ -511,7 +511,7 @@ Create a `vercel.json`:
|
|
|
511
511
|
Create a `server.js`:
|
|
512
512
|
|
|
513
513
|
```javascript
|
|
514
|
-
const jsonServer = require('
|
|
514
|
+
const jsonServer = require('rest_api_faker');
|
|
515
515
|
const server = jsonServer.create();
|
|
516
516
|
const router = jsonServer.router('db.json');
|
|
517
517
|
const middlewares = jsonServer.defaults();
|
|
@@ -539,7 +539,7 @@ web: node server.js
|
|
|
539
539
|
Create a `server.js`:
|
|
540
540
|
|
|
541
541
|
```javascript
|
|
542
|
-
const jsonServer = require('
|
|
542
|
+
const jsonServer = require('rest_api_faker');
|
|
543
543
|
const server = jsonServer.create();
|
|
544
544
|
const router = jsonServer.router('db.json');
|
|
545
545
|
const middlewares = jsonServer.defaults();
|
|
@@ -568,7 +568,7 @@ git push heroku main
|
|
|
568
568
|
You can also use API Faker programmatically in your Node.js applications:
|
|
569
569
|
|
|
570
570
|
```javascript
|
|
571
|
-
const jsonServer = require('
|
|
571
|
+
const jsonServer = require('rest_api_faker');
|
|
572
572
|
const server = jsonServer.create();
|
|
573
573
|
const router = jsonServer.router('db.json');
|
|
574
574
|
const middlewares = jsonServer.defaults();
|
package/bin/api-faker.js
CHANGED
|
File without changes
|
package/dist/cli.js
CHANGED
|
@@ -1596,11 +1596,11 @@ function getVersion() {
|
|
|
1596
1596
|
}
|
|
1597
1597
|
}
|
|
1598
1598
|
function parseCli() {
|
|
1599
|
-
const argv = (0, import_yargs.default)((0, import_helpers.hideBin)(process.argv)).scriptName("
|
|
1599
|
+
const argv = (0, import_yargs.default)((0, import_helpers.hideBin)(process.argv)).scriptName("rest_api_faker").usage("Usage: $0 [options] <source>").example("$0 db.json", "Start API Faker with db.json").example("$0 file.js", "Start API Faker with a JS file").example("$0 http://example.com/db.json", "Start API Faker with a remote schema").option("config", {
|
|
1600
1600
|
alias: "c",
|
|
1601
1601
|
type: "string",
|
|
1602
1602
|
description: "Path to config file",
|
|
1603
|
-
default: "
|
|
1603
|
+
default: "rest_api_faker.json"
|
|
1604
1604
|
}).option("port", {
|
|
1605
1605
|
alias: "p",
|
|
1606
1606
|
type: "number",
|
|
@@ -1672,7 +1672,7 @@ function parseCli() {
|
|
|
1672
1672
|
type: "boolean",
|
|
1673
1673
|
description: "Suppress log messages from output",
|
|
1674
1674
|
default: false
|
|
1675
|
-
}).help("help", "Show help").alias("h", "help").version(getVersion()).alias("v", "version").epilogue("For more information, visit https://github.com/hamidmayeli/
|
|
1675
|
+
}).help("help", "Show help").alias("h", "help").version(getVersion()).alias("v", "version").epilogue("For more information, visit https://github.com/hamidmayeli/rest_api_faker").parseSync();
|
|
1676
1676
|
let fileConfig = null;
|
|
1677
1677
|
try {
|
|
1678
1678
|
fileConfig = loadConfig(argv.config);
|
|
@@ -1731,7 +1731,7 @@ async function main() {
|
|
|
1731
1731
|
}
|
|
1732
1732
|
if (!config.source) {
|
|
1733
1733
|
logger.error("No source file specified");
|
|
1734
|
-
logger.info('Run "
|
|
1734
|
+
logger.info('Run "rest_api_faker --help" for usage information');
|
|
1735
1735
|
process.exit(1);
|
|
1736
1736
|
}
|
|
1737
1737
|
if (!config.quiet) {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/database.ts","../src/server.ts","../src/router.ts","../src/logger.ts","../src/query.ts","../src/relationships.ts","../src/static.ts","../src/loader.ts","../src/rewriter.ts","../src/config.ts"],"sourcesContent":["import yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\nimport { readFileSync, existsSync } from 'fs';\nimport { resolve } from 'path';\nimport { watch } from 'chokidar';\nimport { Database } from './database';\nimport { createServer, startServer, ServerOptions } from './server';\nimport { loadConfig, mergeConfig, Config } from './config';\nimport { logger } from './logger';\n\n/**\n * CLI configuration interface\n */\ninterface CliConfig {\n source: string | undefined;\n port: number;\n host: string;\n watch: boolean;\n routes: string | undefined;\n middlewares: string | undefined;\n static: string | undefined;\n noStatic: boolean;\n readOnly: boolean;\n noCors: boolean;\n noGzip: boolean;\n snapshots: string;\n delay: number | undefined;\n id: string;\n foreignKeySuffix: string;\n quiet: boolean;\n config: string;\n}\n\n/**\n * Get package version\n */\nfunction getVersion(): string {\n try {\n const packageJson = JSON.parse(\n readFileSync(resolve(__dirname, '../package.json'), 'utf-8')\n ) as { version: string };\n return packageJson.version;\n } catch {\n return '0.0.0';\n }\n}\n\n/**\n * Parse and validate CLI arguments\n */\nfunction parseCli(): CliConfig {\n const argv = yargs(hideBin(process.argv))\n .scriptName('api-faker')\n .usage('Usage: $0 [options] <source>')\n .example('$0 db.json', 'Start API Faker with db.json')\n .example('$0 file.js', 'Start API Faker with a JS file')\n .example('$0 http://example.com/db.json', 'Start API Faker with a remote schema')\n .option('config', {\n alias: 'c',\n type: 'string',\n description: 'Path to config file',\n default: 'api-faker.json',\n })\n .option('port', {\n alias: 'p',\n type: 'number',\n description: 'Set port',\n default: 3000,\n })\n .option('host', {\n alias: 'H',\n type: 'string',\n description: 'Set host',\n default: 'localhost',\n })\n .option('watch', {\n alias: 'w',\n type: 'boolean',\n description: 'Watch file(s)',\n default: false,\n })\n .option('routes', {\n alias: 'r',\n type: 'string',\n description: 'Path to routes file',\n })\n .option('middlewares', {\n alias: 'm',\n type: 'string',\n description: 'Path to middleware file',\n })\n .option('static', {\n alias: 's',\n type: 'string',\n description: 'Set static files directory',\n default: './public',\n })\n .option('no-static', {\n type: 'boolean',\n description: 'Disable static file serving',\n default: false,\n })\n .option('read-only', {\n alias: 'ro',\n type: 'boolean',\n description: 'Allow only GET requests',\n default: false,\n })\n .option('no-cors', {\n alias: 'nc',\n type: 'boolean',\n description: 'Disable Cross-Origin Resource Sharing',\n default: false,\n })\n .option('no-gzip', {\n alias: 'ng',\n type: 'boolean',\n description: 'Disable GZIP Content-Encoding',\n default: false,\n })\n .option('snapshots', {\n alias: 'S',\n type: 'string',\n description: 'Set snapshots directory',\n default: '.',\n })\n .option('delay', {\n alias: 'd',\n type: 'number',\n description: 'Add delay to responses (ms)',\n })\n .option('id', {\n alias: 'i',\n type: 'string',\n description: 'Set database id property',\n default: 'id',\n })\n .option('foreignKeySuffix', {\n alias: 'fks',\n type: 'string',\n description: 'Set foreign key suffix',\n default: 'Id',\n })\n .option('quiet', {\n alias: 'q',\n type: 'boolean',\n description: 'Suppress log messages from output',\n default: false,\n })\n .help('help', 'Show help')\n .alias('h', 'help')\n .version(getVersion())\n .alias('v', 'version')\n .epilogue('For more information, visit https://github.com/hamidmayeli/api-faker')\n .parseSync();\n\n // Load config file if it exists\n let fileConfig: Config | null = null;\n try {\n fileConfig = loadConfig(argv.config);\n } catch (error) {\n logger.error(\n `Failed to load config file: ${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n }\n\n // Build CLI config object (only values explicitly provided by CLI)\n const cliConfig: Partial<Config> = {};\n\n // Only include CLI values that were explicitly set (not defaults)\n // We check if the value is different from the default or if it was provided\n if (argv.port !== 3000) cliConfig.port = argv.port;\n if (argv.host !== 'localhost') cliConfig.host = argv.host;\n if (argv.watch) cliConfig.watch = argv.watch;\n if (argv.routes) cliConfig.routes = argv.routes;\n if (argv.middlewares) cliConfig.middlewares = argv.middlewares;\n if (argv.static !== './public') cliConfig.static = argv.static;\n if (argv['read-only']) cliConfig.readOnly = argv['read-only'];\n if (argv['no-cors']) cliConfig.noCors = argv['no-cors'];\n if (argv['no-gzip']) cliConfig.noGzip = argv['no-gzip'];\n if (argv.snapshots !== '.') cliConfig.snapshots = argv.snapshots;\n if (argv.delay !== undefined) cliConfig.delay = argv.delay;\n if (argv.id !== 'id') cliConfig.id = argv.id;\n if (argv.foreignKeySuffix !== 'Id') cliConfig.foreignKeySuffix = argv.foreignKeySuffix;\n if (argv.quiet) cliConfig.quiet = argv.quiet;\n\n // Merge config file with CLI args (CLI takes precedence)\n const merged = mergeConfig(cliConfig, fileConfig);\n\n return {\n source: argv._[0] as string | undefined,\n port: merged.port ?? 3000,\n host: merged.host ?? 'localhost',\n watch: merged.watch ?? false,\n routes: merged.routes,\n middlewares: merged.middlewares,\n static: merged.static ?? './public',\n noStatic: argv['no-static'],\n readOnly: merged.readOnly ?? false,\n noCors: merged.noCors ?? false,\n noGzip: merged.noGzip ?? false,\n snapshots: merged.snapshots ?? '.',\n delay: merged.delay,\n id: merged.id ?? 'id',\n foreignKeySuffix: merged.foreignKeySuffix ?? 'Id',\n quiet: merged.quiet ?? false,\n config: argv.config,\n };\n}\n\n/**\n * Main CLI entry point\n */\nasync function main(): Promise<void> {\n const config = parseCli();\n\n if (!config.quiet) {\n logger.log(`\n ╔═══════════════════════════════════════╗\n ║ ║\n ║ API Faker v${getVersion().padEnd(19)}║\n ║ ║\n ╚═══════════════════════════════════════╝\n `);\n }\n\n if (!config.source) {\n logger.error('No source file specified');\n logger.info('Run \"api-faker --help\" for usage information');\n process.exit(1);\n }\n\n if (!config.quiet) {\n logger.info(`Source: ${config.source}`);\n logger.info(`Port: ${String(config.port)}`);\n logger.info(`Host: ${config.host}`);\n logger.log('');\n logger.info('Loading database...');\n }\n\n try {\n // Initialize database\n const db = new Database(config.source, {\n idField: config.id,\n foreignKeySuffix: config.foreignKeySuffix,\n });\n\n await db.init();\n\n if (!config.quiet) {\n const data = db.getData();\n const resources = Object.keys(data);\n logger.success(`Loaded ${String(resources.length)} resource(s): ${resources.join(', ')}`);\n logger.log('');\n }\n\n // Create and start server\n const serverOptions: ServerOptions = {\n port: config.port,\n host: config.host,\n readOnly: config.readOnly,\n noCors: config.noCors,\n noGzip: config.noGzip,\n quiet: config.quiet,\n idField: config.id,\n foreignKeySuffix: config.foreignKeySuffix,\n enabled: !config.noStatic,\n };\n\n // Add static directory if specified\n if (config.static) {\n serverOptions.directory = config.static;\n }\n\n // Add custom routes if specified\n if (config.routes) {\n serverOptions.routes = config.routes;\n }\n\n // Add custom middlewares if specified\n if (config.middlewares) {\n serverOptions.middlewares = config.middlewares;\n }\n\n // Only add delay if it's defined\n if (config.delay !== undefined) {\n serverOptions.delay = config.delay;\n }\n\n const app = await createServer(db, serverOptions);\n\n const server = startServer(app, {\n port: config.port,\n host: config.host,\n quiet: config.quiet,\n });\n\n // Set up file watching if enabled\n if (config.watch && config.source && existsSync(config.source)) {\n const watcher = watch(config.source, {\n ignoreInitial: true,\n persistent: true,\n });\n\n watcher.on('change', (path) => {\n if (!config.quiet) {\n logger.log('');\n logger.info(`File changed: ${path}`);\n logger.info('Reloading database...');\n }\n\n db.init()\n .then(() => {\n if (!config.quiet) {\n const data = db.getData();\n const resources = Object.keys(data);\n logger.success(\n `Reloaded ${String(resources.length)} resource(s): ${resources.join(', ')}`\n );\n }\n })\n .catch((error: unknown) => {\n logger.error(\n `Failed to reload database: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n });\n });\n\n watcher.on('error', (error) => {\n logger.error(`Watcher error: ${error instanceof Error ? error.message : String(error)}`);\n });\n\n if (!config.quiet) {\n logger.info(`Watching ${config.source} for changes...`);\n logger.log('');\n }\n\n // Handle graceful shutdown\n process.on('SIGINT', () => {\n if (!config.quiet) {\n logger.log('');\n logger.info('Shutting down...');\n }\n watcher.close().catch(() => {\n // Ignore errors during shutdown\n });\n server.close(() => {\n process.exit(0);\n });\n });\n }\n } catch (error) {\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n}\n\n// Run CLI\nmain().catch((error: unknown) => {\n logger.error(`Fatal error: ${error instanceof Error ? error.message : 'Unknown error'}`);\n process.exit(1);\n});\n","import { Low } from 'lowdb';\nimport { JSONFile } from 'lowdb/node';\nimport { existsSync } from 'fs';\nimport { resolve, extname } from 'path';\nimport { pathToFileURL } from 'url';\n\n/**\n * Database data structure\n */\nexport interface DatabaseData {\n [key: string]: unknown;\n}\n\n/**\n * Database configuration options\n */\nexport interface DatabaseOptions {\n idField?: string;\n foreignKeySuffix?: string;\n}\n\n/**\n * Database class for managing JSON data with lowdb\n */\nexport class Database {\n private db: Low<DatabaseData>;\n private filePath: string;\n private options: Required<DatabaseOptions>;\n\n /**\n * Creates a new Database instance\n *\n * @param source - Path to JSON file or object with data\n * @param options - Database configuration options\n *\n * @example\n * ```typescript\n * const db = new Database('db.json', { idField: 'id' });\n * await db.init();\n * ```\n */\n constructor(source: string | DatabaseData, options: DatabaseOptions = {}) {\n this.options = {\n idField: options.idField || 'id',\n foreignKeySuffix: options.foreignKeySuffix || 'Id',\n };\n\n if (typeof source === 'string') {\n this.filePath = resolve(source);\n const adapter = new JSONFile<DatabaseData>(this.filePath);\n this.db = new Low(adapter, {});\n } else {\n this.filePath = '';\n // For in-memory database with object data\n const adapter = new JSONFile<DatabaseData>(':memory:');\n this.db = new Low(adapter, source);\n }\n }\n\n /**\n * Initialize the database by reading from file or using provided data\n * Supports .json, .js, .ts, and .mjs files\n *\n * @throws Error if file doesn't exist or contains invalid data\n */\n async init(): Promise<void> {\n if (this.filePath && !existsSync(this.filePath)) {\n const ext = this.filePath.endsWith('.js') ? '.js' : '.json';\n throw new Error(\n `Database file not found: ${this.filePath}\\n` +\n `\\nMake sure the file exists and the path is correct.\\n` +\n `Expected format: ${ext === '.js' ? 'JavaScript module exporting data' : 'JSON file with data structure'}`\n );\n }\n\n // Check if file is a JavaScript/TypeScript module\n if (this.filePath) {\n const ext = extname(this.filePath).toLowerCase();\n\n if (ext === '.js' || ext === '.mjs' || ext === '.cjs' || ext === '.ts') {\n // Load JavaScript module\n try {\n const fileUrl = pathToFileURL(this.filePath).href;\n const module = (await import(fileUrl)) as { default?: unknown } & Record<string, unknown>;\n const data: unknown = module.default ?? module;\n\n // If it's a function, call it to get the data\n if (typeof data === 'function') {\n const result: unknown = await Promise.resolve((data as () => unknown)());\n if (typeof result !== 'object' || result === null) {\n throw new Error('JavaScript module function must return an object');\n }\n this.db.data = result as DatabaseData;\n } else if (typeof data === 'object' && data !== null) {\n this.db.data = data as DatabaseData;\n } else {\n throw new Error('JavaScript module must export an object or function');\n }\n } catch (error) {\n throw new Error(\n `Failed to load JavaScript module: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n } else {\n // Load JSON file\n await this.db.read();\n }\n } else {\n // In-memory database\n await this.db.read();\n }\n\n if (typeof this.db.data !== 'object') {\n this.db.data = {};\n }\n\n // Ensure all collections are arrays or objects\n for (const key in this.db.data) {\n const value = this.db.data[key];\n if (value !== null && typeof value !== 'object') {\n throw new Error(`Invalid data structure: ${key} must be an array or object`);\n }\n }\n }\n\n /**\n * Get all data from the database\n *\n * @returns Complete database data\n */\n getData(): DatabaseData {\n return this.db.data;\n }\n\n /**\n * Get a specific collection (array) or resource (object)\n *\n * @param name - Name of the collection/resource\n * @returns Collection array, resource object, or undefined\n */\n getCollection(name: string): unknown {\n return this.db.data[name];\n }\n\n /**\n * Get a single item from a collection by ID\n *\n * @param collectionName - Name of the collection\n * @param id - ID of the item\n * @returns The item or undefined\n */\n getById(collectionName: string, id: string | number): unknown {\n const collection = this.db.data[collectionName];\n\n if (!Array.isArray(collection)) {\n return undefined;\n }\n\n return collection.find((item) => {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n const record = item as Record<string, unknown>;\n return record[this.options.idField] === id || record[this.options.idField] === Number(id);\n });\n }\n\n /**\n * Generate next ID for a collection\n *\n * @param collectionName - Name of the collection\n * @returns Next available ID\n */\n generateId(collectionName: string): number {\n const collection = this.db.data[collectionName];\n\n if (!Array.isArray(collection)) {\n return 1;\n }\n\n let maxId = 0;\n for (const item of collection) {\n if (typeof item === 'object' && item !== null) {\n const record = item as Record<string, unknown>;\n const idValue: unknown = record[this.options.idField];\n if (typeof idValue === 'number' && idValue > maxId) {\n maxId = idValue;\n }\n }\n }\n\n return maxId + 1;\n }\n\n /**\n * Create a new item in a collection\n *\n * @param collectionName - Name of the collection\n * @param data - Data to insert\n * @returns Created item with ID\n * @throws Error if collection is not an array\n */\n async create(\n collectionName: string,\n data: Record<string, unknown>\n ): Promise<Record<string, unknown>> {\n let collection = this.db.data[collectionName];\n\n // Create collection if it doesn't exist\n if (!collection) {\n collection = [];\n this.db.data[collectionName] = collection;\n }\n\n if (!Array.isArray(collection)) {\n throw new Error(`Cannot create in ${collectionName}: not a collection`);\n }\n\n // Generate ID if not provided or use provided ID\n const idValue: unknown =\n data[this.options.idField] !== undefined\n ? data[this.options.idField]\n : this.generateId(collectionName);\n\n // Check if ID already exists\n const existingItem = this.getById(collectionName, idValue as string | number);\n if (existingItem) {\n throw new Error(`Item with ${this.options.idField}=${String(idValue)} already exists`);\n }\n\n const newItem: Record<string, unknown> = { ...data, [this.options.idField]: idValue };\n collection.push(newItem);\n\n await this.save();\n return newItem;\n }\n\n /**\n * Update an item in a collection (full replacement)\n *\n * @param collectionName - Name of the collection\n * @param id - ID of the item\n * @param data - New data (ID field will be preserved)\n * @returns Updated item or undefined if not found\n */\n async update(\n collectionName: string,\n id: string | number,\n data: Record<string, unknown>\n ): Promise<Record<string, unknown> | undefined> {\n const collection = this.db.data[collectionName];\n\n if (!Array.isArray(collection)) {\n return undefined;\n }\n\n const index = collection.findIndex((item) => {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n const record = item as Record<string, unknown>;\n return record[this.options.idField] === id || record[this.options.idField] === Number(id);\n });\n\n if (index === -1) {\n return undefined;\n }\n\n // Preserve the ID (convert to number if it's a numeric string)\n const originalItem = collection[index] as Record<string, unknown>;\n const originalId: unknown = originalItem[this.options.idField];\n const updatedItem: Record<string, unknown> = { ...data, [this.options.idField]: originalId };\n collection[index] = updatedItem;\n\n await this.save();\n return updatedItem;\n }\n\n /**\n * Patch an item in a collection (partial update)\n *\n * @param collectionName - Name of the collection\n * @param id - ID of the item\n * @param data - Partial data to merge (ID field will be ignored)\n * @returns Updated item or undefined if not found\n */\n async patch(\n collectionName: string,\n id: string | number,\n data: Record<string, unknown>\n ): Promise<Record<string, unknown> | undefined> {\n const collection = this.db.data[collectionName];\n\n if (!Array.isArray(collection)) {\n return undefined;\n }\n\n const index = collection.findIndex((item) => {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n const record = item as Record<string, unknown>;\n return record[this.options.idField] === id || record[this.options.idField] === Number(id);\n });\n\n if (index === -1) {\n return undefined;\n }\n\n const currentItem = collection[index] as Record<string, unknown>;\n // Merge data but ignore ID field in the patch data\n const { [this.options.idField]: _ignoredId, ...patchData } = data;\n const patchedItem = { ...currentItem, ...patchData };\n\n collection[index] = patchedItem;\n\n await this.save();\n return patchedItem;\n }\n\n /**\n * Delete an item from a collection\n *\n * @param collectionName - Name of the collection\n * @param id - ID of the item\n * @returns true if deleted, false if not found\n */\n async delete(collectionName: string, id: string | number): Promise<boolean> {\n const collection = this.db.data[collectionName];\n\n if (!Array.isArray(collection)) {\n return false;\n }\n\n const index = collection.findIndex((item) => {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n const record = item as Record<string, unknown>;\n return record[this.options.idField] === id || record[this.options.idField] === Number(id);\n });\n\n if (index === -1) {\n return false;\n }\n\n collection.splice(index, 1);\n await this.save();\n return true;\n }\n\n /**\n * Update or create a singular resource\n *\n * @param resourceName - Name of the resource\n * @param data - Resource data\n * @returns Updated resource\n */\n async updateSingular(\n resourceName: string,\n data: Record<string, unknown>\n ): Promise<Record<string, unknown>> {\n this.db.data[resourceName] = data;\n await this.save();\n return data;\n }\n\n /**\n * Save the database to file\n */\n async save(): Promise<void> {\n await this.db.write();\n }\n\n /**\n * Check if a resource is a collection (array) or singular (object)\n *\n * @param name - Name of the resource\n * @returns true if collection, false if singular or doesn't exist\n */\n isCollection(name: string): boolean {\n return Array.isArray(this.db.data[name]);\n }\n\n /**\n * Get ID field name\n */\n getIdField(): string {\n return this.options.idField;\n }\n\n /**\n * Get foreign key suffix\n */\n getForeignKeySuffix(): string {\n return this.options.foreignKeySuffix;\n }\n}\n","import express, { Express } from 'express';\nimport cors from 'cors';\nimport compression from 'compression';\nimport { Database } from './database';\nimport { createRouter, RouterOptions } from './router';\nimport { createStaticMiddleware, createHomepageMiddleware, StaticOptions } from './static';\nimport { loadMiddlewares } from './loader';\nimport { loadRewriteRules, createRewriterMiddleware } from './rewriter';\nimport { logger } from './logger';\n\n/**\n * Server configuration options\n */\nexport interface ServerOptions extends RouterOptions, StaticOptions {\n port?: number;\n host?: string;\n noCors?: boolean;\n noGzip?: boolean;\n delay?: number;\n quiet?: boolean;\n routes?: string;\n middlewares?: string;\n}\n\n/**\n * Create Express server with API Faker\n *\n * @param db - Database instance\n * @param options - Server configuration options\n * @returns Express application\n *\n * @example\n * ```typescript\n * const db = new Database('db.json');\n * await db.init();\n * const app = await createServer(db, { port: 3000 });\n * ```\n */\nexport async function createServer(\n db: Database,\n options: Partial<ServerOptions> = {}\n): Promise<Express> {\n const app = express();\n\n // CORS\n if (!options.noCors) {\n app.use(cors());\n }\n\n // GZIP compression\n if (!options.noGzip) {\n app.use(compression());\n }\n\n // JSON body parser\n app.use(express.json());\n\n // Delay middleware (for testing/simulation)\n if (options.delay && options.delay > 0) {\n const delay = options.delay;\n app.use((_req, _res, next) => {\n setTimeout(() => {\n next();\n }, delay);\n });\n }\n\n // Request logger (unless quiet)\n if (!options.quiet) {\n app.use((req, _res, next) => {\n logger.request(req.method, req.url);\n next();\n });\n }\n\n // Custom middlewares (load before routes)\n if (options.middlewares) {\n try {\n const middlewares = await loadMiddlewares(options.middlewares);\n for (const middleware of middlewares) {\n app.use(middleware);\n }\n if (!options.quiet) {\n logger.success(`Loaded custom middlewares from ${options.middlewares}`);\n }\n } catch (error) {\n logger.error(\n `Failed to load middlewares: ${error instanceof Error ? error.message : String(error)}`\n );\n throw error;\n }\n }\n\n // Homepage middleware (must be before static to allow custom index.html)\n app.use(createHomepageMiddleware(options));\n\n // Static file server\n app.use(createStaticMiddleware(options));\n\n // Custom route rewriting (load before all routes)\n if (options.routes) {\n try {\n const rules = await loadRewriteRules(options.routes);\n const rewriter = createRewriterMiddleware(rules);\n app.use(rewriter);\n if (!options.quiet) {\n logger.success(`Loaded route rewrite rules from ${options.routes}`);\n }\n } catch (error) {\n logger.error(\n `Failed to load routes: ${error instanceof Error ? error.message : String(error)}`\n );\n throw error;\n }\n }\n\n // Special endpoint: /db (full database dump)\n app.get('/db', (_req, res) => {\n res.json(db.getData());\n });\n\n // API routes\n const router = createRouter(db, options);\n app.use(router);\n\n // 404 handler\n app.use((_req, res) => {\n res.status(404).json({ error: 'Not Found' });\n });\n\n return app;\n}\n\n/**\n * Start the server\n *\n * @param app - Express application\n * @param options - Server options (port, host)\n * @returns Server instance\n */\nexport function startServer(\n app: Express,\n options: Pick<ServerOptions, 'port' | 'host' | 'quiet'> = {}\n): ReturnType<Express['listen']> {\n const port = options.port || 3000;\n const host = options.host || 'localhost';\n\n return app.listen(port, host, () => {\n if (!options.quiet) {\n logger.banner([\n '🚀 API Faker is running!',\n '',\n ' Resources:',\n ` http://${host}:${String(port)}/`,\n '',\n ' Home:',\n ` http://${host}:${String(port)}`,\n ]);\n }\n });\n}\n","import { Router, Request, Response, NextFunction } from 'express';\nimport { Database } from './database';\nimport { logger } from './logger';\nimport { parseQuery, applyQuery, generateLinkHeader } from './query';\nimport { parseRelationships, applyRelationships, getForeignKey } from './relationships';\n\n/**\n * Helper to safely get route param (Express guarantees route params exist)\n */\nfunction getParam(req: Request, name: string): string {\n const value = req.params[name];\n if (value === undefined) {\n throw new Error(`Route parameter '${name}' is missing`);\n }\n return value;\n}\n\n/**\n * Router configuration options\n */\nexport interface RouterOptions {\n idField?: string;\n foreignKeySuffix?: string;\n readOnly?: boolean;\n}\n\n/**\n * Create API Faker router with CRUD operations\n *\n * @param db - Database instance\n * @param options - Router configuration options\n * @returns Express router\n *\n * @example\n * ```typescript\n * const db = new Database('db.json');\n * await db.init();\n * const router = createRouter(db);\n * app.use(router);\n * ```\n */\nexport function createRouter(db: Database, options: Partial<RouterOptions> = {}): Router {\n const router = Router();\n const readOnly = options.readOnly || false;\n const idField = options.idField || 'id';\n const foreignKeySuffix = options.foreignKeySuffix || 'Id';\n\n /**\n * Validate Content-Type for write operations\n */\n const validateContentType = (req: Request, _res: Response, next: NextFunction): void => {\n const contentType = req.get('Content-Type');\n if (!contentType || !contentType.includes('application/json')) {\n // Express still parses but we should warn about missing header\n // In real json-server, this would still work but without actual data modification\n logger.warn('Content-Type should be application/json');\n }\n next();\n };\n\n /**\n * GET /db - Return entire database\n */\n router.get('/db', (_req: Request, res: Response) => {\n res.json(db.getData());\n });\n\n /**\n * GET /:resource - Get all items in a collection or singular resource\n */\n router.get('/:resource', (req: Request, res: Response): void => {\n const resource = getParam(req, 'resource');\n const data = db.getCollection(resource);\n\n if (data === undefined) {\n res.status(404).json({ error: `Resource '${resource}' not found` });\n return;\n }\n // For collections (arrays), apply query parameters\n if (Array.isArray(data)) {\n const queryOptions = parseQuery(req);\n const { data: filtered, total } = applyQuery(data, queryOptions);\n\n // Apply relationships (_embed, _expand)\n const { embed, expand } = parseRelationships(req.query as Record<string, unknown>);\n const withRelationships = applyRelationships(\n filtered as Record<string, unknown>[],\n resource,\n embed,\n expand,\n db,\n idField,\n foreignKeySuffix\n );\n\n // Add X-Total-Count header\n res.set('X-Total-Count', String(total));\n\n // Add Link header for pagination\n if (queryOptions.page !== undefined && queryOptions.limit !== undefined) {\n const linkHeader = generateLinkHeader(req, queryOptions.page, queryOptions.limit, total);\n res.set('Link', linkHeader);\n }\n\n res.json(withRelationships);\n return;\n }\n\n // For singular resources (objects), return as-is\n res.json(data);\n });\n\n /**\n * GET /:resource/:id - Get single item by ID\n */\n router.get('/:resource/:id', (req: Request, res: Response): void => {\n const resource = getParam(req, 'resource');\n const id = getParam(req, 'id');\n\n // Check if resource is a collection\n if (!db.isCollection(resource)) {\n res.status(404).json({ error: `Collection '${resource}' not found` });\n return;\n }\n\n const item = db.getById(resource, id);\n\n if (!item) {\n res.status(404).json({ error: `Item with id '${id}' not found in '${resource}'` });\n return;\n }\n\n // Apply relationships if requested\n const { embed, expand } = parseRelationships(req.query as Record<string, unknown>);\n if (embed.length > 0 || expand.length > 0) {\n const withRelationships = applyRelationships(\n [item as Record<string, unknown>],\n resource,\n embed,\n expand,\n db,\n idField,\n foreignKeySuffix\n );\n res.json(withRelationships[0]);\n return;\n }\n\n res.json(item);\n });\n\n /**\n * GET /:parent/:parentId/:children - Get nested children\n */\n router.get('/:parent/:parentId/:children', (req: Request, res: Response): void => {\n const parent = getParam(req, 'parent');\n const parentId = getParam(req, 'parentId');\n const children = getParam(req, 'children');\n\n // Verify parent exists\n if (!db.isCollection(parent)) {\n res.status(404).json({ error: `Collection '${parent}' not found` });\n return;\n }\n\n const parentItem = db.getById(parent, parentId);\n if (!parentItem) {\n res.status(404).json({ error: `Parent item with id '${parentId}' not found in '${parent}'` });\n return;\n }\n\n // Get children collection\n const childrenData = db.getCollection(children);\n if (!Array.isArray(childrenData)) {\n res.status(404).json({ error: `Collection '${children}' not found` });\n return;\n }\n\n // Filter children by parent foreign key\n const foreignKey = getForeignKey(parent, foreignKeySuffix);\n const filtered = childrenData.filter((child) => {\n if (typeof child !== 'object' || child === null) return false;\n const childFk = (child as Record<string, unknown>)[foreignKey];\n return (\n childFk === parentId ||\n (typeof childFk === 'number' || typeof childFk === 'string'\n ? String(childFk) === parentId\n : false)\n );\n });\n\n // Apply query parameters\n const queryOptions = parseQuery(req);\n const { data: result, total } = applyQuery(filtered, queryOptions);\n\n res.set('X-Total-Count', String(total));\n res.json(result);\n });\n\n /**\n * POST /:parent/:parentId/:children - Create nested child\n */\n router.post(\n '/:parent/:parentId/:children',\n validateContentType,\n async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const parent = getParam(req, 'parent');\n const parentId = getParam(req, 'parentId');\n const children = getParam(req, 'children');\n const data = req.body as Record<string, unknown>;\n\n if (typeof data !== 'object') {\n return res.status(400).json({ error: 'Request body must be a JSON object' });\n }\n\n // Verify parent exists\n if (!db.isCollection(parent)) {\n return res.status(404).json({ error: `Collection '${parent}' not found` });\n }\n\n const parentItem = db.getById(parent, parentId);\n if (!parentItem) {\n return res\n .status(404)\n .json({ error: `Parent item with id '${parentId}' not found in '${parent}'` });\n }\n\n // Auto-set foreign key\n const foreignKey = getForeignKey(parent, foreignKeySuffix);\n data[foreignKey] = parentId;\n\n try {\n const created = await db.create(children, data);\n return res.status(201).json(created);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return res.status(400).json({ error: message });\n }\n }\n );\n\n /**\n * POST /:resource - Create new item\n */\n router.post('/:resource', validateContentType, async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const resource = getParam(req, 'resource');\n const data = req.body as Record<string, unknown>;\n\n if (typeof data !== 'object') {\n return res.status(400).json({ error: 'Request body must be a JSON object' });\n }\n\n try {\n // Handle singular resources\n if (!db.isCollection(resource) && db.getCollection(resource) !== undefined) {\n const updated = await db.updateSingular(resource, data);\n return res.status(200).json(updated);\n }\n\n // Create in collection\n const created = await db.create(resource, data);\n return res.status(201).json(created);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return res.status(400).json({ error: message });\n }\n });\n\n /**\n * PUT /:resource/:id - Full update of item\n */\n router.put('/:resource/:id', validateContentType, async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const resource = getParam(req, 'resource');\n const id = getParam(req, 'id');\n const data = req.body as Record<string, unknown>;\n\n if (typeof data !== 'object') {\n return res.status(400).json({ error: 'Request body must be a JSON object' });\n }\n\n if (!db.isCollection(resource)) {\n return res.status(404).json({ error: `Collection '${resource}' not found` });\n }\n\n try {\n const updated = await db.update(resource, id, data);\n\n if (!updated) {\n return res.status(404).json({ error: `Item with id '${id}' not found in '${resource}'` });\n }\n\n return res.json(updated);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return res.status(400).json({ error: message });\n }\n });\n\n /**\n * PATCH /:resource/:id - Partial update of item\n */\n router.patch('/:resource/:id', validateContentType, async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const resource = getParam(req, 'resource');\n const id = getParam(req, 'id');\n const data = req.body as Record<string, unknown>;\n\n if (typeof data !== 'object') {\n return res.status(400).json({ error: 'Request body must be a JSON object' });\n }\n\n if (!db.isCollection(resource)) {\n return res.status(404).json({ error: `Collection '${resource}' not found` });\n }\n\n try {\n const patched = await db.patch(resource, id, data);\n\n if (!patched) {\n return res.status(404).json({ error: `Item with id '${id}' not found in '${resource}'` });\n }\n\n return res.json(patched);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return res.status(400).json({ error: message });\n }\n });\n\n /**\n * PUT /:resource - Full update of singular resource\n */\n router.put('/:resource', validateContentType, async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const resource = getParam(req, 'resource');\n const data = req.body as Record<string, unknown>;\n\n if (typeof data !== 'object') {\n return res.status(400).json({ error: 'Request body must be a JSON object' });\n }\n\n // Only allow for singular resources (objects, not arrays)\n if (db.isCollection(resource)) {\n return res\n .status(400)\n .json({\n error: `Cannot PUT to collection '${resource}'. Use POST or PUT /${resource}/:id`,\n });\n }\n\n try {\n const updated = await db.updateSingular(resource, data);\n return res.json(updated);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return res.status(400).json({ error: message });\n }\n });\n\n /**\n * PATCH /:resource - Partial update of singular resource\n */\n router.patch('/:resource', validateContentType, async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const resource = getParam(req, 'resource');\n const data = req.body as Record<string, unknown>;\n\n if (typeof data !== 'object') {\n return res.status(400).json({ error: 'Request body must be a JSON object' });\n }\n\n // Only allow for singular resources\n if (db.isCollection(resource)) {\n return res\n .status(400)\n .json({ error: `Cannot PATCH collection '${resource}'. Use PATCH /${resource}/:id` });\n }\n\n const current = db.getCollection(resource) as Record<string, unknown> | undefined;\n\n if (!current || typeof current !== 'object') {\n return res.status(404).json({ error: `Resource '${resource}' not found` });\n }\n\n try {\n const merged = { ...current, ...data };\n const updated = await db.updateSingular(resource, merged);\n return res.json(updated);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return res.status(400).json({ error: message });\n }\n });\n\n /**\n * DELETE /:resource/:id - Delete item by ID\n */\n router.delete('/:resource/:id', async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const resource = getParam(req, 'resource');\n const id = getParam(req, 'id');\n\n if (!db.isCollection(resource)) {\n return res.status(404).json({ error: `Collection '${resource}' not found` });\n }\n\n const deleted = await db.delete(resource, id);\n\n if (!deleted) {\n return res.status(404).json({ error: `Item with id '${id}' not found in '${resource}'` });\n }\n\n // Return 204 No Content\n return res.status(204).send();\n });\n\n return router;\n}\n","import pc from 'picocolors';\n\n/**\n * Logger utility for colored console output\n *\n * Provides consistent, color-coded logging throughout the application.\n * Follows a standard format: [LEVEL] Message\n */\nexport const logger = {\n /**\n * Log success message in green\n *\n * @param message - Success message\n *\n * @example\n * ```typescript\n * logger.success('Server started successfully');\n * // Output: ✓ Server started successfully (in green)\n * ```\n */\n success(message: string): void {\n console.log(pc.green(`✓ ${message}`));\n },\n\n /**\n * Log error message in red\n *\n * @param message - Error message\n *\n * @example\n * ```typescript\n * logger.error('Failed to load file');\n * // Output: ✗ Failed to load file (in red)\n * ```\n */\n error(message: string): void {\n console.error(pc.red(`✗ ${message}`));\n },\n\n /**\n * Log warning message in yellow\n *\n * @param message - Warning message\n *\n * @example\n * ```typescript\n * logger.warn('Using default port');\n * // Output: ⚠ Using default port (in yellow)\n * ```\n */\n warn(message: string): void {\n console.warn(pc.yellow(`⚠ ${message}`));\n },\n\n /**\n * Log info message in cyan\n *\n * @param message - Info message\n *\n * @example\n * ```typescript\n * logger.info('Watching for changes...');\n * // Output: ℹ Watching for changes... (in cyan)\n * ```\n */\n info(message: string): void {\n console.log(pc.cyan(`ℹ ${message}`));\n },\n\n /**\n * Log plain message without color\n *\n * @param message - Message to log\n *\n * @example\n * ```typescript\n * logger.log('http://localhost:3000');\n * // Output: http://localhost:3000\n * ```\n */\n log(message: string): void {\n console.log(message);\n },\n\n /**\n * Log request in gray (for non-quiet mode)\n *\n * @param method - HTTP method\n * @param url - Request URL\n *\n * @example\n * ```typescript\n * logger.request('GET', '/api/users');\n * // Output: [timestamp] GET /api/users (in gray)\n * ```\n */\n request(method: string, url: string): void {\n const timestamp = new Date().toLocaleTimeString();\n console.log(pc.gray(`[${timestamp}] ${method} ${url}`));\n },\n\n /**\n * Log formatted banner\n *\n * @param lines - Array of lines to display\n *\n * @example\n * ```typescript\n * logger.banner([\n * '🚀 API Faker is running!',\n * '',\n * ' Resources:',\n * ' http://localhost:3000/'\n * ]);\n * ```\n */\n banner(lines: string[]): void {\n console.log();\n for (const line of lines) {\n // Color URLs in cyan\n if (line.includes('http://') || line.includes('https://')) {\n const colored = line.replace(/(https?:\\/\\/[^\\s]+)/g, (url) => pc.cyan(url));\n console.log(colored);\n } else if (line.startsWith('🚀')) {\n // Color rocket emoji line in bold\n console.log(pc.bold(line));\n } else {\n console.log(line);\n }\n }\n console.log();\n },\n};\n","/**\n * Query processing module for filtering, sorting, pagination, etc.\n */\n\nimport type { Request } from 'express';\n\n/**\n * Query options extracted from request\n */\nexport interface QueryOptions {\n filters: Record<string, string | string[]>;\n operators: Record<string, Record<string, string>>;\n sort: string[];\n order: string[];\n page?: number;\n limit?: number;\n start?: number;\n end?: number;\n q?: string;\n}\n\n/**\n * Parse query parameters from request\n *\n * @param req - Express request object\n * @returns Parsed query options\n *\n * @example\n * parseQuery(req) // { filters: { title: 'value' }, sort: ['id'], order: ['asc'] }\n */\nexport function parseQuery(req: Request): QueryOptions {\n const query = req.query;\n const filters: Record<string, string | string[]> = {};\n const operators: Record<string, Record<string, string>> = {};\n const sort: string[] = [];\n const order: string[] = [];\n let page: number | undefined;\n let limit: number | undefined;\n let start: number | undefined;\n let end: number | undefined;\n let q: string | undefined;\n\n for (const [key, value] of Object.entries(query)) {\n // Skip non-string and non-array values\n const stringValue =\n typeof value === 'string'\n ? value\n : Array.isArray(value) && value.length > 0 && typeof value[0] === 'string'\n ? value[0]\n : null;\n\n if (stringValue === null && !Array.isArray(value)) {\n continue;\n }\n\n // Special query parameters\n if (key === '_sort') {\n if (typeof value === 'string') {\n sort.push(...value.split(','));\n } else if (Array.isArray(value)) {\n for (const v of value) {\n if (typeof v === 'string') {\n sort.push(v);\n }\n }\n }\n continue;\n }\n\n if (key === '_order') {\n if (typeof value === 'string') {\n order.push(...value.split(','));\n } else if (Array.isArray(value)) {\n for (const v of value) {\n if (typeof v === 'string') {\n order.push(v);\n }\n }\n }\n continue;\n }\n\n if (key === '_page' && stringValue) {\n const parsed = parseInt(stringValue, 10);\n if (!isNaN(parsed) && parsed > 0) {\n page = parsed;\n }\n continue;\n }\n\n if (key === '_limit' && stringValue) {\n const parsed = parseInt(stringValue, 10);\n if (!isNaN(parsed) && parsed > 0) {\n limit = parsed;\n }\n continue;\n }\n\n if (key === '_start' && stringValue) {\n const parsed = parseInt(stringValue, 10);\n if (!isNaN(parsed) && parsed >= 0) {\n start = parsed;\n }\n continue;\n }\n\n if (key === '_end' && stringValue) {\n const parsed = parseInt(stringValue, 10);\n if (!isNaN(parsed) && parsed >= 0) {\n end = parsed;\n }\n continue;\n }\n\n if (key === 'q' && stringValue) {\n q = stringValue;\n continue;\n }\n\n // Operator filters (_gte, _lte, _ne, _like)\n const operatorMatch = key.match(/^(.+)_(gte|lte|ne|like)$/);\n if (operatorMatch && stringValue) {\n const field = operatorMatch[1];\n const operator = operatorMatch[2];\n\n if (field && operator) {\n if (!operators[field]) {\n operators[field] = {};\n }\n operators[field][operator] = stringValue;\n }\n continue;\n }\n\n // Regular filters\n if (typeof value === 'string') {\n filters[key] = value;\n } else if (Array.isArray(value)) {\n const stringValues = value.filter((v): v is string => typeof v === 'string');\n if (stringValues.length > 0) {\n filters[key] = stringValues;\n }\n }\n }\n\n const result: QueryOptions = {\n filters,\n operators,\n sort,\n order,\n };\n\n if (page !== undefined) result.page = page;\n if (limit !== undefined) result.limit = limit;\n if (start !== undefined) result.start = start;\n if (end !== undefined) result.end = end;\n if (q !== undefined) result.q = q;\n\n return result;\n}\n\n/**\n * Get nested property value from object\n *\n * @param obj - Object to get property from\n * @param path - Dot-separated property path\n * @returns Property value or undefined\n *\n * @example\n * getNestedValue({ user: { name: 'John' } }, 'user.name') // 'John'\n */\nfunction getNestedValue(obj: unknown, path: string): unknown {\n if (typeof obj !== 'object' || obj === null) {\n return undefined;\n }\n\n const keys = path.split('.');\n let current: unknown = obj;\n\n for (const key of keys) {\n if (typeof current !== 'object' || current === null || !(key in current)) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[key];\n }\n\n return current;\n}\n\n/**\n * Check if item matches filter criteria\n *\n * @param item - Item to check\n * @param filters - Filter criteria\n * @returns True if item matches all filters\n *\n * @example\n * matchesFilters({ title: 'test' }, { title: 'test' }) // true\n */\nfunction matchesFilters(item: unknown, filters: Record<string, string | string[]>): boolean {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n\n for (const [key, filterValue] of Object.entries(filters)) {\n const itemValue = getNestedValue(item, key);\n\n if (Array.isArray(filterValue)) {\n // Multiple values - item must match at least one\n const matched = filterValue.some((val) => String(itemValue) === val);\n if (!matched) {\n return false;\n }\n } else {\n // Single value - exact match\n if (String(itemValue) !== filterValue) {\n return false;\n }\n }\n }\n\n return true;\n}\n\n/**\n * Check if item matches operator criteria\n *\n * @param item - Item to check\n * @param operators - Operator criteria\n * @returns True if item matches all operators\n *\n * @example\n * matchesOperators({ age: 25 }, { age: { _gte: '18' } }) // true\n */\nfunction matchesOperators(\n item: unknown,\n operators: Record<string, Record<string, string>>\n): boolean {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n\n for (const [key, ops] of Object.entries(operators)) {\n const itemValue = getNestedValue(item, key);\n\n for (const [operator, filterValue] of Object.entries(ops)) {\n if (operator === 'gte') {\n if (Number(itemValue) < Number(filterValue)) {\n return false;\n }\n } else if (operator === 'lte') {\n if (Number(itemValue) > Number(filterValue)) {\n return false;\n }\n } else if (operator === 'ne') {\n if (String(itemValue) === filterValue) {\n return false;\n }\n } else if (operator === 'like') {\n const itemStr = String(itemValue).toLowerCase();\n const filterStr = filterValue.toLowerCase();\n if (!itemStr.includes(filterStr)) {\n return false;\n }\n }\n }\n }\n\n return true;\n}\n\n/**\n * Check if item matches full-text search\n *\n * @param item - Item to check\n * @param searchText - Text to search for\n * @returns True if any string property contains search text\n *\n * @example\n * matchesSearch({ title: 'Hello World' }, 'world') // true\n */\nfunction matchesSearch(item: unknown, searchText: string): boolean {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n\n const lowerSearch = searchText.toLowerCase();\n\n function searchObject(obj: unknown): boolean {\n if (typeof obj === 'string') {\n return obj.toLowerCase().includes(lowerSearch);\n }\n\n if (typeof obj === 'object' && obj !== null) {\n return Object.values(obj).some(searchObject);\n }\n\n return false;\n }\n\n return searchObject(item);\n}\n\n/**\n * Apply filtering, sorting, pagination, etc. to data array\n *\n * @param data - Array of items to process\n * @param options - Query options\n * @returns Processed array and total count (before pagination)\n *\n * @example\n * applyQuery([{ id: 1 }, { id: 2 }], { limit: 1 }) // { data: [{ id: 1 }], total: 2 }\n */\nexport function applyQuery<T>(data: T[], options: QueryOptions): { data: T[]; total: number } {\n let result = [...data];\n\n // Apply full-text search\n if (options.q) {\n const searchText = options.q;\n result = result.filter((item) => matchesSearch(item, searchText));\n }\n\n // Apply filters\n if (Object.keys(options.filters).length > 0) {\n result = result.filter((item) => matchesFilters(item, options.filters));\n }\n\n // Apply operators\n if (Object.keys(options.operators).length > 0) {\n result = result.filter((item) => matchesOperators(item, options.operators));\n }\n\n // Store total before pagination\n const total = result.length;\n\n // Apply sorting\n if (options.sort.length > 0) {\n const sortFields = options.sort;\n const sortOrders = options.order;\n\n result.sort((a, b) => {\n for (let i = 0; i < sortFields.length; i++) {\n const field = sortFields[i];\n if (!field) continue;\n\n const order = sortOrders[i] === 'desc' ? -1 : 1;\n\n const aVal = getNestedValue(a, field);\n const bVal = getNestedValue(b, field);\n\n if (aVal === bVal) continue;\n\n // Handle null/undefined\n if (aVal === undefined || aVal === null) return 1 * order;\n if (bVal === undefined || bVal === null) return -1 * order;\n\n // Compare values\n if (aVal < bVal) return -1 * order;\n if (aVal > bVal) return 1 * order;\n }\n return 0;\n });\n }\n\n // Apply slicing (_start and _end)\n if (options.start !== undefined || options.end !== undefined) {\n const start = options.start ?? 0;\n const end = options.end ?? result.length;\n result = result.slice(start, end);\n }\n // Apply pagination (_page and _limit)\n else if (options.page !== undefined && options.limit !== undefined) {\n const start = (options.page - 1) * options.limit;\n result = result.slice(start, start + options.limit);\n }\n // Apply limit only\n else if (options.limit !== undefined) {\n result = result.slice(0, options.limit);\n }\n\n return { data: result, total };\n}\n\n/**\n * Generate Link header for pagination\n *\n * @param req - Express request object\n * @param page - Current page\n * @param limit - Items per page\n * @param total - Total number of items\n * @returns Link header value\n *\n * @example\n * generateLinkHeader(req, 2, 10, 100) // '<...>; rel=\"first\", <...>; rel=\"prev\", ...'\n */\nexport function generateLinkHeader(\n req: Request,\n page: number,\n limit: number,\n total: number\n): string {\n const host = req.get('host') ?? 'localhost';\n const baseUrl = `${req.protocol}://${host}${req.path}`;\n const query = new URLSearchParams(req.query as Record<string, string>);\n const lastPage = Math.ceil(total / limit);\n\n const links: string[] = [];\n\n // First page\n query.set('_page', '1');\n query.set('_limit', String(limit));\n links.push(`<${baseUrl}?${query.toString()}>; rel=\"first\"`);\n\n // Previous page\n if (page > 1) {\n query.set('_page', String(page - 1));\n links.push(`<${baseUrl}?${query.toString()}>; rel=\"prev\"`);\n }\n\n // Next page\n if (page < lastPage) {\n query.set('_page', String(page + 1));\n links.push(`<${baseUrl}?${query.toString()}>; rel=\"next\"`);\n }\n\n // Last page\n query.set('_page', String(lastPage));\n links.push(`<${baseUrl}?${query.toString()}>; rel=\"last\"`);\n\n return links.join(', ');\n}\n","/**\n * Relationships module for handling _embed and _expand parameters\n */\n\nimport type { Database } from './database';\n\n/**\n * Detect foreign key relationships based on naming conventions\n *\n * @param collectionName - Name of the collection (e.g., 'posts')\n * @param foreignKeySuffix - Suffix for foreign keys (default: 'Id')\n * @returns Possible foreign key name (e.g., 'postId')\n *\n * @example\n * getForeignKey('posts', 'Id') // 'postId'\n * getForeignKey('users', '_id') // 'user_id'\n */\nexport function getForeignKey(collectionName: string, foreignKeySuffix: string): string {\n // Remove trailing 's' for singular form\n const singular = collectionName.endsWith('s') ? collectionName.slice(0, -1) : collectionName;\n\n return `${singular}${foreignKeySuffix}`;\n}\n\n/**\n * Embed child resources into parent items\n *\n * @param items - Parent items to embed children into\n * @param parentCollection - Name of parent collection\n * @param childCollection - Name of child collection\n * @param db - Database instance\n * @param idField - ID field name (default: 'id')\n * @param foreignKeySuffix - Foreign key suffix (default: 'Id')\n * @returns Items with embedded children\n *\n * @example\n * embedChildren([{ id: 1 }], 'posts', 'comments', db)\n * // [{ id: 1, comments: [{ id: 1, postId: 1, body: '...' }] }]\n */\nexport function embedChildren<T extends Record<string, unknown>>(\n items: T[],\n parentCollection: string,\n childCollection: string,\n db: Database,\n idField: string,\n foreignKeySuffix: string\n): T[] {\n const children = db.getCollection(childCollection);\n\n // If children collection doesn't exist or isn't an array, return items unchanged\n if (!Array.isArray(children)) {\n return items;\n }\n\n const foreignKey = getForeignKey(parentCollection, foreignKeySuffix);\n\n return items.map((item) => {\n // Find all children that reference this parent\n const matchingChildren = children.filter((child) => {\n if (typeof child !== 'object' || child === null) return false;\n const childFk = (child as Record<string, unknown>)[foreignKey];\n const parentId = item[idField];\n return childFk === parentId || String(childFk) === String(parentId);\n });\n\n return {\n ...item,\n [childCollection]: matchingChildren,\n };\n });\n}\n\n/**\n * Expand parent resource into child items\n *\n * @param items - Child items to expand parent into\n * @param childCollection - Name of child collection\n * @param parentCollection - Name of parent collection\n * @param db - Database instance\n * @param idField - ID field name (default: 'id')\n * @param foreignKeySuffix - Foreign key suffix (default: 'Id')\n * @returns Items with expanded parent\n *\n * @example\n * expandParent([{ id: 1, postId: 1 }], 'comments', 'posts', db)\n * // [{ id: 1, postId: 1, post: { id: 1, title: '...' } }]\n */\nexport function expandParent<T extends Record<string, unknown>>(\n items: T[],\n _childCollection: string,\n parentCollection: string,\n db: Database,\n idField: string,\n foreignKeySuffix: string\n): T[] {\n const parents = db.getCollection(parentCollection);\n\n // If parent collection doesn't exist or isn't an array, return items unchanged\n if (!Array.isArray(parents)) {\n return items;\n }\n\n const foreignKey = getForeignKey(parentCollection, foreignKeySuffix);\n\n return items.map((item) => {\n const foreignKeyValue: unknown = item[foreignKey];\n\n if (foreignKeyValue === undefined) {\n return item;\n }\n\n // Find the parent that matches this foreign key\n const parent: unknown = parents.find((p) => {\n if (typeof p !== 'object' || p === null) return false;\n const parentRecord = p as Record<string, unknown>;\n const parentId: unknown = parentRecord[idField];\n // Handle both numeric and string IDs\n if (typeof foreignKeyValue === 'number' || typeof foreignKeyValue === 'string') {\n return parentId === foreignKeyValue || String(parentId) === String(foreignKeyValue);\n }\n return false;\n });\n\n if (!parent || typeof parent !== 'object') {\n return item;\n }\n\n // Use singular form for parent property name\n const parentPropName = parentCollection.endsWith('s')\n ? parentCollection.slice(0, -1)\n : parentCollection;\n\n return {\n ...item,\n [parentPropName]: parent as Record<string, unknown>,\n };\n });\n}\n\n/**\n * Apply relationship parameters (_embed, _expand) to query results\n *\n * @param data - Query results\n * @param resource - Resource name\n * @param embed - Collections to embed\n * @param expand - Collections to expand\n * @param db - Database instance\n * @param idField - ID field name\n * @param foreignKeySuffix - Foreign key suffix\n * @returns Data with relationships applied\n *\n * @example\n * applyRelationships(posts, 'posts', ['comments'], [], db)\n */\nexport function applyRelationships<T extends Record<string, unknown>>(\n data: T[],\n resource: string,\n embed: string[],\n expand: string[],\n db: Database,\n idField: string,\n foreignKeySuffix: string\n): T[] {\n let result = data;\n\n // Apply embeds (include children)\n for (const childCollection of embed) {\n result = embedChildren(result, resource, childCollection, db, idField, foreignKeySuffix);\n }\n\n // Apply expands (include parent)\n for (const parentCollection of expand) {\n result = expandParent(result, resource, parentCollection, db, idField, foreignKeySuffix);\n }\n\n return result;\n}\n\n/**\n * Parse relationship parameters from query\n *\n * @param query - Express query object\n * @returns Arrays of collections to embed and expand\n *\n * @example\n * parseRelationships({ _embed: 'comments' }) // { embed: ['comments'], expand: [] }\n * parseRelationships({ _embed: ['comments', 'likes'] }) // { embed: ['comments', 'likes'], expand: [] }\n */\nexport function parseRelationships(query: Record<string, unknown>): {\n embed: string[];\n expand: string[];\n} {\n const embed: string[] = [];\n const expand: string[] = [];\n\n // Parse _embed\n const embedParam = query._embed;\n if (typeof embedParam === 'string') {\n embed.push(...embedParam.split(','));\n } else if (Array.isArray(embedParam)) {\n for (const e of embedParam) {\n if (typeof e === 'string') {\n embed.push(e);\n }\n }\n }\n\n // Parse _expand\n const expandParam = query._expand;\n if (typeof expandParam === 'string') {\n expand.push(...expandParam.split(','));\n } else if (Array.isArray(expandParam)) {\n for (const e of expandParam) {\n if (typeof e === 'string') {\n expand.push(e);\n }\n }\n }\n\n return { embed, expand };\n}\n","/**\n * Static file server module\n */\n\nimport express, { RequestHandler } from 'express';\nimport { existsSync } from 'fs';\nimport { resolve } from 'path';\n\n/**\n * Static server options\n */\nexport interface StaticOptions {\n directory?: string;\n enabled?: boolean;\n}\n\n/**\n * Create static file server middleware\n *\n * @param options - Static server configuration\n * @returns Express middleware for serving static files\n *\n * @example\n * ```typescript\n * const staticMiddleware = createStaticMiddleware({ directory: './public' });\n * app.use(staticMiddleware);\n * ```\n */\nexport function createStaticMiddleware(options: StaticOptions = {}): RequestHandler {\n const directory = options.directory || './public';\n const enabled = options.enabled !== false;\n const staticPath = resolve(process.cwd(), directory);\n\n // If disabled or directory doesn't exist, return pass-through middleware\n if (!enabled || !existsSync(staticPath)) {\n return (_req, _res, next) => {\n next();\n };\n }\n\n // Serve static files from the directory\n return express.static(staticPath, {\n index: 'index.html',\n dotfiles: 'ignore',\n redirect: true,\n });\n}\n\n/**\n * Create homepage middleware that serves index.html or shows default page\n *\n * @param options - Static server configuration\n * @returns Express request handler\n */\nexport function createHomepageMiddleware(options: StaticOptions = {}): RequestHandler {\n const directory = options.directory || './public';\n const enabled = options.enabled !== false;\n const staticPath = resolve(process.cwd(), directory);\n const indexPath = resolve(staticPath, 'index.html');\n\n return (req, res, next) => {\n // Only handle root path\n if (req.path !== '/') {\n next();\n return;\n }\n\n // If static serving is enabled and index.html exists, let static middleware handle it\n if (enabled && existsSync(indexPath)) {\n next();\n return;\n }\n\n // Otherwise, show default homepage with available routes\n const host = req.get('host') || 'localhost:3000';\n const protocol = req.protocol;\n const baseUrl = `${protocol}://${host}`;\n\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.send(`<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>API Faker</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n line-height: 1.6;\n color: #333;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n min-height: 100vh;\n padding: 2rem;\n }\n .container {\n max-width: 800px;\n margin: 0 auto;\n background: white;\n border-radius: 12px;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n padding: 3rem;\n }\n h1 {\n color: #667eea;\n font-size: 2.5rem;\n margin-bottom: 1rem;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n h2 {\n color: #555;\n font-size: 1.5rem;\n margin-top: 2rem;\n margin-bottom: 1rem;\n border-bottom: 2px solid #667eea;\n padding-bottom: 0.5rem;\n }\n .intro {\n color: #666;\n font-size: 1.1rem;\n margin-bottom: 2rem;\n }\n .endpoint {\n background: #f8f9fa;\n border-left: 4px solid #667eea;\n padding: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n }\n .endpoint a {\n color: #667eea;\n text-decoration: none;\n font-family: 'Monaco', 'Courier New', monospace;\n font-weight: bold;\n }\n .endpoint a:hover {\n text-decoration: underline;\n }\n .endpoint p {\n color: #666;\n margin-top: 0.5rem;\n font-size: 0.95rem;\n }\n .info-box {\n background: #e3f2fd;\n border-left: 4px solid #2196f3;\n padding: 1rem;\n margin-top: 2rem;\n border-radius: 4px;\n }\n .info-box p {\n color: #1565c0;\n margin: 0;\n }\n code {\n background: #f5f5f5;\n padding: 0.2rem 0.4rem;\n border-radius: 3px;\n font-family: 'Monaco', 'Courier New', monospace;\n font-size: 0.9em;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <h1>🚀 API Faker</h1>\n <p class=\"intro\">\n Your JSON REST API is up and running! Use the endpoints below to interact with your data.\n </p>\n\n <h2>📡 Endpoints</h2>\n <div class=\"endpoint\">\n <a href=\"${baseUrl}/db\" target=\"_blank\">${baseUrl}/db</a>\n <p>View the full database</p>\n </div>\n\n <h2>💡 Tips</h2>\n <div class=\"info-box\">\n <p>\n Use query parameters to filter, sort, and paginate your data. \n Examples: <code>?_sort=name&_order=asc</code>, <code>?_page=1&_limit=10</code>\n </p>\n </div>\n </div>\n</body>\n</html>`);\n };\n}\n","import { pathToFileURL } from 'node:url';\nimport { RequestHandler } from 'express';\n\n/**\n * Load a JavaScript or TypeScript module dynamically\n *\n * @param filePath - Path to the module file\n * @returns Module exports\n * @throws Error if module cannot be loaded\n *\n * @example\n * ```typescript\n * const module = await loadModule('./routes.js');\n * ```\n */\nexport async function loadModule(filePath: string): Promise<unknown> {\n try {\n const fileUrl = pathToFileURL(filePath).href;\n const module = (await import(fileUrl)) as { default?: unknown } & Record<string, unknown>;\n return module;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Failed to load module '${filePath}': ${message}\\n` +\n `\\nMake sure:\\n` +\n ` - The file exists and is a valid JavaScript file\\n` +\n ` - The file doesn't have syntax errors\\n` +\n ` - All dependencies are installed`\n );\n }\n}\n\n/**\n * Load custom middleware from a file\n *\n * @param filePath - Path to the middleware file\n * @returns Array of Express middleware functions\n * @throws Error if middleware file is invalid\n *\n * @example\n * ```typescript\n * // middleware.js\n * module.exports = (req, res, next) => {\n * console.log('Custom middleware');\n * next();\n * };\n *\n * // Or export array\n * module.exports = [\n * (req, res, next) => { console.log('First'); next(); },\n * (req, res, next) => { console.log('Second'); next(); }\n * ];\n *\n * // Usage\n * const middlewares = await loadMiddlewares('./middleware.js');\n * middlewares.forEach(mw => app.use(mw));\n * ```\n */\nexport async function loadMiddlewares(filePath: string): Promise<RequestHandler[]> {\n const module = await loadModule(filePath);\n\n // Check for default export first (ES modules or wrapped CommonJS)\n let middlewareExport: unknown;\n if (typeof module === 'object' && module !== null && 'default' in module) {\n const defaultExport = (module as { default: unknown }).default;\n // If default is also an object with a default property, unwrap it\n if (typeof defaultExport === 'object' && defaultExport !== null && 'default' in defaultExport) {\n middlewareExport = (defaultExport as { default: unknown }).default;\n } else {\n middlewareExport = defaultExport;\n }\n } else {\n middlewareExport = module;\n }\n\n // Normalize to array\n const middlewares = Array.isArray(middlewareExport) ? middlewareExport : [middlewareExport];\n\n // Validate all are functions\n for (let i = 0; i < middlewares.length; i++) {\n const mw: unknown = middlewares[i];\n if (typeof mw !== 'function') {\n const indexStr = String(i);\n throw new Error(\n `Middleware file '${filePath}' at index ${indexStr} is not a function, got ${typeof mw}`\n );\n }\n }\n\n return middlewares as RequestHandler[];\n}\n","import { RequestHandler } from 'express';\nimport { readFile } from 'node:fs/promises';\n\n/**\n * Route rewrite rules mapping\n * Maps incoming URL patterns to target URL patterns\n *\n * @example\n * ```json\n * {\n * \"/api/*\": \"/$1\",\n * \"/me\": \"/profile\",\n * \"/news/top\": \"/news?_sort=date&_order=asc&_limit=10\"\n * }\n * ```\n */\nexport type RewriteRules = Record<string, string>;\n\n/**\n * Load rewrite rules from a JSON file\n *\n * @param filePath - Path to the JSON file containing route mappings\n * @returns Rewrite rules object\n * @throws Error if file cannot be loaded or parsed\n *\n * @example\n * ```typescript\n * const rules = await loadRewriteRules('./routes.json');\n * const middleware = createRewriterMiddleware(rules);\n * app.use(middleware);\n * ```\n */\nexport async function loadRewriteRules(filePath: string): Promise<RewriteRules> {\n try {\n const content = await readFile(filePath, 'utf-8');\n const rules = JSON.parse(content) as unknown;\n\n if (typeof rules !== 'object' || rules === null || Array.isArray(rules)) {\n throw new Error('Routes file must contain a JSON object with route mappings');\n }\n\n // Validate all keys and values are strings\n for (const [key, value] of Object.entries(rules)) {\n if (typeof value !== 'string') {\n throw new Error(`Route mapping for '${key}' must be a string, got ${typeof value}`);\n }\n }\n\n return rules as RewriteRules;\n } catch (error) {\n if (error instanceof Error && error.message.includes('Routes file must contain')) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Failed to load routes from '${filePath}': ${message}\\n` +\n `\\nExpected format:\\n` +\n `{\\n` +\n ` \"/api/*\": \"/$1\",\\n` +\n ` \"/users/:id\": \"/api/users/:id\"\\n` +\n `}\\n` +\n `\\nSee examples/routes.json for more examples.`\n );\n }\n}\n\n/**\n * Convert a route pattern with wildcards and parameters to a RegExp\n *\n * @param pattern - Route pattern (e.g., '/api/*', '/posts/:id')\n * @returns Regular expression and parameter names\n *\n * @example\n * ```typescript\n * const { regex, params } = patternToRegex('/api/*');\n * // regex: /^\\/api\\/(.*)$/\n * // params: ['$1']\n *\n * const { regex, params } = patternToRegex('/posts/:id/comments/:commentId');\n * // regex: /^\\/posts\\/([^\\/]+)\\/comments\\/([^\\/]+)$/\n * // params: ['id', 'commentId']\n * ```\n */\nfunction patternToRegex(pattern: string): { regex: RegExp; params: string[] } {\n const params: string[] = [];\n\n // Escape special regex characters except * and :\n let regexPattern = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n // Replace :param with capturing group and collect param names\n regexPattern = regexPattern.replace(/:([^/]+)/g, (_match, param: string) => {\n params.push(param);\n return '([^/]+)';\n });\n\n // Replace * with capturing group\n regexPattern = regexPattern.replace(/\\*/g, () => {\n params.push(`$${String(params.length + 1)}`);\n return '(.*)';\n });\n\n return {\n regex: new RegExp(`^${regexPattern}$`),\n params,\n };\n}\n\n/**\n * Rewrite a URL based on a pattern and substitution\n *\n * @param url - Original URL\n * @param fromPattern - Pattern to match (with * or :param)\n * @param toPattern - Target pattern with $1, $2, etc. or :param\n * @returns Rewritten URL or null if pattern doesn't match\n *\n * @example\n * ```typescript\n * rewriteUrl('/api/posts', '/api/*', '/$1') // → '/posts'\n * rewriteUrl('/me', '/me', '/profile') // → '/profile'\n * rewriteUrl('/posts/1', '/posts/:id', '/items/:id') // → '/items/1'\n * ```\n */\nfunction rewriteUrl(url: string, fromPattern: string, toPattern: string): string | null {\n const { regex, params } = patternToRegex(fromPattern);\n const match = url.match(regex);\n\n if (!match) {\n return null;\n }\n\n // Extract captured values\n const captures = match.slice(1);\n\n // Build the replacement URL\n let result = toPattern;\n\n // Replace $1, $2, etc. with captured values\n for (let i = 0; i < captures.length; i++) {\n const value = captures[i];\n if (value !== undefined) {\n result = result.replace(`$${String(i + 1)}`, value);\n }\n }\n\n // Replace :param with captured values (for named parameters)\n for (let i = 0; i < params.length; i++) {\n const param = params[i];\n const value = captures[i];\n if (param && !param.startsWith('$') && value !== undefined) {\n result = result.replace(`:${param}`, value);\n }\n }\n\n return result;\n}\n\n/**\n * Create Express middleware that rewrites URLs based on rules\n *\n * @param rules - Route rewrite rules\n * @returns Express middleware function\n *\n * @example\n * ```typescript\n * const rules = {\n * '/api/*': '/$1',\n * '/me': '/profile',\n * '/posts/:category': '/posts?category=:category'\n * };\n *\n * const rewriter = createRewriterMiddleware(rules);\n * app.use(rewriter);\n * ```\n */\nexport function createRewriterMiddleware(rules: RewriteRules): RequestHandler {\n return (req, _res, next) => {\n // Try each rule in order\n for (const [from, to] of Object.entries(rules)) {\n const rewritten = rewriteUrl(req.url, from, to);\n\n if (rewritten !== null) {\n req.url = rewritten;\n break; // Apply only the first matching rule\n }\n }\n\n next();\n };\n}\n","import { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\n/**\n * Find the closest matching string using Levenshtein distance\n *\n * @param input - Input string to match\n * @param candidates - Array of candidate strings\n * @returns Closest match or null if no good match found\n */\nfunction findClosestMatch(input: string, candidates: string[]): string | null {\n /* eslint-disable @typescript-eslint/no-non-null-assertion */\n const levenshtein = (a: string, b: string): number => {\n const matrix: number[][] = [];\n\n for (let i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n\n for (let j = 0; j <= a.length; j++) {\n matrix[0]![j] = j;\n }\n\n for (let i = 1; i <= b.length; i++) {\n for (let j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n matrix[i]![j] = matrix[i - 1]![j - 1]!;\n } else {\n matrix[i]![j] = Math.min(\n matrix[i - 1]![j - 1]! + 1,\n matrix[i]![j - 1]! + 1,\n matrix[i - 1]![j]! + 1\n );\n }\n }\n }\n\n return matrix[b.length]![a.length]!;\n };\n /* eslint-enable @typescript-eslint/no-non-null-assertion */\n\n let closestMatch: string | null = null;\n let minDistance = Infinity;\n\n for (const candidate of candidates) {\n const distance = levenshtein(input.toLowerCase(), candidate.toLowerCase());\n if (distance < minDistance && distance <= 3) {\n // Only suggest if distance is 3 or less\n minDistance = distance;\n closestMatch = candidate;\n }\n }\n\n return closestMatch;\n}\n\n/**\n * Configuration options that can be specified in config file or CLI\n */\nexport interface Config {\n port?: number;\n host?: string;\n watch?: boolean;\n routes?: string;\n middlewares?: string;\n static?: string;\n readOnly?: boolean;\n noCors?: boolean;\n noGzip?: boolean;\n snapshots?: string;\n delay?: number;\n id?: string;\n foreignKeySuffix?: string;\n quiet?: boolean;\n}\n\n/**\n * Load configuration from a JSON file\n *\n * @param configPath - Path to the configuration file\n * @returns Configuration object or null if file doesn't exist\n * @throws Error if file exists but is invalid JSON or contains invalid options\n *\n * @example\n * ```typescript\n * const config = loadConfig('api-faker.json');\n * if (config) {\n * console.log('Loaded config:', config);\n * }\n * ```\n */\nexport function loadConfig(configPath: string): Config | null {\n const resolvedPath = resolve(configPath);\n\n // If file doesn't exist, return null (not an error)\n if (!existsSync(resolvedPath)) {\n return null;\n }\n\n try {\n const content = readFileSync(resolvedPath, 'utf-8');\n const config = JSON.parse(content) as unknown;\n\n // Validate config is an object\n if (typeof config !== 'object' || config === null || Array.isArray(config)) {\n throw new Error('Config file must contain a JSON object');\n }\n\n // Validate config properties\n validateConfig(config as Record<string, unknown>);\n\n return config as Config;\n } catch (error) {\n if (error instanceof Error && error.message.includes('Config file must contain')) {\n throw error;\n }\n throw new Error(\n `Failed to load config from '${configPath}': ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * Validate configuration object\n *\n * @param config - Configuration object to validate\n * @throws Error if configuration contains invalid options\n */\nfunction validateConfig(config: Record<string, unknown>): void {\n const validOptions = new Set([\n 'port',\n 'host',\n 'watch',\n 'routes',\n 'middlewares',\n 'static',\n 'readOnly',\n 'noCors',\n 'noGzip',\n 'snapshots',\n 'delay',\n 'id',\n 'foreignKeySuffix',\n 'quiet',\n ]);\n\n // Check for unknown options\n for (const key of Object.keys(config)) {\n if (!validOptions.has(key)) {\n const suggestion = findClosestMatch(key, Array.from(validOptions));\n const didYouMean = suggestion ? ` Did you mean '${suggestion}'?` : '';\n throw new Error(\n `Unknown config option: '${key}'.${didYouMean} Valid options are: ${Array.from(validOptions).join(', ')}`\n );\n }\n }\n\n // Validate types\n if (\n 'port' in config &&\n (typeof config.port !== 'number' || config.port < 0 || config.port > 65535)\n ) {\n throw new Error(\"Config option 'port' must be a number between 0 and 65535\");\n }\n\n if ('host' in config && typeof config.host !== 'string') {\n throw new Error(\"Config option 'host' must be a string\");\n }\n\n if ('watch' in config && typeof config.watch !== 'boolean') {\n throw new Error(\"Config option 'watch' must be a boolean\");\n }\n\n if ('routes' in config && typeof config.routes !== 'string') {\n throw new Error(\"Config option 'routes' must be a string\");\n }\n\n if ('middlewares' in config && typeof config.middlewares !== 'string') {\n throw new Error(\"Config option 'middlewares' must be a string\");\n }\n\n if ('static' in config && typeof config.static !== 'string') {\n throw new Error(\"Config option 'static' must be a string\");\n }\n\n if ('readOnly' in config && typeof config.readOnly !== 'boolean') {\n throw new Error(\"Config option 'readOnly' must be a boolean\");\n }\n\n if ('noCors' in config && typeof config.noCors !== 'boolean') {\n throw new Error(\"Config option 'noCors' must be a boolean\");\n }\n\n if ('noGzip' in config && typeof config.noGzip !== 'boolean') {\n throw new Error(\"Config option 'noGzip' must be a boolean\");\n }\n\n if ('snapshots' in config && typeof config.snapshots !== 'string') {\n throw new Error(\"Config option 'snapshots' must be a string\");\n }\n\n if ('delay' in config && (typeof config.delay !== 'number' || config.delay < 0)) {\n throw new Error(\"Config option 'delay' must be a non-negative number\");\n }\n\n if ('id' in config && typeof config.id !== 'string') {\n throw new Error(\"Config option 'id' must be a string\");\n }\n\n if ('foreignKeySuffix' in config && typeof config.foreignKeySuffix !== 'string') {\n throw new Error(\"Config option 'foreignKeySuffix' must be a string\");\n }\n\n if ('quiet' in config && typeof config.quiet !== 'boolean') {\n throw new Error(\"Config option 'quiet' must be a boolean\");\n }\n}\n\n/**\n * Merge CLI arguments with config file\n * CLI arguments take precedence over config file values\n *\n * @param cliConfig - Configuration from CLI arguments\n * @param fileConfig - Configuration from config file\n * @returns Merged configuration\n *\n * @example\n * ```typescript\n * const fileConfig = loadConfig('api-faker.json');\n * const merged = mergeConfig(cliArgs, fileConfig);\n * ```\n */\nexport function mergeConfig(cliConfig: Partial<Config>, fileConfig: Config | null): Config {\n if (!fileConfig) {\n return cliConfig as Config;\n }\n\n // CLI arguments override config file values\n return {\n ...fileConfig,\n ...cliConfig,\n };\n}\n"],"mappings":"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","names":["import_fs","import_path","module","import_express","pc","import_express","import_fs","import_path","express","module","express","cors","compression","yargs"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/database.ts","../src/server.ts","../src/router.ts","../src/logger.ts","../src/query.ts","../src/relationships.ts","../src/static.ts","../src/loader.ts","../src/rewriter.ts","../src/config.ts"],"sourcesContent":["import yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\nimport { readFileSync, existsSync } from 'fs';\nimport { resolve } from 'path';\nimport { watch } from 'chokidar';\nimport { Database } from './database';\nimport { createServer, startServer, ServerOptions } from './server';\nimport { loadConfig, mergeConfig, Config } from './config';\nimport { logger } from './logger';\n\n/**\n * CLI configuration interface\n */\ninterface CliConfig {\n source: string | undefined;\n port: number;\n host: string;\n watch: boolean;\n routes: string | undefined;\n middlewares: string | undefined;\n static: string | undefined;\n noStatic: boolean;\n readOnly: boolean;\n noCors: boolean;\n noGzip: boolean;\n snapshots: string;\n delay: number | undefined;\n id: string;\n foreignKeySuffix: string;\n quiet: boolean;\n config: string;\n}\n\n/**\n * Get package version\n */\nfunction getVersion(): string {\n try {\n const packageJson = JSON.parse(\n readFileSync(resolve(__dirname, '../package.json'), 'utf-8')\n ) as { version: string };\n return packageJson.version;\n } catch {\n return '0.0.0';\n }\n}\n\n/**\n * Parse and validate CLI arguments\n */\nfunction parseCli(): CliConfig {\n const argv = yargs(hideBin(process.argv))\n .scriptName('rest_api_faker')\n .usage('Usage: $0 [options] <source>')\n .example('$0 db.json', 'Start API Faker with db.json')\n .example('$0 file.js', 'Start API Faker with a JS file')\n .example('$0 http://example.com/db.json', 'Start API Faker with a remote schema')\n .option('config', {\n alias: 'c',\n type: 'string',\n description: 'Path to config file',\n default: 'rest_api_faker.json',\n })\n .option('port', {\n alias: 'p',\n type: 'number',\n description: 'Set port',\n default: 3000,\n })\n .option('host', {\n alias: 'H',\n type: 'string',\n description: 'Set host',\n default: 'localhost',\n })\n .option('watch', {\n alias: 'w',\n type: 'boolean',\n description: 'Watch file(s)',\n default: false,\n })\n .option('routes', {\n alias: 'r',\n type: 'string',\n description: 'Path to routes file',\n })\n .option('middlewares', {\n alias: 'm',\n type: 'string',\n description: 'Path to middleware file',\n })\n .option('static', {\n alias: 's',\n type: 'string',\n description: 'Set static files directory',\n default: './public',\n })\n .option('no-static', {\n type: 'boolean',\n description: 'Disable static file serving',\n default: false,\n })\n .option('read-only', {\n alias: 'ro',\n type: 'boolean',\n description: 'Allow only GET requests',\n default: false,\n })\n .option('no-cors', {\n alias: 'nc',\n type: 'boolean',\n description: 'Disable Cross-Origin Resource Sharing',\n default: false,\n })\n .option('no-gzip', {\n alias: 'ng',\n type: 'boolean',\n description: 'Disable GZIP Content-Encoding',\n default: false,\n })\n .option('snapshots', {\n alias: 'S',\n type: 'string',\n description: 'Set snapshots directory',\n default: '.',\n })\n .option('delay', {\n alias: 'd',\n type: 'number',\n description: 'Add delay to responses (ms)',\n })\n .option('id', {\n alias: 'i',\n type: 'string',\n description: 'Set database id property',\n default: 'id',\n })\n .option('foreignKeySuffix', {\n alias: 'fks',\n type: 'string',\n description: 'Set foreign key suffix',\n default: 'Id',\n })\n .option('quiet', {\n alias: 'q',\n type: 'boolean',\n description: 'Suppress log messages from output',\n default: false,\n })\n .help('help', 'Show help')\n .alias('h', 'help')\n .version(getVersion())\n .alias('v', 'version')\n .epilogue('For more information, visit https://github.com/hamidmayeli/rest_api_faker')\n .parseSync();\n\n // Load config file if it exists\n let fileConfig: Config | null = null;\n try {\n fileConfig = loadConfig(argv.config);\n } catch (error) {\n logger.error(\n `Failed to load config file: ${error instanceof Error ? error.message : String(error)}`\n );\n process.exit(1);\n }\n\n // Build CLI config object (only values explicitly provided by CLI)\n const cliConfig: Partial<Config> = {};\n\n // Only include CLI values that were explicitly set (not defaults)\n // We check if the value is different from the default or if it was provided\n if (argv.port !== 3000) cliConfig.port = argv.port;\n if (argv.host !== 'localhost') cliConfig.host = argv.host;\n if (argv.watch) cliConfig.watch = argv.watch;\n if (argv.routes) cliConfig.routes = argv.routes;\n if (argv.middlewares) cliConfig.middlewares = argv.middlewares;\n if (argv.static !== './public') cliConfig.static = argv.static;\n if (argv['read-only']) cliConfig.readOnly = argv['read-only'];\n if (argv['no-cors']) cliConfig.noCors = argv['no-cors'];\n if (argv['no-gzip']) cliConfig.noGzip = argv['no-gzip'];\n if (argv.snapshots !== '.') cliConfig.snapshots = argv.snapshots;\n if (argv.delay !== undefined) cliConfig.delay = argv.delay;\n if (argv.id !== 'id') cliConfig.id = argv.id;\n if (argv.foreignKeySuffix !== 'Id') cliConfig.foreignKeySuffix = argv.foreignKeySuffix;\n if (argv.quiet) cliConfig.quiet = argv.quiet;\n\n // Merge config file with CLI args (CLI takes precedence)\n const merged = mergeConfig(cliConfig, fileConfig);\n\n return {\n source: argv._[0] as string | undefined,\n port: merged.port ?? 3000,\n host: merged.host ?? 'localhost',\n watch: merged.watch ?? false,\n routes: merged.routes,\n middlewares: merged.middlewares,\n static: merged.static ?? './public',\n noStatic: argv['no-static'],\n readOnly: merged.readOnly ?? false,\n noCors: merged.noCors ?? false,\n noGzip: merged.noGzip ?? false,\n snapshots: merged.snapshots ?? '.',\n delay: merged.delay,\n id: merged.id ?? 'id',\n foreignKeySuffix: merged.foreignKeySuffix ?? 'Id',\n quiet: merged.quiet ?? false,\n config: argv.config,\n };\n}\n\n/**\n * Main CLI entry point\n */\nasync function main(): Promise<void> {\n const config = parseCli();\n\n if (!config.quiet) {\n logger.log(`\n ╔═══════════════════════════════════════╗\n ║ ║\n ║ API Faker v${getVersion().padEnd(19)}║\n ║ ║\n ╚═══════════════════════════════════════╝\n `);\n }\n\n if (!config.source) {\n logger.error('No source file specified');\n logger.info('Run \"rest_api_faker --help\" for usage information');\n process.exit(1);\n }\n\n if (!config.quiet) {\n logger.info(`Source: ${config.source}`);\n logger.info(`Port: ${String(config.port)}`);\n logger.info(`Host: ${config.host}`);\n logger.log('');\n logger.info('Loading database...');\n }\n\n try {\n // Initialize database\n const db = new Database(config.source, {\n idField: config.id,\n foreignKeySuffix: config.foreignKeySuffix,\n });\n\n await db.init();\n\n if (!config.quiet) {\n const data = db.getData();\n const resources = Object.keys(data);\n logger.success(`Loaded ${String(resources.length)} resource(s): ${resources.join(', ')}`);\n logger.log('');\n }\n\n // Create and start server\n const serverOptions: ServerOptions = {\n port: config.port,\n host: config.host,\n readOnly: config.readOnly,\n noCors: config.noCors,\n noGzip: config.noGzip,\n quiet: config.quiet,\n idField: config.id,\n foreignKeySuffix: config.foreignKeySuffix,\n enabled: !config.noStatic,\n };\n\n // Add static directory if specified\n if (config.static) {\n serverOptions.directory = config.static;\n }\n\n // Add custom routes if specified\n if (config.routes) {\n serverOptions.routes = config.routes;\n }\n\n // Add custom middlewares if specified\n if (config.middlewares) {\n serverOptions.middlewares = config.middlewares;\n }\n\n // Only add delay if it's defined\n if (config.delay !== undefined) {\n serverOptions.delay = config.delay;\n }\n\n const app = await createServer(db, serverOptions);\n\n const server = startServer(app, {\n port: config.port,\n host: config.host,\n quiet: config.quiet,\n });\n\n // Set up file watching if enabled\n if (config.watch && config.source && existsSync(config.source)) {\n const watcher = watch(config.source, {\n ignoreInitial: true,\n persistent: true,\n });\n\n watcher.on('change', (path) => {\n if (!config.quiet) {\n logger.log('');\n logger.info(`File changed: ${path}`);\n logger.info('Reloading database...');\n }\n\n db.init()\n .then(() => {\n if (!config.quiet) {\n const data = db.getData();\n const resources = Object.keys(data);\n logger.success(\n `Reloaded ${String(resources.length)} resource(s): ${resources.join(', ')}`\n );\n }\n })\n .catch((error: unknown) => {\n logger.error(\n `Failed to reload database: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n });\n });\n\n watcher.on('error', (error) => {\n logger.error(`Watcher error: ${error instanceof Error ? error.message : String(error)}`);\n });\n\n if (!config.quiet) {\n logger.info(`Watching ${config.source} for changes...`);\n logger.log('');\n }\n\n // Handle graceful shutdown\n process.on('SIGINT', () => {\n if (!config.quiet) {\n logger.log('');\n logger.info('Shutting down...');\n }\n watcher.close().catch(() => {\n // Ignore errors during shutdown\n });\n server.close(() => {\n process.exit(0);\n });\n });\n }\n } catch (error) {\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n}\n\n// Run CLI\nmain().catch((error: unknown) => {\n logger.error(`Fatal error: ${error instanceof Error ? error.message : 'Unknown error'}`);\n process.exit(1);\n});\n","import { Low } from 'lowdb';\nimport { JSONFile } from 'lowdb/node';\nimport { existsSync } from 'fs';\nimport { resolve, extname } from 'path';\nimport { pathToFileURL } from 'url';\n\n/**\n * Database data structure\n */\nexport interface DatabaseData {\n [key: string]: unknown;\n}\n\n/**\n * Database configuration options\n */\nexport interface DatabaseOptions {\n idField?: string;\n foreignKeySuffix?: string;\n}\n\n/**\n * Database class for managing JSON data with lowdb\n */\nexport class Database {\n private db: Low<DatabaseData>;\n private filePath: string;\n private options: Required<DatabaseOptions>;\n\n /**\n * Creates a new Database instance\n *\n * @param source - Path to JSON file or object with data\n * @param options - Database configuration options\n *\n * @example\n * ```typescript\n * const db = new Database('db.json', { idField: 'id' });\n * await db.init();\n * ```\n */\n constructor(source: string | DatabaseData, options: DatabaseOptions = {}) {\n this.options = {\n idField: options.idField || 'id',\n foreignKeySuffix: options.foreignKeySuffix || 'Id',\n };\n\n if (typeof source === 'string') {\n this.filePath = resolve(source);\n const adapter = new JSONFile<DatabaseData>(this.filePath);\n this.db = new Low(adapter, {});\n } else {\n this.filePath = '';\n // For in-memory database with object data\n const adapter = new JSONFile<DatabaseData>(':memory:');\n this.db = new Low(adapter, source);\n }\n }\n\n /**\n * Initialize the database by reading from file or using provided data\n * Supports .json, .js, .ts, and .mjs files\n *\n * @throws Error if file doesn't exist or contains invalid data\n */\n async init(): Promise<void> {\n if (this.filePath && !existsSync(this.filePath)) {\n const ext = this.filePath.endsWith('.js') ? '.js' : '.json';\n throw new Error(\n `Database file not found: ${this.filePath}\\n` +\n `\\nMake sure the file exists and the path is correct.\\n` +\n `Expected format: ${ext === '.js' ? 'JavaScript module exporting data' : 'JSON file with data structure'}`\n );\n }\n\n // Check if file is a JavaScript/TypeScript module\n if (this.filePath) {\n const ext = extname(this.filePath).toLowerCase();\n\n if (ext === '.js' || ext === '.mjs' || ext === '.cjs' || ext === '.ts') {\n // Load JavaScript module\n try {\n const fileUrl = pathToFileURL(this.filePath).href;\n const module = (await import(fileUrl)) as { default?: unknown } & Record<string, unknown>;\n const data: unknown = module.default ?? module;\n\n // If it's a function, call it to get the data\n if (typeof data === 'function') {\n const result: unknown = await Promise.resolve((data as () => unknown)());\n if (typeof result !== 'object' || result === null) {\n throw new Error('JavaScript module function must return an object');\n }\n this.db.data = result as DatabaseData;\n } else if (typeof data === 'object' && data !== null) {\n this.db.data = data as DatabaseData;\n } else {\n throw new Error('JavaScript module must export an object or function');\n }\n } catch (error) {\n throw new Error(\n `Failed to load JavaScript module: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n } else {\n // Load JSON file\n await this.db.read();\n }\n } else {\n // In-memory database\n await this.db.read();\n }\n\n if (typeof this.db.data !== 'object') {\n this.db.data = {};\n }\n\n // Ensure all collections are arrays or objects\n for (const key in this.db.data) {\n const value = this.db.data[key];\n if (value !== null && typeof value !== 'object') {\n throw new Error(`Invalid data structure: ${key} must be an array or object`);\n }\n }\n }\n\n /**\n * Get all data from the database\n *\n * @returns Complete database data\n */\n getData(): DatabaseData {\n return this.db.data;\n }\n\n /**\n * Get a specific collection (array) or resource (object)\n *\n * @param name - Name of the collection/resource\n * @returns Collection array, resource object, or undefined\n */\n getCollection(name: string): unknown {\n return this.db.data[name];\n }\n\n /**\n * Get a single item from a collection by ID\n *\n * @param collectionName - Name of the collection\n * @param id - ID of the item\n * @returns The item or undefined\n */\n getById(collectionName: string, id: string | number): unknown {\n const collection = this.db.data[collectionName];\n\n if (!Array.isArray(collection)) {\n return undefined;\n }\n\n return collection.find((item) => {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n const record = item as Record<string, unknown>;\n return record[this.options.idField] === id || record[this.options.idField] === Number(id);\n });\n }\n\n /**\n * Generate next ID for a collection\n *\n * @param collectionName - Name of the collection\n * @returns Next available ID\n */\n generateId(collectionName: string): number {\n const collection = this.db.data[collectionName];\n\n if (!Array.isArray(collection)) {\n return 1;\n }\n\n let maxId = 0;\n for (const item of collection) {\n if (typeof item === 'object' && item !== null) {\n const record = item as Record<string, unknown>;\n const idValue: unknown = record[this.options.idField];\n if (typeof idValue === 'number' && idValue > maxId) {\n maxId = idValue;\n }\n }\n }\n\n return maxId + 1;\n }\n\n /**\n * Create a new item in a collection\n *\n * @param collectionName - Name of the collection\n * @param data - Data to insert\n * @returns Created item with ID\n * @throws Error if collection is not an array\n */\n async create(\n collectionName: string,\n data: Record<string, unknown>\n ): Promise<Record<string, unknown>> {\n let collection = this.db.data[collectionName];\n\n // Create collection if it doesn't exist\n if (!collection) {\n collection = [];\n this.db.data[collectionName] = collection;\n }\n\n if (!Array.isArray(collection)) {\n throw new Error(`Cannot create in ${collectionName}: not a collection`);\n }\n\n // Generate ID if not provided or use provided ID\n const idValue: unknown =\n data[this.options.idField] !== undefined\n ? data[this.options.idField]\n : this.generateId(collectionName);\n\n // Check if ID already exists\n const existingItem = this.getById(collectionName, idValue as string | number);\n if (existingItem) {\n throw new Error(`Item with ${this.options.idField}=${String(idValue)} already exists`);\n }\n\n const newItem: Record<string, unknown> = { ...data, [this.options.idField]: idValue };\n collection.push(newItem);\n\n await this.save();\n return newItem;\n }\n\n /**\n * Update an item in a collection (full replacement)\n *\n * @param collectionName - Name of the collection\n * @param id - ID of the item\n * @param data - New data (ID field will be preserved)\n * @returns Updated item or undefined if not found\n */\n async update(\n collectionName: string,\n id: string | number,\n data: Record<string, unknown>\n ): Promise<Record<string, unknown> | undefined> {\n const collection = this.db.data[collectionName];\n\n if (!Array.isArray(collection)) {\n return undefined;\n }\n\n const index = collection.findIndex((item) => {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n const record = item as Record<string, unknown>;\n return record[this.options.idField] === id || record[this.options.idField] === Number(id);\n });\n\n if (index === -1) {\n return undefined;\n }\n\n // Preserve the ID (convert to number if it's a numeric string)\n const originalItem = collection[index] as Record<string, unknown>;\n const originalId: unknown = originalItem[this.options.idField];\n const updatedItem: Record<string, unknown> = { ...data, [this.options.idField]: originalId };\n collection[index] = updatedItem;\n\n await this.save();\n return updatedItem;\n }\n\n /**\n * Patch an item in a collection (partial update)\n *\n * @param collectionName - Name of the collection\n * @param id - ID of the item\n * @param data - Partial data to merge (ID field will be ignored)\n * @returns Updated item or undefined if not found\n */\n async patch(\n collectionName: string,\n id: string | number,\n data: Record<string, unknown>\n ): Promise<Record<string, unknown> | undefined> {\n const collection = this.db.data[collectionName];\n\n if (!Array.isArray(collection)) {\n return undefined;\n }\n\n const index = collection.findIndex((item) => {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n const record = item as Record<string, unknown>;\n return record[this.options.idField] === id || record[this.options.idField] === Number(id);\n });\n\n if (index === -1) {\n return undefined;\n }\n\n const currentItem = collection[index] as Record<string, unknown>;\n // Merge data but ignore ID field in the patch data\n const { [this.options.idField]: _ignoredId, ...patchData } = data;\n const patchedItem = { ...currentItem, ...patchData };\n\n collection[index] = patchedItem;\n\n await this.save();\n return patchedItem;\n }\n\n /**\n * Delete an item from a collection\n *\n * @param collectionName - Name of the collection\n * @param id - ID of the item\n * @returns true if deleted, false if not found\n */\n async delete(collectionName: string, id: string | number): Promise<boolean> {\n const collection = this.db.data[collectionName];\n\n if (!Array.isArray(collection)) {\n return false;\n }\n\n const index = collection.findIndex((item) => {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n const record = item as Record<string, unknown>;\n return record[this.options.idField] === id || record[this.options.idField] === Number(id);\n });\n\n if (index === -1) {\n return false;\n }\n\n collection.splice(index, 1);\n await this.save();\n return true;\n }\n\n /**\n * Update or create a singular resource\n *\n * @param resourceName - Name of the resource\n * @param data - Resource data\n * @returns Updated resource\n */\n async updateSingular(\n resourceName: string,\n data: Record<string, unknown>\n ): Promise<Record<string, unknown>> {\n this.db.data[resourceName] = data;\n await this.save();\n return data;\n }\n\n /**\n * Save the database to file\n */\n async save(): Promise<void> {\n await this.db.write();\n }\n\n /**\n * Check if a resource is a collection (array) or singular (object)\n *\n * @param name - Name of the resource\n * @returns true if collection, false if singular or doesn't exist\n */\n isCollection(name: string): boolean {\n return Array.isArray(this.db.data[name]);\n }\n\n /**\n * Get ID field name\n */\n getIdField(): string {\n return this.options.idField;\n }\n\n /**\n * Get foreign key suffix\n */\n getForeignKeySuffix(): string {\n return this.options.foreignKeySuffix;\n }\n}\n","import express, { Express } from 'express';\nimport cors from 'cors';\nimport compression from 'compression';\nimport { Database } from './database';\nimport { createRouter, RouterOptions } from './router';\nimport { createStaticMiddleware, createHomepageMiddleware, StaticOptions } from './static';\nimport { loadMiddlewares } from './loader';\nimport { loadRewriteRules, createRewriterMiddleware } from './rewriter';\nimport { logger } from './logger';\n\n/**\n * Server configuration options\n */\nexport interface ServerOptions extends RouterOptions, StaticOptions {\n port?: number;\n host?: string;\n noCors?: boolean;\n noGzip?: boolean;\n delay?: number;\n quiet?: boolean;\n routes?: string;\n middlewares?: string;\n}\n\n/**\n * Create Express server with API Faker\n *\n * @param db - Database instance\n * @param options - Server configuration options\n * @returns Express application\n *\n * @example\n * ```typescript\n * const db = new Database('db.json');\n * await db.init();\n * const app = await createServer(db, { port: 3000 });\n * ```\n */\nexport async function createServer(\n db: Database,\n options: Partial<ServerOptions> = {}\n): Promise<Express> {\n const app = express();\n\n // CORS\n if (!options.noCors) {\n app.use(cors());\n }\n\n // GZIP compression\n if (!options.noGzip) {\n app.use(compression());\n }\n\n // JSON body parser\n app.use(express.json());\n\n // Delay middleware (for testing/simulation)\n if (options.delay && options.delay > 0) {\n const delay = options.delay;\n app.use((_req, _res, next) => {\n setTimeout(() => {\n next();\n }, delay);\n });\n }\n\n // Request logger (unless quiet)\n if (!options.quiet) {\n app.use((req, _res, next) => {\n logger.request(req.method, req.url);\n next();\n });\n }\n\n // Custom middlewares (load before routes)\n if (options.middlewares) {\n try {\n const middlewares = await loadMiddlewares(options.middlewares);\n for (const middleware of middlewares) {\n app.use(middleware);\n }\n if (!options.quiet) {\n logger.success(`Loaded custom middlewares from ${options.middlewares}`);\n }\n } catch (error) {\n logger.error(\n `Failed to load middlewares: ${error instanceof Error ? error.message : String(error)}`\n );\n throw error;\n }\n }\n\n // Homepage middleware (must be before static to allow custom index.html)\n app.use(createHomepageMiddleware(options));\n\n // Static file server\n app.use(createStaticMiddleware(options));\n\n // Custom route rewriting (load before all routes)\n if (options.routes) {\n try {\n const rules = await loadRewriteRules(options.routes);\n const rewriter = createRewriterMiddleware(rules);\n app.use(rewriter);\n if (!options.quiet) {\n logger.success(`Loaded route rewrite rules from ${options.routes}`);\n }\n } catch (error) {\n logger.error(\n `Failed to load routes: ${error instanceof Error ? error.message : String(error)}`\n );\n throw error;\n }\n }\n\n // Special endpoint: /db (full database dump)\n app.get('/db', (_req, res) => {\n res.json(db.getData());\n });\n\n // API routes\n const router = createRouter(db, options);\n app.use(router);\n\n // 404 handler\n app.use((_req, res) => {\n res.status(404).json({ error: 'Not Found' });\n });\n\n return app;\n}\n\n/**\n * Start the server\n *\n * @param app - Express application\n * @param options - Server options (port, host)\n * @returns Server instance\n */\nexport function startServer(\n app: Express,\n options: Pick<ServerOptions, 'port' | 'host' | 'quiet'> = {}\n): ReturnType<Express['listen']> {\n const port = options.port || 3000;\n const host = options.host || 'localhost';\n\n return app.listen(port, host, () => {\n if (!options.quiet) {\n logger.banner([\n '🚀 API Faker is running!',\n '',\n ' Resources:',\n ` http://${host}:${String(port)}/`,\n '',\n ' Home:',\n ` http://${host}:${String(port)}`,\n ]);\n }\n });\n}\n","import { Router, Request, Response, NextFunction } from 'express';\nimport { Database } from './database';\nimport { logger } from './logger';\nimport { parseQuery, applyQuery, generateLinkHeader } from './query';\nimport { parseRelationships, applyRelationships, getForeignKey } from './relationships';\n\n/**\n * Helper to safely get route param (Express guarantees route params exist)\n */\nfunction getParam(req: Request, name: string): string {\n const value = req.params[name];\n if (value === undefined) {\n throw new Error(`Route parameter '${name}' is missing`);\n }\n return value;\n}\n\n/**\n * Router configuration options\n */\nexport interface RouterOptions {\n idField?: string;\n foreignKeySuffix?: string;\n readOnly?: boolean;\n}\n\n/**\n * Create API Faker router with CRUD operations\n *\n * @param db - Database instance\n * @param options - Router configuration options\n * @returns Express router\n *\n * @example\n * ```typescript\n * const db = new Database('db.json');\n * await db.init();\n * const router = createRouter(db);\n * app.use(router);\n * ```\n */\nexport function createRouter(db: Database, options: Partial<RouterOptions> = {}): Router {\n const router = Router();\n const readOnly = options.readOnly || false;\n const idField = options.idField || 'id';\n const foreignKeySuffix = options.foreignKeySuffix || 'Id';\n\n /**\n * Validate Content-Type for write operations\n */\n const validateContentType = (req: Request, _res: Response, next: NextFunction): void => {\n const contentType = req.get('Content-Type');\n if (!contentType || !contentType.includes('application/json')) {\n // Express still parses but we should warn about missing header\n // In real json-server, this would still work but without actual data modification\n logger.warn('Content-Type should be application/json');\n }\n next();\n };\n\n /**\n * GET /db - Return entire database\n */\n router.get('/db', (_req: Request, res: Response) => {\n res.json(db.getData());\n });\n\n /**\n * GET /:resource - Get all items in a collection or singular resource\n */\n router.get('/:resource', (req: Request, res: Response): void => {\n const resource = getParam(req, 'resource');\n const data = db.getCollection(resource);\n\n if (data === undefined) {\n res.status(404).json({ error: `Resource '${resource}' not found` });\n return;\n }\n // For collections (arrays), apply query parameters\n if (Array.isArray(data)) {\n const queryOptions = parseQuery(req);\n const { data: filtered, total } = applyQuery(data, queryOptions);\n\n // Apply relationships (_embed, _expand)\n const { embed, expand } = parseRelationships(req.query as Record<string, unknown>);\n const withRelationships = applyRelationships(\n filtered as Record<string, unknown>[],\n resource,\n embed,\n expand,\n db,\n idField,\n foreignKeySuffix\n );\n\n // Add X-Total-Count header\n res.set('X-Total-Count', String(total));\n\n // Add Link header for pagination\n if (queryOptions.page !== undefined && queryOptions.limit !== undefined) {\n const linkHeader = generateLinkHeader(req, queryOptions.page, queryOptions.limit, total);\n res.set('Link', linkHeader);\n }\n\n res.json(withRelationships);\n return;\n }\n\n // For singular resources (objects), return as-is\n res.json(data);\n });\n\n /**\n * GET /:resource/:id - Get single item by ID\n */\n router.get('/:resource/:id', (req: Request, res: Response): void => {\n const resource = getParam(req, 'resource');\n const id = getParam(req, 'id');\n\n // Check if resource is a collection\n if (!db.isCollection(resource)) {\n res.status(404).json({ error: `Collection '${resource}' not found` });\n return;\n }\n\n const item = db.getById(resource, id);\n\n if (!item) {\n res.status(404).json({ error: `Item with id '${id}' not found in '${resource}'` });\n return;\n }\n\n // Apply relationships if requested\n const { embed, expand } = parseRelationships(req.query as Record<string, unknown>);\n if (embed.length > 0 || expand.length > 0) {\n const withRelationships = applyRelationships(\n [item as Record<string, unknown>],\n resource,\n embed,\n expand,\n db,\n idField,\n foreignKeySuffix\n );\n res.json(withRelationships[0]);\n return;\n }\n\n res.json(item);\n });\n\n /**\n * GET /:parent/:parentId/:children - Get nested children\n */\n router.get('/:parent/:parentId/:children', (req: Request, res: Response): void => {\n const parent = getParam(req, 'parent');\n const parentId = getParam(req, 'parentId');\n const children = getParam(req, 'children');\n\n // Verify parent exists\n if (!db.isCollection(parent)) {\n res.status(404).json({ error: `Collection '${parent}' not found` });\n return;\n }\n\n const parentItem = db.getById(parent, parentId);\n if (!parentItem) {\n res.status(404).json({ error: `Parent item with id '${parentId}' not found in '${parent}'` });\n return;\n }\n\n // Get children collection\n const childrenData = db.getCollection(children);\n if (!Array.isArray(childrenData)) {\n res.status(404).json({ error: `Collection '${children}' not found` });\n return;\n }\n\n // Filter children by parent foreign key\n const foreignKey = getForeignKey(parent, foreignKeySuffix);\n const filtered = childrenData.filter((child) => {\n if (typeof child !== 'object' || child === null) return false;\n const childFk = (child as Record<string, unknown>)[foreignKey];\n return (\n childFk === parentId ||\n (typeof childFk === 'number' || typeof childFk === 'string'\n ? String(childFk) === parentId\n : false)\n );\n });\n\n // Apply query parameters\n const queryOptions = parseQuery(req);\n const { data: result, total } = applyQuery(filtered, queryOptions);\n\n res.set('X-Total-Count', String(total));\n res.json(result);\n });\n\n /**\n * POST /:parent/:parentId/:children - Create nested child\n */\n router.post(\n '/:parent/:parentId/:children',\n validateContentType,\n async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const parent = getParam(req, 'parent');\n const parentId = getParam(req, 'parentId');\n const children = getParam(req, 'children');\n const data = req.body as Record<string, unknown>;\n\n if (typeof data !== 'object') {\n return res.status(400).json({ error: 'Request body must be a JSON object' });\n }\n\n // Verify parent exists\n if (!db.isCollection(parent)) {\n return res.status(404).json({ error: `Collection '${parent}' not found` });\n }\n\n const parentItem = db.getById(parent, parentId);\n if (!parentItem) {\n return res\n .status(404)\n .json({ error: `Parent item with id '${parentId}' not found in '${parent}'` });\n }\n\n // Auto-set foreign key\n const foreignKey = getForeignKey(parent, foreignKeySuffix);\n data[foreignKey] = parentId;\n\n try {\n const created = await db.create(children, data);\n return res.status(201).json(created);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return res.status(400).json({ error: message });\n }\n }\n );\n\n /**\n * POST /:resource - Create new item\n */\n router.post('/:resource', validateContentType, async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const resource = getParam(req, 'resource');\n const data = req.body as Record<string, unknown>;\n\n if (typeof data !== 'object') {\n return res.status(400).json({ error: 'Request body must be a JSON object' });\n }\n\n try {\n // Handle singular resources\n if (!db.isCollection(resource) && db.getCollection(resource) !== undefined) {\n const updated = await db.updateSingular(resource, data);\n return res.status(200).json(updated);\n }\n\n // Create in collection\n const created = await db.create(resource, data);\n return res.status(201).json(created);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return res.status(400).json({ error: message });\n }\n });\n\n /**\n * PUT /:resource/:id - Full update of item\n */\n router.put('/:resource/:id', validateContentType, async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const resource = getParam(req, 'resource');\n const id = getParam(req, 'id');\n const data = req.body as Record<string, unknown>;\n\n if (typeof data !== 'object') {\n return res.status(400).json({ error: 'Request body must be a JSON object' });\n }\n\n if (!db.isCollection(resource)) {\n return res.status(404).json({ error: `Collection '${resource}' not found` });\n }\n\n try {\n const updated = await db.update(resource, id, data);\n\n if (!updated) {\n return res.status(404).json({ error: `Item with id '${id}' not found in '${resource}'` });\n }\n\n return res.json(updated);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return res.status(400).json({ error: message });\n }\n });\n\n /**\n * PATCH /:resource/:id - Partial update of item\n */\n router.patch('/:resource/:id', validateContentType, async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const resource = getParam(req, 'resource');\n const id = getParam(req, 'id');\n const data = req.body as Record<string, unknown>;\n\n if (typeof data !== 'object') {\n return res.status(400).json({ error: 'Request body must be a JSON object' });\n }\n\n if (!db.isCollection(resource)) {\n return res.status(404).json({ error: `Collection '${resource}' not found` });\n }\n\n try {\n const patched = await db.patch(resource, id, data);\n\n if (!patched) {\n return res.status(404).json({ error: `Item with id '${id}' not found in '${resource}'` });\n }\n\n return res.json(patched);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return res.status(400).json({ error: message });\n }\n });\n\n /**\n * PUT /:resource - Full update of singular resource\n */\n router.put('/:resource', validateContentType, async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const resource = getParam(req, 'resource');\n const data = req.body as Record<string, unknown>;\n\n if (typeof data !== 'object') {\n return res.status(400).json({ error: 'Request body must be a JSON object' });\n }\n\n // Only allow for singular resources (objects, not arrays)\n if (db.isCollection(resource)) {\n return res\n .status(400)\n .json({\n error: `Cannot PUT to collection '${resource}'. Use POST or PUT /${resource}/:id`,\n });\n }\n\n try {\n const updated = await db.updateSingular(resource, data);\n return res.json(updated);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return res.status(400).json({ error: message });\n }\n });\n\n /**\n * PATCH /:resource - Partial update of singular resource\n */\n router.patch('/:resource', validateContentType, async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const resource = getParam(req, 'resource');\n const data = req.body as Record<string, unknown>;\n\n if (typeof data !== 'object') {\n return res.status(400).json({ error: 'Request body must be a JSON object' });\n }\n\n // Only allow for singular resources\n if (db.isCollection(resource)) {\n return res\n .status(400)\n .json({ error: `Cannot PATCH collection '${resource}'. Use PATCH /${resource}/:id` });\n }\n\n const current = db.getCollection(resource) as Record<string, unknown> | undefined;\n\n if (!current || typeof current !== 'object') {\n return res.status(404).json({ error: `Resource '${resource}' not found` });\n }\n\n try {\n const merged = { ...current, ...data };\n const updated = await db.updateSingular(resource, merged);\n return res.json(updated);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return res.status(400).json({ error: message });\n }\n });\n\n /**\n * DELETE /:resource/:id - Delete item by ID\n */\n router.delete('/:resource/:id', async (req: Request, res: Response) => {\n if (readOnly) {\n return res.status(403).json({ error: 'Read-only mode enabled' });\n }\n\n const resource = getParam(req, 'resource');\n const id = getParam(req, 'id');\n\n if (!db.isCollection(resource)) {\n return res.status(404).json({ error: `Collection '${resource}' not found` });\n }\n\n const deleted = await db.delete(resource, id);\n\n if (!deleted) {\n return res.status(404).json({ error: `Item with id '${id}' not found in '${resource}'` });\n }\n\n // Return 204 No Content\n return res.status(204).send();\n });\n\n return router;\n}\n","import pc from 'picocolors';\n\n/**\n * Logger utility for colored console output\n *\n * Provides consistent, color-coded logging throughout the application.\n * Follows a standard format: [LEVEL] Message\n */\nexport const logger = {\n /**\n * Log success message in green\n *\n * @param message - Success message\n *\n * @example\n * ```typescript\n * logger.success('Server started successfully');\n * // Output: ✓ Server started successfully (in green)\n * ```\n */\n success(message: string): void {\n console.log(pc.green(`✓ ${message}`));\n },\n\n /**\n * Log error message in red\n *\n * @param message - Error message\n *\n * @example\n * ```typescript\n * logger.error('Failed to load file');\n * // Output: ✗ Failed to load file (in red)\n * ```\n */\n error(message: string): void {\n console.error(pc.red(`✗ ${message}`));\n },\n\n /**\n * Log warning message in yellow\n *\n * @param message - Warning message\n *\n * @example\n * ```typescript\n * logger.warn('Using default port');\n * // Output: ⚠ Using default port (in yellow)\n * ```\n */\n warn(message: string): void {\n console.warn(pc.yellow(`⚠ ${message}`));\n },\n\n /**\n * Log info message in cyan\n *\n * @param message - Info message\n *\n * @example\n * ```typescript\n * logger.info('Watching for changes...');\n * // Output: ℹ Watching for changes... (in cyan)\n * ```\n */\n info(message: string): void {\n console.log(pc.cyan(`ℹ ${message}`));\n },\n\n /**\n * Log plain message without color\n *\n * @param message - Message to log\n *\n * @example\n * ```typescript\n * logger.log('http://localhost:3000');\n * // Output: http://localhost:3000\n * ```\n */\n log(message: string): void {\n console.log(message);\n },\n\n /**\n * Log request in gray (for non-quiet mode)\n *\n * @param method - HTTP method\n * @param url - Request URL\n *\n * @example\n * ```typescript\n * logger.request('GET', '/api/users');\n * // Output: [timestamp] GET /api/users (in gray)\n * ```\n */\n request(method: string, url: string): void {\n const timestamp = new Date().toLocaleTimeString();\n console.log(pc.gray(`[${timestamp}] ${method} ${url}`));\n },\n\n /**\n * Log formatted banner\n *\n * @param lines - Array of lines to display\n *\n * @example\n * ```typescript\n * logger.banner([\n * '🚀 API Faker is running!',\n * '',\n * ' Resources:',\n * ' http://localhost:3000/'\n * ]);\n * ```\n */\n banner(lines: string[]): void {\n console.log();\n for (const line of lines) {\n // Color URLs in cyan\n if (line.includes('http://') || line.includes('https://')) {\n const colored = line.replace(/(https?:\\/\\/[^\\s]+)/g, (url) => pc.cyan(url));\n console.log(colored);\n } else if (line.startsWith('🚀')) {\n // Color rocket emoji line in bold\n console.log(pc.bold(line));\n } else {\n console.log(line);\n }\n }\n console.log();\n },\n};\n","/**\n * Query processing module for filtering, sorting, pagination, etc.\n */\n\nimport type { Request } from 'express';\n\n/**\n * Query options extracted from request\n */\nexport interface QueryOptions {\n filters: Record<string, string | string[]>;\n operators: Record<string, Record<string, string>>;\n sort: string[];\n order: string[];\n page?: number;\n limit?: number;\n start?: number;\n end?: number;\n q?: string;\n}\n\n/**\n * Parse query parameters from request\n *\n * @param req - Express request object\n * @returns Parsed query options\n *\n * @example\n * parseQuery(req) // { filters: { title: 'value' }, sort: ['id'], order: ['asc'] }\n */\nexport function parseQuery(req: Request): QueryOptions {\n const query = req.query;\n const filters: Record<string, string | string[]> = {};\n const operators: Record<string, Record<string, string>> = {};\n const sort: string[] = [];\n const order: string[] = [];\n let page: number | undefined;\n let limit: number | undefined;\n let start: number | undefined;\n let end: number | undefined;\n let q: string | undefined;\n\n for (const [key, value] of Object.entries(query)) {\n // Skip non-string and non-array values\n const stringValue =\n typeof value === 'string'\n ? value\n : Array.isArray(value) && value.length > 0 && typeof value[0] === 'string'\n ? value[0]\n : null;\n\n if (stringValue === null && !Array.isArray(value)) {\n continue;\n }\n\n // Special query parameters\n if (key === '_sort') {\n if (typeof value === 'string') {\n sort.push(...value.split(','));\n } else if (Array.isArray(value)) {\n for (const v of value) {\n if (typeof v === 'string') {\n sort.push(v);\n }\n }\n }\n continue;\n }\n\n if (key === '_order') {\n if (typeof value === 'string') {\n order.push(...value.split(','));\n } else if (Array.isArray(value)) {\n for (const v of value) {\n if (typeof v === 'string') {\n order.push(v);\n }\n }\n }\n continue;\n }\n\n if (key === '_page' && stringValue) {\n const parsed = parseInt(stringValue, 10);\n if (!isNaN(parsed) && parsed > 0) {\n page = parsed;\n }\n continue;\n }\n\n if (key === '_limit' && stringValue) {\n const parsed = parseInt(stringValue, 10);\n if (!isNaN(parsed) && parsed > 0) {\n limit = parsed;\n }\n continue;\n }\n\n if (key === '_start' && stringValue) {\n const parsed = parseInt(stringValue, 10);\n if (!isNaN(parsed) && parsed >= 0) {\n start = parsed;\n }\n continue;\n }\n\n if (key === '_end' && stringValue) {\n const parsed = parseInt(stringValue, 10);\n if (!isNaN(parsed) && parsed >= 0) {\n end = parsed;\n }\n continue;\n }\n\n if (key === 'q' && stringValue) {\n q = stringValue;\n continue;\n }\n\n // Operator filters (_gte, _lte, _ne, _like)\n const operatorMatch = key.match(/^(.+)_(gte|lte|ne|like)$/);\n if (operatorMatch && stringValue) {\n const field = operatorMatch[1];\n const operator = operatorMatch[2];\n\n if (field && operator) {\n if (!operators[field]) {\n operators[field] = {};\n }\n operators[field][operator] = stringValue;\n }\n continue;\n }\n\n // Regular filters\n if (typeof value === 'string') {\n filters[key] = value;\n } else if (Array.isArray(value)) {\n const stringValues = value.filter((v): v is string => typeof v === 'string');\n if (stringValues.length > 0) {\n filters[key] = stringValues;\n }\n }\n }\n\n const result: QueryOptions = {\n filters,\n operators,\n sort,\n order,\n };\n\n if (page !== undefined) result.page = page;\n if (limit !== undefined) result.limit = limit;\n if (start !== undefined) result.start = start;\n if (end !== undefined) result.end = end;\n if (q !== undefined) result.q = q;\n\n return result;\n}\n\n/**\n * Get nested property value from object\n *\n * @param obj - Object to get property from\n * @param path - Dot-separated property path\n * @returns Property value or undefined\n *\n * @example\n * getNestedValue({ user: { name: 'John' } }, 'user.name') // 'John'\n */\nfunction getNestedValue(obj: unknown, path: string): unknown {\n if (typeof obj !== 'object' || obj === null) {\n return undefined;\n }\n\n const keys = path.split('.');\n let current: unknown = obj;\n\n for (const key of keys) {\n if (typeof current !== 'object' || current === null || !(key in current)) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[key];\n }\n\n return current;\n}\n\n/**\n * Check if item matches filter criteria\n *\n * @param item - Item to check\n * @param filters - Filter criteria\n * @returns True if item matches all filters\n *\n * @example\n * matchesFilters({ title: 'test' }, { title: 'test' }) // true\n */\nfunction matchesFilters(item: unknown, filters: Record<string, string | string[]>): boolean {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n\n for (const [key, filterValue] of Object.entries(filters)) {\n const itemValue = getNestedValue(item, key);\n\n if (Array.isArray(filterValue)) {\n // Multiple values - item must match at least one\n const matched = filterValue.some((val) => String(itemValue) === val);\n if (!matched) {\n return false;\n }\n } else {\n // Single value - exact match\n if (String(itemValue) !== filterValue) {\n return false;\n }\n }\n }\n\n return true;\n}\n\n/**\n * Check if item matches operator criteria\n *\n * @param item - Item to check\n * @param operators - Operator criteria\n * @returns True if item matches all operators\n *\n * @example\n * matchesOperators({ age: 25 }, { age: { _gte: '18' } }) // true\n */\nfunction matchesOperators(\n item: unknown,\n operators: Record<string, Record<string, string>>\n): boolean {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n\n for (const [key, ops] of Object.entries(operators)) {\n const itemValue = getNestedValue(item, key);\n\n for (const [operator, filterValue] of Object.entries(ops)) {\n if (operator === 'gte') {\n if (Number(itemValue) < Number(filterValue)) {\n return false;\n }\n } else if (operator === 'lte') {\n if (Number(itemValue) > Number(filterValue)) {\n return false;\n }\n } else if (operator === 'ne') {\n if (String(itemValue) === filterValue) {\n return false;\n }\n } else if (operator === 'like') {\n const itemStr = String(itemValue).toLowerCase();\n const filterStr = filterValue.toLowerCase();\n if (!itemStr.includes(filterStr)) {\n return false;\n }\n }\n }\n }\n\n return true;\n}\n\n/**\n * Check if item matches full-text search\n *\n * @param item - Item to check\n * @param searchText - Text to search for\n * @returns True if any string property contains search text\n *\n * @example\n * matchesSearch({ title: 'Hello World' }, 'world') // true\n */\nfunction matchesSearch(item: unknown, searchText: string): boolean {\n if (typeof item !== 'object' || item === null) {\n return false;\n }\n\n const lowerSearch = searchText.toLowerCase();\n\n function searchObject(obj: unknown): boolean {\n if (typeof obj === 'string') {\n return obj.toLowerCase().includes(lowerSearch);\n }\n\n if (typeof obj === 'object' && obj !== null) {\n return Object.values(obj).some(searchObject);\n }\n\n return false;\n }\n\n return searchObject(item);\n}\n\n/**\n * Apply filtering, sorting, pagination, etc. to data array\n *\n * @param data - Array of items to process\n * @param options - Query options\n * @returns Processed array and total count (before pagination)\n *\n * @example\n * applyQuery([{ id: 1 }, { id: 2 }], { limit: 1 }) // { data: [{ id: 1 }], total: 2 }\n */\nexport function applyQuery<T>(data: T[], options: QueryOptions): { data: T[]; total: number } {\n let result = [...data];\n\n // Apply full-text search\n if (options.q) {\n const searchText = options.q;\n result = result.filter((item) => matchesSearch(item, searchText));\n }\n\n // Apply filters\n if (Object.keys(options.filters).length > 0) {\n result = result.filter((item) => matchesFilters(item, options.filters));\n }\n\n // Apply operators\n if (Object.keys(options.operators).length > 0) {\n result = result.filter((item) => matchesOperators(item, options.operators));\n }\n\n // Store total before pagination\n const total = result.length;\n\n // Apply sorting\n if (options.sort.length > 0) {\n const sortFields = options.sort;\n const sortOrders = options.order;\n\n result.sort((a, b) => {\n for (let i = 0; i < sortFields.length; i++) {\n const field = sortFields[i];\n if (!field) continue;\n\n const order = sortOrders[i] === 'desc' ? -1 : 1;\n\n const aVal = getNestedValue(a, field);\n const bVal = getNestedValue(b, field);\n\n if (aVal === bVal) continue;\n\n // Handle null/undefined\n if (aVal === undefined || aVal === null) return 1 * order;\n if (bVal === undefined || bVal === null) return -1 * order;\n\n // Compare values\n if (aVal < bVal) return -1 * order;\n if (aVal > bVal) return 1 * order;\n }\n return 0;\n });\n }\n\n // Apply slicing (_start and _end)\n if (options.start !== undefined || options.end !== undefined) {\n const start = options.start ?? 0;\n const end = options.end ?? result.length;\n result = result.slice(start, end);\n }\n // Apply pagination (_page and _limit)\n else if (options.page !== undefined && options.limit !== undefined) {\n const start = (options.page - 1) * options.limit;\n result = result.slice(start, start + options.limit);\n }\n // Apply limit only\n else if (options.limit !== undefined) {\n result = result.slice(0, options.limit);\n }\n\n return { data: result, total };\n}\n\n/**\n * Generate Link header for pagination\n *\n * @param req - Express request object\n * @param page - Current page\n * @param limit - Items per page\n * @param total - Total number of items\n * @returns Link header value\n *\n * @example\n * generateLinkHeader(req, 2, 10, 100) // '<...>; rel=\"first\", <...>; rel=\"prev\", ...'\n */\nexport function generateLinkHeader(\n req: Request,\n page: number,\n limit: number,\n total: number\n): string {\n const host = req.get('host') ?? 'localhost';\n const baseUrl = `${req.protocol}://${host}${req.path}`;\n const query = new URLSearchParams(req.query as Record<string, string>);\n const lastPage = Math.ceil(total / limit);\n\n const links: string[] = [];\n\n // First page\n query.set('_page', '1');\n query.set('_limit', String(limit));\n links.push(`<${baseUrl}?${query.toString()}>; rel=\"first\"`);\n\n // Previous page\n if (page > 1) {\n query.set('_page', String(page - 1));\n links.push(`<${baseUrl}?${query.toString()}>; rel=\"prev\"`);\n }\n\n // Next page\n if (page < lastPage) {\n query.set('_page', String(page + 1));\n links.push(`<${baseUrl}?${query.toString()}>; rel=\"next\"`);\n }\n\n // Last page\n query.set('_page', String(lastPage));\n links.push(`<${baseUrl}?${query.toString()}>; rel=\"last\"`);\n\n return links.join(', ');\n}\n","/**\n * Relationships module for handling _embed and _expand parameters\n */\n\nimport type { Database } from './database';\n\n/**\n * Detect foreign key relationships based on naming conventions\n *\n * @param collectionName - Name of the collection (e.g., 'posts')\n * @param foreignKeySuffix - Suffix for foreign keys (default: 'Id')\n * @returns Possible foreign key name (e.g., 'postId')\n *\n * @example\n * getForeignKey('posts', 'Id') // 'postId'\n * getForeignKey('users', '_id') // 'user_id'\n */\nexport function getForeignKey(collectionName: string, foreignKeySuffix: string): string {\n // Remove trailing 's' for singular form\n const singular = collectionName.endsWith('s') ? collectionName.slice(0, -1) : collectionName;\n\n return `${singular}${foreignKeySuffix}`;\n}\n\n/**\n * Embed child resources into parent items\n *\n * @param items - Parent items to embed children into\n * @param parentCollection - Name of parent collection\n * @param childCollection - Name of child collection\n * @param db - Database instance\n * @param idField - ID field name (default: 'id')\n * @param foreignKeySuffix - Foreign key suffix (default: 'Id')\n * @returns Items with embedded children\n *\n * @example\n * embedChildren([{ id: 1 }], 'posts', 'comments', db)\n * // [{ id: 1, comments: [{ id: 1, postId: 1, body: '...' }] }]\n */\nexport function embedChildren<T extends Record<string, unknown>>(\n items: T[],\n parentCollection: string,\n childCollection: string,\n db: Database,\n idField: string,\n foreignKeySuffix: string\n): T[] {\n const children = db.getCollection(childCollection);\n\n // If children collection doesn't exist or isn't an array, return items unchanged\n if (!Array.isArray(children)) {\n return items;\n }\n\n const foreignKey = getForeignKey(parentCollection, foreignKeySuffix);\n\n return items.map((item) => {\n // Find all children that reference this parent\n const matchingChildren = children.filter((child) => {\n if (typeof child !== 'object' || child === null) return false;\n const childFk = (child as Record<string, unknown>)[foreignKey];\n const parentId = item[idField];\n return childFk === parentId || String(childFk) === String(parentId);\n });\n\n return {\n ...item,\n [childCollection]: matchingChildren,\n };\n });\n}\n\n/**\n * Expand parent resource into child items\n *\n * @param items - Child items to expand parent into\n * @param childCollection - Name of child collection\n * @param parentCollection - Name of parent collection\n * @param db - Database instance\n * @param idField - ID field name (default: 'id')\n * @param foreignKeySuffix - Foreign key suffix (default: 'Id')\n * @returns Items with expanded parent\n *\n * @example\n * expandParent([{ id: 1, postId: 1 }], 'comments', 'posts', db)\n * // [{ id: 1, postId: 1, post: { id: 1, title: '...' } }]\n */\nexport function expandParent<T extends Record<string, unknown>>(\n items: T[],\n _childCollection: string,\n parentCollection: string,\n db: Database,\n idField: string,\n foreignKeySuffix: string\n): T[] {\n const parents = db.getCollection(parentCollection);\n\n // If parent collection doesn't exist or isn't an array, return items unchanged\n if (!Array.isArray(parents)) {\n return items;\n }\n\n const foreignKey = getForeignKey(parentCollection, foreignKeySuffix);\n\n return items.map((item) => {\n const foreignKeyValue: unknown = item[foreignKey];\n\n if (foreignKeyValue === undefined) {\n return item;\n }\n\n // Find the parent that matches this foreign key\n const parent: unknown = parents.find((p) => {\n if (typeof p !== 'object' || p === null) return false;\n const parentRecord = p as Record<string, unknown>;\n const parentId: unknown = parentRecord[idField];\n // Handle both numeric and string IDs\n if (typeof foreignKeyValue === 'number' || typeof foreignKeyValue === 'string') {\n return parentId === foreignKeyValue || String(parentId) === String(foreignKeyValue);\n }\n return false;\n });\n\n if (!parent || typeof parent !== 'object') {\n return item;\n }\n\n // Use singular form for parent property name\n const parentPropName = parentCollection.endsWith('s')\n ? parentCollection.slice(0, -1)\n : parentCollection;\n\n return {\n ...item,\n [parentPropName]: parent as Record<string, unknown>,\n };\n });\n}\n\n/**\n * Apply relationship parameters (_embed, _expand) to query results\n *\n * @param data - Query results\n * @param resource - Resource name\n * @param embed - Collections to embed\n * @param expand - Collections to expand\n * @param db - Database instance\n * @param idField - ID field name\n * @param foreignKeySuffix - Foreign key suffix\n * @returns Data with relationships applied\n *\n * @example\n * applyRelationships(posts, 'posts', ['comments'], [], db)\n */\nexport function applyRelationships<T extends Record<string, unknown>>(\n data: T[],\n resource: string,\n embed: string[],\n expand: string[],\n db: Database,\n idField: string,\n foreignKeySuffix: string\n): T[] {\n let result = data;\n\n // Apply embeds (include children)\n for (const childCollection of embed) {\n result = embedChildren(result, resource, childCollection, db, idField, foreignKeySuffix);\n }\n\n // Apply expands (include parent)\n for (const parentCollection of expand) {\n result = expandParent(result, resource, parentCollection, db, idField, foreignKeySuffix);\n }\n\n return result;\n}\n\n/**\n * Parse relationship parameters from query\n *\n * @param query - Express query object\n * @returns Arrays of collections to embed and expand\n *\n * @example\n * parseRelationships({ _embed: 'comments' }) // { embed: ['comments'], expand: [] }\n * parseRelationships({ _embed: ['comments', 'likes'] }) // { embed: ['comments', 'likes'], expand: [] }\n */\nexport function parseRelationships(query: Record<string, unknown>): {\n embed: string[];\n expand: string[];\n} {\n const embed: string[] = [];\n const expand: string[] = [];\n\n // Parse _embed\n const embedParam = query._embed;\n if (typeof embedParam === 'string') {\n embed.push(...embedParam.split(','));\n } else if (Array.isArray(embedParam)) {\n for (const e of embedParam) {\n if (typeof e === 'string') {\n embed.push(e);\n }\n }\n }\n\n // Parse _expand\n const expandParam = query._expand;\n if (typeof expandParam === 'string') {\n expand.push(...expandParam.split(','));\n } else if (Array.isArray(expandParam)) {\n for (const e of expandParam) {\n if (typeof e === 'string') {\n expand.push(e);\n }\n }\n }\n\n return { embed, expand };\n}\n","/**\n * Static file server module\n */\n\nimport express, { RequestHandler } from 'express';\nimport { existsSync } from 'fs';\nimport { resolve } from 'path';\n\n/**\n * Static server options\n */\nexport interface StaticOptions {\n directory?: string;\n enabled?: boolean;\n}\n\n/**\n * Create static file server middleware\n *\n * @param options - Static server configuration\n * @returns Express middleware for serving static files\n *\n * @example\n * ```typescript\n * const staticMiddleware = createStaticMiddleware({ directory: './public' });\n * app.use(staticMiddleware);\n * ```\n */\nexport function createStaticMiddleware(options: StaticOptions = {}): RequestHandler {\n const directory = options.directory || './public';\n const enabled = options.enabled !== false;\n const staticPath = resolve(process.cwd(), directory);\n\n // If disabled or directory doesn't exist, return pass-through middleware\n if (!enabled || !existsSync(staticPath)) {\n return (_req, _res, next) => {\n next();\n };\n }\n\n // Serve static files from the directory\n return express.static(staticPath, {\n index: 'index.html',\n dotfiles: 'ignore',\n redirect: true,\n });\n}\n\n/**\n * Create homepage middleware that serves index.html or shows default page\n *\n * @param options - Static server configuration\n * @returns Express request handler\n */\nexport function createHomepageMiddleware(options: StaticOptions = {}): RequestHandler {\n const directory = options.directory || './public';\n const enabled = options.enabled !== false;\n const staticPath = resolve(process.cwd(), directory);\n const indexPath = resolve(staticPath, 'index.html');\n\n return (req, res, next) => {\n // Only handle root path\n if (req.path !== '/') {\n next();\n return;\n }\n\n // If static serving is enabled and index.html exists, let static middleware handle it\n if (enabled && existsSync(indexPath)) {\n next();\n return;\n }\n\n // Otherwise, show default homepage with available routes\n const host = req.get('host') || 'localhost:3000';\n const protocol = req.protocol;\n const baseUrl = `${protocol}://${host}`;\n\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.send(`<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>API Faker</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n line-height: 1.6;\n color: #333;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n min-height: 100vh;\n padding: 2rem;\n }\n .container {\n max-width: 800px;\n margin: 0 auto;\n background: white;\n border-radius: 12px;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n padding: 3rem;\n }\n h1 {\n color: #667eea;\n font-size: 2.5rem;\n margin-bottom: 1rem;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n }\n h2 {\n color: #555;\n font-size: 1.5rem;\n margin-top: 2rem;\n margin-bottom: 1rem;\n border-bottom: 2px solid #667eea;\n padding-bottom: 0.5rem;\n }\n .intro {\n color: #666;\n font-size: 1.1rem;\n margin-bottom: 2rem;\n }\n .endpoint {\n background: #f8f9fa;\n border-left: 4px solid #667eea;\n padding: 1rem;\n margin-bottom: 1rem;\n border-radius: 4px;\n }\n .endpoint a {\n color: #667eea;\n text-decoration: none;\n font-family: 'Monaco', 'Courier New', monospace;\n font-weight: bold;\n }\n .endpoint a:hover {\n text-decoration: underline;\n }\n .endpoint p {\n color: #666;\n margin-top: 0.5rem;\n font-size: 0.95rem;\n }\n .info-box {\n background: #e3f2fd;\n border-left: 4px solid #2196f3;\n padding: 1rem;\n margin-top: 2rem;\n border-radius: 4px;\n }\n .info-box p {\n color: #1565c0;\n margin: 0;\n }\n code {\n background: #f5f5f5;\n padding: 0.2rem 0.4rem;\n border-radius: 3px;\n font-family: 'Monaco', 'Courier New', monospace;\n font-size: 0.9em;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <h1>🚀 API Faker</h1>\n <p class=\"intro\">\n Your JSON REST API is up and running! Use the endpoints below to interact with your data.\n </p>\n\n <h2>📡 Endpoints</h2>\n <div class=\"endpoint\">\n <a href=\"${baseUrl}/db\" target=\"_blank\">${baseUrl}/db</a>\n <p>View the full database</p>\n </div>\n\n <h2>💡 Tips</h2>\n <div class=\"info-box\">\n <p>\n Use query parameters to filter, sort, and paginate your data. \n Examples: <code>?_sort=name&_order=asc</code>, <code>?_page=1&_limit=10</code>\n </p>\n </div>\n </div>\n</body>\n</html>`);\n };\n}\n","import { pathToFileURL } from 'node:url';\nimport { RequestHandler } from 'express';\n\n/**\n * Load a JavaScript or TypeScript module dynamically\n *\n * @param filePath - Path to the module file\n * @returns Module exports\n * @throws Error if module cannot be loaded\n *\n * @example\n * ```typescript\n * const module = await loadModule('./routes.js');\n * ```\n */\nexport async function loadModule(filePath: string): Promise<unknown> {\n try {\n const fileUrl = pathToFileURL(filePath).href;\n const module = (await import(fileUrl)) as { default?: unknown } & Record<string, unknown>;\n return module;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Failed to load module '${filePath}': ${message}\\n` +\n `\\nMake sure:\\n` +\n ` - The file exists and is a valid JavaScript file\\n` +\n ` - The file doesn't have syntax errors\\n` +\n ` - All dependencies are installed`\n );\n }\n}\n\n/**\n * Load custom middleware from a file\n *\n * @param filePath - Path to the middleware file\n * @returns Array of Express middleware functions\n * @throws Error if middleware file is invalid\n *\n * @example\n * ```typescript\n * // middleware.js\n * module.exports = (req, res, next) => {\n * console.log('Custom middleware');\n * next();\n * };\n *\n * // Or export array\n * module.exports = [\n * (req, res, next) => { console.log('First'); next(); },\n * (req, res, next) => { console.log('Second'); next(); }\n * ];\n *\n * // Usage\n * const middlewares = await loadMiddlewares('./middleware.js');\n * middlewares.forEach(mw => app.use(mw));\n * ```\n */\nexport async function loadMiddlewares(filePath: string): Promise<RequestHandler[]> {\n const module = await loadModule(filePath);\n\n // Check for default export first (ES modules or wrapped CommonJS)\n let middlewareExport: unknown;\n if (typeof module === 'object' && module !== null && 'default' in module) {\n const defaultExport = (module as { default: unknown }).default;\n // If default is also an object with a default property, unwrap it\n if (typeof defaultExport === 'object' && defaultExport !== null && 'default' in defaultExport) {\n middlewareExport = (defaultExport as { default: unknown }).default;\n } else {\n middlewareExport = defaultExport;\n }\n } else {\n middlewareExport = module;\n }\n\n // Normalize to array\n const middlewares = Array.isArray(middlewareExport) ? middlewareExport : [middlewareExport];\n\n // Validate all are functions\n for (let i = 0; i < middlewares.length; i++) {\n const mw: unknown = middlewares[i];\n if (typeof mw !== 'function') {\n const indexStr = String(i);\n throw new Error(\n `Middleware file '${filePath}' at index ${indexStr} is not a function, got ${typeof mw}`\n );\n }\n }\n\n return middlewares as RequestHandler[];\n}\n","import { RequestHandler } from 'express';\nimport { readFile } from 'node:fs/promises';\n\n/**\n * Route rewrite rules mapping\n * Maps incoming URL patterns to target URL patterns\n *\n * @example\n * ```json\n * {\n * \"/api/*\": \"/$1\",\n * \"/me\": \"/profile\",\n * \"/news/top\": \"/news?_sort=date&_order=asc&_limit=10\"\n * }\n * ```\n */\nexport type RewriteRules = Record<string, string>;\n\n/**\n * Load rewrite rules from a JSON file\n *\n * @param filePath - Path to the JSON file containing route mappings\n * @returns Rewrite rules object\n * @throws Error if file cannot be loaded or parsed\n *\n * @example\n * ```typescript\n * const rules = await loadRewriteRules('./routes.json');\n * const middleware = createRewriterMiddleware(rules);\n * app.use(middleware);\n * ```\n */\nexport async function loadRewriteRules(filePath: string): Promise<RewriteRules> {\n try {\n const content = await readFile(filePath, 'utf-8');\n const rules = JSON.parse(content) as unknown;\n\n if (typeof rules !== 'object' || rules === null || Array.isArray(rules)) {\n throw new Error('Routes file must contain a JSON object with route mappings');\n }\n\n // Validate all keys and values are strings\n for (const [key, value] of Object.entries(rules)) {\n if (typeof value !== 'string') {\n throw new Error(`Route mapping for '${key}' must be a string, got ${typeof value}`);\n }\n }\n\n return rules as RewriteRules;\n } catch (error) {\n if (error instanceof Error && error.message.includes('Routes file must contain')) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Failed to load routes from '${filePath}': ${message}\\n` +\n `\\nExpected format:\\n` +\n `{\\n` +\n ` \"/api/*\": \"/$1\",\\n` +\n ` \"/users/:id\": \"/api/users/:id\"\\n` +\n `}\\n` +\n `\\nSee examples/routes.json for more examples.`\n );\n }\n}\n\n/**\n * Convert a route pattern with wildcards and parameters to a RegExp\n *\n * @param pattern - Route pattern (e.g., '/api/*', '/posts/:id')\n * @returns Regular expression and parameter names\n *\n * @example\n * ```typescript\n * const { regex, params } = patternToRegex('/api/*');\n * // regex: /^\\/api\\/(.*)$/\n * // params: ['$1']\n *\n * const { regex, params } = patternToRegex('/posts/:id/comments/:commentId');\n * // regex: /^\\/posts\\/([^\\/]+)\\/comments\\/([^\\/]+)$/\n * // params: ['id', 'commentId']\n * ```\n */\nfunction patternToRegex(pattern: string): { regex: RegExp; params: string[] } {\n const params: string[] = [];\n\n // Escape special regex characters except * and :\n let regexPattern = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n // Replace :param with capturing group and collect param names\n regexPattern = regexPattern.replace(/:([^/]+)/g, (_match, param: string) => {\n params.push(param);\n return '([^/]+)';\n });\n\n // Replace * with capturing group\n regexPattern = regexPattern.replace(/\\*/g, () => {\n params.push(`$${String(params.length + 1)}`);\n return '(.*)';\n });\n\n return {\n regex: new RegExp(`^${regexPattern}$`),\n params,\n };\n}\n\n/**\n * Rewrite a URL based on a pattern and substitution\n *\n * @param url - Original URL\n * @param fromPattern - Pattern to match (with * or :param)\n * @param toPattern - Target pattern with $1, $2, etc. or :param\n * @returns Rewritten URL or null if pattern doesn't match\n *\n * @example\n * ```typescript\n * rewriteUrl('/api/posts', '/api/*', '/$1') // → '/posts'\n * rewriteUrl('/me', '/me', '/profile') // → '/profile'\n * rewriteUrl('/posts/1', '/posts/:id', '/items/:id') // → '/items/1'\n * ```\n */\nfunction rewriteUrl(url: string, fromPattern: string, toPattern: string): string | null {\n const { regex, params } = patternToRegex(fromPattern);\n const match = url.match(regex);\n\n if (!match) {\n return null;\n }\n\n // Extract captured values\n const captures = match.slice(1);\n\n // Build the replacement URL\n let result = toPattern;\n\n // Replace $1, $2, etc. with captured values\n for (let i = 0; i < captures.length; i++) {\n const value = captures[i];\n if (value !== undefined) {\n result = result.replace(`$${String(i + 1)}`, value);\n }\n }\n\n // Replace :param with captured values (for named parameters)\n for (let i = 0; i < params.length; i++) {\n const param = params[i];\n const value = captures[i];\n if (param && !param.startsWith('$') && value !== undefined) {\n result = result.replace(`:${param}`, value);\n }\n }\n\n return result;\n}\n\n/**\n * Create Express middleware that rewrites URLs based on rules\n *\n * @param rules - Route rewrite rules\n * @returns Express middleware function\n *\n * @example\n * ```typescript\n * const rules = {\n * '/api/*': '/$1',\n * '/me': '/profile',\n * '/posts/:category': '/posts?category=:category'\n * };\n *\n * const rewriter = createRewriterMiddleware(rules);\n * app.use(rewriter);\n * ```\n */\nexport function createRewriterMiddleware(rules: RewriteRules): RequestHandler {\n return (req, _res, next) => {\n // Try each rule in order\n for (const [from, to] of Object.entries(rules)) {\n const rewritten = rewriteUrl(req.url, from, to);\n\n if (rewritten !== null) {\n req.url = rewritten;\n break; // Apply only the first matching rule\n }\n }\n\n next();\n };\n}\n","import { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\n/**\n * Find the closest matching string using Levenshtein distance\n *\n * @param input - Input string to match\n * @param candidates - Array of candidate strings\n * @returns Closest match or null if no good match found\n */\nfunction findClosestMatch(input: string, candidates: string[]): string | null {\n /* eslint-disable @typescript-eslint/no-non-null-assertion */\n const levenshtein = (a: string, b: string): number => {\n const matrix: number[][] = [];\n\n for (let i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n\n for (let j = 0; j <= a.length; j++) {\n matrix[0]![j] = j;\n }\n\n for (let i = 1; i <= b.length; i++) {\n for (let j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n matrix[i]![j] = matrix[i - 1]![j - 1]!;\n } else {\n matrix[i]![j] = Math.min(\n matrix[i - 1]![j - 1]! + 1,\n matrix[i]![j - 1]! + 1,\n matrix[i - 1]![j]! + 1\n );\n }\n }\n }\n\n return matrix[b.length]![a.length]!;\n };\n /* eslint-enable @typescript-eslint/no-non-null-assertion */\n\n let closestMatch: string | null = null;\n let minDistance = Infinity;\n\n for (const candidate of candidates) {\n const distance = levenshtein(input.toLowerCase(), candidate.toLowerCase());\n if (distance < minDistance && distance <= 3) {\n // Only suggest if distance is 3 or less\n minDistance = distance;\n closestMatch = candidate;\n }\n }\n\n return closestMatch;\n}\n\n/**\n * Configuration options that can be specified in config file or CLI\n */\nexport interface Config {\n port?: number;\n host?: string;\n watch?: boolean;\n routes?: string;\n middlewares?: string;\n static?: string;\n readOnly?: boolean;\n noCors?: boolean;\n noGzip?: boolean;\n snapshots?: string;\n delay?: number;\n id?: string;\n foreignKeySuffix?: string;\n quiet?: boolean;\n}\n\n/**\n * Load configuration from a JSON file\n *\n * @param configPath - Path to the configuration file\n * @returns Configuration object or null if file doesn't exist\n * @throws Error if file exists but is invalid JSON or contains invalid options\n *\n * @example\n * ```typescript\n * const config = loadConfig('rest_api_faker.json');\n * if (config) {\n * console.log('Loaded config:', config);\n * }\n * ```\n */\nexport function loadConfig(configPath: string): Config | null {\n const resolvedPath = resolve(configPath);\n\n // If file doesn't exist, return null (not an error)\n if (!existsSync(resolvedPath)) {\n return null;\n }\n\n try {\n const content = readFileSync(resolvedPath, 'utf-8');\n const config = JSON.parse(content) as unknown;\n\n // Validate config is an object\n if (typeof config !== 'object' || config === null || Array.isArray(config)) {\n throw new Error('Config file must contain a JSON object');\n }\n\n // Validate config properties\n validateConfig(config as Record<string, unknown>);\n\n return config as Config;\n } catch (error) {\n if (error instanceof Error && error.message.includes('Config file must contain')) {\n throw error;\n }\n throw new Error(\n `Failed to load config from '${configPath}': ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\n/**\n * Validate configuration object\n *\n * @param config - Configuration object to validate\n * @throws Error if configuration contains invalid options\n */\nfunction validateConfig(config: Record<string, unknown>): void {\n const validOptions = new Set([\n 'port',\n 'host',\n 'watch',\n 'routes',\n 'middlewares',\n 'static',\n 'readOnly',\n 'noCors',\n 'noGzip',\n 'snapshots',\n 'delay',\n 'id',\n 'foreignKeySuffix',\n 'quiet',\n ]);\n\n // Check for unknown options\n for (const key of Object.keys(config)) {\n if (!validOptions.has(key)) {\n const suggestion = findClosestMatch(key, Array.from(validOptions));\n const didYouMean = suggestion ? ` Did you mean '${suggestion}'?` : '';\n throw new Error(\n `Unknown config option: '${key}'.${didYouMean} Valid options are: ${Array.from(validOptions).join(', ')}`\n );\n }\n }\n\n // Validate types\n if (\n 'port' in config &&\n (typeof config.port !== 'number' || config.port < 0 || config.port > 65535)\n ) {\n throw new Error(\"Config option 'port' must be a number between 0 and 65535\");\n }\n\n if ('host' in config && typeof config.host !== 'string') {\n throw new Error(\"Config option 'host' must be a string\");\n }\n\n if ('watch' in config && typeof config.watch !== 'boolean') {\n throw new Error(\"Config option 'watch' must be a boolean\");\n }\n\n if ('routes' in config && typeof config.routes !== 'string') {\n throw new Error(\"Config option 'routes' must be a string\");\n }\n\n if ('middlewares' in config && typeof config.middlewares !== 'string') {\n throw new Error(\"Config option 'middlewares' must be a string\");\n }\n\n if ('static' in config && typeof config.static !== 'string') {\n throw new Error(\"Config option 'static' must be a string\");\n }\n\n if ('readOnly' in config && typeof config.readOnly !== 'boolean') {\n throw new Error(\"Config option 'readOnly' must be a boolean\");\n }\n\n if ('noCors' in config && typeof config.noCors !== 'boolean') {\n throw new Error(\"Config option 'noCors' must be a boolean\");\n }\n\n if ('noGzip' in config && typeof config.noGzip !== 'boolean') {\n throw new Error(\"Config option 'noGzip' must be a boolean\");\n }\n\n if ('snapshots' in config && typeof config.snapshots !== 'string') {\n throw new Error(\"Config option 'snapshots' must be a string\");\n }\n\n if ('delay' in config && (typeof config.delay !== 'number' || config.delay < 0)) {\n throw new Error(\"Config option 'delay' must be a non-negative number\");\n }\n\n if ('id' in config && typeof config.id !== 'string') {\n throw new Error(\"Config option 'id' must be a string\");\n }\n\n if ('foreignKeySuffix' in config && typeof config.foreignKeySuffix !== 'string') {\n throw new Error(\"Config option 'foreignKeySuffix' must be a string\");\n }\n\n if ('quiet' in config && typeof config.quiet !== 'boolean') {\n throw new Error(\"Config option 'quiet' must be a boolean\");\n }\n}\n\n/**\n * Merge CLI arguments with config file\n * CLI arguments take precedence over config file values\n *\n * @param cliConfig - Configuration from CLI arguments\n * @param fileConfig - Configuration from config file\n * @returns Merged configuration\n *\n * @example\n * ```typescript\n * const fileConfig = loadConfig('rest_api_faker.json');\n * const merged = mergeConfig(cliArgs, fileConfig);\n * ```\n */\nexport function mergeConfig(cliConfig: Partial<Config>, fileConfig: Config | null): Config {\n if (!fileConfig) {\n return cliConfig as Config;\n }\n\n // CLI arguments override config file values\n return {\n ...fileConfig,\n ...cliConfig,\n };\n}\n"],"mappings":"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","names":["import_fs","import_path","module","import_express","pc","import_express","import_fs","import_path","express","module","express","cors","compression","yargs"]}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * API Faker - Main entry point for programmatic usage\n *\n * @example\n * ```typescript\n * import { create, router, defaults } from '
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * API Faker - Main entry point for programmatic usage\n *\n * @example\n * ```typescript\n * import { create, router, defaults } from 'rest_api_faker';\n *\n * const server = create();\n * const apiRouter = router('db.json');\n *\n * server.use(defaults());\n * server.use(apiRouter);\n * server.listen(3000);\n * ```\n */\n\n// TODO: Implement in Phase 7\nexport function create(): void {\n throw new Error('Not implemented yet - coming in Phase 7');\n}\n\nexport function router(): void {\n throw new Error('Not implemented yet - coming in Phase 7');\n}\n\nexport function defaults(): void {\n throw new Error('Not implemented yet - coming in Phase 7');\n}\n\nexport function rewriter(): void {\n throw new Error('Not implemented yet - coming in Phase 7');\n}\n\nexport const bodyParser = {};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBO,SAAS,SAAe;AAC7B,QAAM,IAAI,MAAM,yCAAyC;AAC3D;AAEO,SAAS,SAAe;AAC7B,QAAM,IAAI,MAAM,yCAAyC;AAC3D;AAEO,SAAS,WAAiB;AAC/B,QAAM,IAAI,MAAM,yCAAyC;AAC3D;AAEO,SAAS,WAAiB;AAC/B,QAAM,IAAI,MAAM,yCAAyC;AAC3D;AAEO,IAAM,aAAa,CAAC;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rest_api_faker",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Get a full fake REST API with zero coding in less than 30 seconds",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"bin": {
|
|
8
|
-
"
|
|
8
|
+
"rest_api_faker": "./dist/cli.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "tsup",
|
|
63
|
-
"dev": "tsup && nodemon --watch dist --exec \"node
|
|
63
|
+
"dev": "tsup && nodemon --watch dist --exec \"node dist/cli.js db.json\"",
|
|
64
64
|
"test": "vitest",
|
|
65
65
|
"test:run": "vitest run",
|
|
66
66
|
"test:coverage": "vitest --coverage",
|