fakelab 1.0.3 → 1.0.4

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/database.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./lib/database";
package/database.js ADDED
@@ -0,0 +1 @@
1
+ export * from "./lib/database.js";
package/lib/cli.js CHANGED
@@ -1,14 +1,6 @@
1
- import b from'path';import d from'fs-extra';import {Command}from'commander';import $ from'express';import oe from'cors';import ae from'http';import pe from'express-ejs-layouts';import {Project}from'ts-morph';import g from'picocolors';import {JSONFilePreset}from'lowdb/node';import {fileURLToPath}from'url';import Y from'qs';import ce from'figlet';import re from'mitt';import {transform}from'esbuild';import {bundleRequire}from'bundle-require';import ue from'joycon';var q=new Intl.ListFormat("en",{style:"long",type:"unit"}),o=class{static label(e){switch(e){case "info":return g.blueBright(e.toUpperCase());case "error":return g.redBright(e.toUpperCase());case "warn":return g.yellowBright(e.toUpperCase());case "success":return g.greenBright(e.toUpperCase())}}static log(e,t){return [g.dim(`[${new Date().toISOString()}]`),this.label(e),t].join(" ")}static blue(e){return g.blueBright(e)}static red(e){return g.redBright(e)}static yellow(e){return g.yellowBright(e)}static green(e){return g.greenBright(e)}static info(e,...t){console.log(this.log("info",e),...t);}static warn(e,...t){console.log(this.log("warn",e),...t);}static error(e,...t){console.error(this.log("error",e),...t);}static success(e,...t){console.log(this.log("success",e),...t);}static debug(e,...t){typeof process>"u"||!process.env.DEBUG||console.log(this.log("info",e),...t);}static list(e){return q.format(e)}};var T=class{constructor(e){this.faker=e;}JSDOC_FAKER_FIELD="faker";FAKER_TAG_REGEX=/^([a-zA-Z0-9._]+)(?:\((.*)\))?$/;boolMapping={true:true,false:false};string(e){return this.execute(e,this.faker.word.noun)}int(e){return this.execute(e,this.faker.number.int)}bool(e){return this.execute(e,this.faker.datatype.boolean)}bigInt(e){return this.execute(e,this.faker.number.bigInt)}litbool(e){return this.boolMapping[e]}async object(e,t){let r={};return await Promise.all(e.map(async s=>{let i=s.getTypeAtLocation(s.getValueDeclarationOrThrow());r[s.getName()]=await t(i,this,this.readJSDocTags(s));})),r}async union(e){let t=await Promise.all(e);return t[Math.floor(Math.random()*t.length)]}async intersection(e){let t=await Promise.all(e);return t[Math.floor(Math.random()*t.length)]}evalArgs(e){if(!(!e||!e.trim()))return Function(`"use strict"; return (${e});`)()}readJSDocTags(e){let t=e.getJsDocTags().filter(r=>r.getName()===this.JSDOC_FAKER_FIELD);return t.length===0?[]:t.map(r=>{let[s]=r.getText();if(!s)return;let i=s.text.trim().match(this.FAKER_TAG_REGEX);if(!i)return;let[,n,p]=i,l=this.evalArgs(p);return {path:n,args:l}})}execute(e,t){if(!e)return t();let r=e.path.split("."),s=this.faker;for(let i of r)s=s[i],s||(o.error("Invalid faker module path: (%s)",e.path),process.exit(1));typeof s!="function"&&(o.error("Unresolvable faker function. (%s)",e.path),process.exit(1));try{return e.args?s(e.args):s()}catch(i){return o.error("Passed invalid arguments to faker function. error: %s",i),s()}}};var h=b.dirname(fileURLToPath(import.meta.url)),m=process.cwd();var y=class a{constructor(e){this.config=e;this.enabled=this.enabled.bind(this),this.options=this.config.options.database(),this.options.enabled||d.rmSync(a.DATABASE_DIR,{force:true,recursive:true});}options;static DATABASE_DIR=b.resolve(m,".fakelab/db");static register(e){return new a(e)}enabled(){return this.options.enabled??false}async initialize(){if(this.enabled())try{await d.ensureDir(a.DATABASE_DIR),await this.modifyGitignoreFile(".fakelab/*");}catch(e){o.error("Could not create database. error: %s",e);}}async modifyGitignoreFile(e){try{let t=b.resolve(m,".gitignore");if((await d.readFile(t,{encoding:"utf8"})).split(`
2
- `).some(s=>s.trim()===e.trim()))return;await d.appendFile(t,`
3
- ${e}`);}catch(t){o.warn("Cannot modify .gitignore file. error: %s",t);}}};var k=class{constructor(e,t){this.files=e;this.tsConfigFilePath=t;let s=new Project({tsConfigFilePath:this.tsConfigFilePath}).addSourceFilesAtPaths(this.files);this.__targets=s.flatMap(i=>{let n=i.getInterfaces(),p=i.getTypeAliases(),l=i.getExportDeclarations().flatMap(c=>c.getNamedExports().flatMap(u=>u.getLocalTargetDeclarations()));return [...n,...p,...l]}),this.generateInFileEntitiyMap(),this.generateTypeUtils();}__targets;async run(e){return await e()}normalizePath(e){return e.split(b.sep).join(b.posix.sep)}address(e,t){let r=this.normalizePath(m);return `${e.replace(r,"")}/${t}`}generateTypeUtils(){let t=`
4
- interface $$ {
5
- ${[...new Set(this.__targets.map(r=>{let s=r.getName(),i=r.getSourceFile().getFilePath();return `${s.toLowerCase()}: import("${i}").${s}`}))].join(`
6
- `)}
7
- }`;d.appendFile(b.resolve(h,"type-utils.d.ts"),t);}generateInFileEntitiyMap(){let t=`
8
- interface Runtime$ {
9
- ${[...new Set(this.__targets.map(r=>{let s=r.getName(),i=r.getSourceFile().getFilePath();return `${s.toLowerCase()}: import("${i}").${s}`}))].join(`
10
- `)}
11
- }`;d.appendFile(b.resolve(h,"runtime.d.ts"),t);}async entities(){let e=await Promise.all(this.__targets.map(async t=>{let r=t.getName().toLowerCase(),s=t.getType(),i=this.normalizePath(t.getSourceFile().getDirectoryPath()),n=t.getSourceFile().getBaseName(),p=this.address(i,n),l=b.resolve(y.DATABASE_DIR,`${r}.json`),c=this.address(this.normalizePath(y.DATABASE_DIR),b.basename(l)),u=i.includes("/.fakelab/snapshots"),f=await JSONFilePreset(l,[]);return [r,{type:s,filepath:p,snapshot:u,table:f,tablepath:c}]}));return new Map(e.sort((t,r)=>Number(t[1].snapshot)-Number(r[1].snapshot)))}async initFakerLibrary(e){let{faker:t}=await import(`@faker-js/faker/locale/${e.locale}`);return t}};async function S(a,e,t=[],r=0){if(a.isString())return e.string(t[r]);if(a.isNumber())return e.int(t[r]);if(a.isBoolean())return e.bool(t[r]);if(a.isBigInt())return e.bigInt(t[r]);if(a.isBooleanLiteral())return e.litbool(a.getText());if(a.isLiteral())return a.getLiteralValue();if(!a.isUndefined()){if(a.isUnion()){let s=a.getUnionTypes();return await e.union(s.map((i,n)=>S(i,e,t,n)))}if(a.isIntersection()){let s=a.getIntersectionTypes();return await e.intersection(s.map((i,n)=>S(i,e,t,n)))}if(a.isArray()){let s=a.getArrayElementTypeOrThrow();return [await S(s,e,t,r)]}if(a.isObject()){let s=a.getProperties();return await e.object(s,(i,n,p)=>S(i,n,p,r))}return null}}function X({each:a}){return {resolve:async t=>await Promise.all(Array.from({length:t},a))}}async function B(a,e){let t=await a.files(e.source),r=new k(t,e.tsConfig||a.tsConfig()),s=await r.entities(),i=await r.initFakerLibrary(a.options.faker(e.locale)),n=new T(i);async function p(l,c){let u=X({each:()=>S(l,n)}),f=await(c.count?u.resolve(parseInt(String(c.count))):S(l,n)),w=JSON.stringify(f,null,2);return {data:f,json:w}}return {entities:s,build:p}}var D=class{constructor(e,t,r){this.builder=e;this.database=t;this.pkg=r;}async handleQueries(e){let t=e.query.count;return t?{count:t.toString()}:{}}index(){return (e,t)=>{t.render("index",{name:null,entities:this.builder.entities,version:this.pkg.version,enabled:this.database.enabled()});}}preview(e){return async(t,r)=>{let s=`${t.protocol}://${t.host}/`,i=t.params.name,n=await this.handleQueries(t),p=Y.stringify(n,{addQueryPrefix:true}),l=this.builder.entities.get(i.toLowerCase());if(l){let{json:c}=await this.builder.build(l.type,n),u=l.filepath;r.render("preview",{name:i,suffix:l.snapshot?"(snapshot)":"",filepath:u,address:s,search:p,json:c,prefix:e,entities:this.builder.entities,version:this.pkg.version,enabled:this.database.enabled()});}else r.redirect("/");}}db(){return (e,t)=>{this.database.enabled()?t.render("database",{name:null,entities:this.builder.entities,version:this.pkg.version}):t.redirect("/");}}table(e){return async(t,r)=>{let s=`${t.protocol}://${t.host}/`,i=t.params.name,n=this.builder.entities.get(i.toLowerCase());if(!this.database.enabled())r.redirect("/");else if(n){await n.table.read();let l=n.table.data.length>0,c=JSON.stringify(n.table.data,null,2),u=n.filepath;r.render("table",{name:i,filepath:u,address:s,prefix:e,json:c,hasData:l,entities:this.builder.entities,version:this.pkg.version});}else r.redirect("/database");}}};var C=class{constructor(e,t,r){this.builder=e;this.network=t;this.database=r;}SEED_MERGE_THRESHOLD=1e3;async handleQueries(e){let t=e.query.count;return t?{count:t.toString()}:{}}async applyNetworkHandlers(e){if(this.network.offline()){let{status:t,message:r}=this.network.state("offline");return e.status(t).json({message:r}),true}if(await this.network.wait(),this.network.timeout())return true;if(this.network.error()){let{status:t,message:r}=this.network.state("error");return e.status(t).json({message:r}),true}return false}entity(){return async(e,t)=>{try{if(await this.applyNetworkHandlers(t))return;let r=e.params.name,s=await this.handleQueries(e),i=this.builder.entities.get(r.toLowerCase());if(i){let{data:n}=await this.builder.build(i.type,s);t.status(200).json(n);}else t.status(400).json({message:"The entity is not exists"});}catch(r){t.status(500).send(r);}}}getTable(){return async(e,t)=>{try{if(!this.database.enabled())return t.status(403).json({message:"database is not enabled or initialized."});if(await this.applyNetworkHandlers(t))return;let r=e.params.name,s=this.builder.entities.get(r.toLowerCase());s?(await s.table.read(),t.status(200).json(s.table.data)):t.status(400).json({message:`${r} table is not exists`});}catch(r){t.status(500).send(r);}}}updateTable(){return async(e,t)=>{try{if(!this.database.enabled())return t.status(403).json({message:"database is not enabled or initialized."});if(await this.applyNetworkHandlers(t))return;let r=e.params.name,s=await this.handleQueries(e),i=this.builder.entities.get(r.toLowerCase());if(i){let{data:n}=await this.builder.build(i.type,s);await i.table.update(p=>p.push(...Array.isArray(n)?n:[n])),t.status(200).json({success:!0});}else t.status(400).json({success:!1,message:`${r} table is not exists`});}catch(r){t.status(500).send(r);}}}insert(){return async(e,t)=>{try{if(!this.database.enabled())return t.status(403).json({message:"database is not enabled or initialized."});let r=e.body?.count||1,s=e.body?.strategy||"reset",i=e.params.name,n=this.builder.entities.get(i.toLowerCase());if(n){let{data:p}=await this.builder.build(n.type,{count:r});if(await n.table.read(),s==="once"&&n.table.data.length>0)return t.status(200).json({message:`${i} entity was seeded once before.`});await n.table.update(l=>{s!=="merge"&&(l.length=0);let c=Array.isArray(p)?p:[p];l.length+c.length<this.SEED_MERGE_THRESHOLD&&l.push(...c);}),t.status(200).json({success:!0});}else t.status(400).json({success:!1,message:"The table is not exists"});}catch(r){t.status(500).send(r);}}}flush(){return async(e,t)=>{try{if(!this.database.enabled())return t.status(403).json({message:"database is not enabled or initialized."});let r=e.params.name,s=this.builder.entities.get(r.toLowerCase());s?(await s.table.update(i=>i.length=0),t.status(200).json({success:!0})):t.status(400).json({success:!1,message:"The table is not exists"});}catch(r){t.status(500).send(r);}}}_update(){return async(e,t)=>{try{if(!this.database.enabled())return t.status(403).json({message:"database is not enabled or initialized."});let r=e.params.name,s=await this.handleQueries(e),i=this.builder.entities.get(r.toLowerCase());if(i){let{data:n}=await this.builder.build(i.type,s);await i.table.update(p=>p.push(n)),t.status(301).redirect(`/database/${r.toLowerCase()}`);}else t.status(400).redirect("/database");}catch{t.status(500).redirect("/database");}}}_clear(){return async(e,t)=>{try{if(!this.database.enabled())return t.status(403).json({message:"database is not enabled or initialized."});let r=e.params.name,s=this.builder.entities.get(r.toLowerCase());s?(await s.table.read(),s.table.data.length>0&&await s.table.update(i=>i.length=0),t.status(301).redirect(`/database/${r.toLowerCase()}`)):t.status(400).redirect("/database");}catch{t.status(500).redirect("/database");}}}};var te=d.readJSONSync(b.join(h,"../package.json")),L=class{constructor(e,t,r,s,i){this.router=e;this.config=t;this.network=r;this.database=s;this.options=i;let{pathPrefix:n}=this.config.options.server(this.options.pathPrefix,this.options.port);this.prefix=n;}prefix;async register(){let e=await B(this.config,this.options),t=new D(e,this.database,te),r=new C(e,this.network,this.database);this.router.get("/",t.index()),this.router.get("/entities/:name",t.preview(this.prefix)),this.router.get("/database",t.db()),this.router.get("/database/:name",t.table(this.prefix)),this.router.get(`/${this.prefix}/:name`,r.entity()),this.router.get(`/${this.prefix}/database/:name`,r.getTable()),this.router.post(`/${this.prefix}/database/:name`,r.updateTable()),this.router.post(`/${this.prefix}/database/insert/:name`,r.insert()),this.router.post(`/${this.prefix}/database/flush/:name`,r.flush()),this.router.post("/__update/:name",r._update()),this.router.post("/__delete/:name",r._clear());}};var N=class a{constructor(e){this.config=e;this.options=this.config.options.network(),this.timeout=this.timeout.bind(this),this.error=this.error.bind(this),this.state=this.state.bind(this),this.middleware=this.middleware.bind(this),this.wait=this.wait.bind(this),this.offline=this.offline.bind(this);}options;static initHandlers(e){return new a(e)}timeout(){let e=this.chance(this.options.timeoutRate);return e&&o.debug("Network timeout..."),e}error(){let e=this.chance(this.options.errorRate);return e&&o.debug("Network error..."),e}state(e){switch(e){case "error":{let t=this.options.errors?.statusCodes||[],r=t.length>0?t[Math.floor(Math.random()*t.length)]:500,s=this.options.errors?.messages?.[r]??"Network error";return {status:r,message:s}}case "offline":return {status:503,message:"Network offline"};default:return {status:500,message:"Server unknown error"}}}async wait(){let e=this.resolveDelay();e>0&&(o.debug("Network waiting (%d ms)...",e),await this.sleep(e));}offline(){return this.options.offline??false}middleware(e,t,r){let s=this.options.errorRate||0,i=this.options.timeoutRate||0,n=this.options.offline??false,p=`delay=${this.resolveDelay()},error=${s},timeout=${i},offline=${n}`;t.setHeader("X-Fakelab-Network",p),r();}resolveDelay(){let e=this.options.delay;return typeof e=="number"?Math.round(e):Array.isArray(e)?this.random(e):0}chance(e=0){return Math.random()<e}random([e,t]){return Math.round(Math.random()*(t-e)+e)}sleep(e){return new Promise(t=>setTimeout(t,e))}};var E=class a{constructor(e,t){this.subscriber=e;this.config=t;this.options=this.config.options.webhook(),this.dispose();}options;unsubs=new Set;history=new Set;activated=false;static processHandlersRegistered=false;isActivated(){return this.activated}activate(){if(this.flush("REACTIVATE"),!this.options.enabled){o.warn("Webhook is disabled. Skipping activation.");return}if(this.options.hooks.length===0){o.debug?.("Webhook enabled but no hooks configured. Skipping activation.");return}this.activated=true;for(let e of this.options.hooks){let{error:t,message:r,args:s=[]}=this.validateHook(e);if(t){o.error(r,...s);continue}if(this.history.has(e.name))return;let i=new AbortController,n=this.subscriber.subscribe(e.name,e.trigger.event,p=>this.handle(e,p,i.signal));this.history.add(e.name),this.unsubs.add(p=>{try{n();}finally{this.history.delete(e.name),i.abort(p);}});}}flush(e){this.subscriber.clear();for(let t of this.unsubs)t(e);this.unsubs.clear();}async handle({name:e,method:t,url:r,headers:s,transform:i,trigger:n},p,l){let c=p;try{c=typeof i=="function"?i(p):p;}catch(u){o.error("Webhook %s payload transformation failed. error: %s",o.blue(e),u);}l.aborted?o.error("Webhook %s aborted",o.blue(e)):l.addEventListener("abort",()=>{o.error("Webhook %s aborted",o.blue(e));});try{o.info("Delivering %s to %s",o.blue(e),o.blue(r)),(await fetch(r,{method:t,body:JSON.stringify(c),signal:l,headers:this.requestHeaders(e,n.event,s)})).ok?o.success("Webhook %s delivered successfully.",o.blue(e)):o.error("Webhook %s request failed.",o.blue(e));}catch(u){o.error("Webhook %s network error: %s",o.blue(e),u);}}validateHook(e){if(e.method.toUpperCase()!=="POST")return {error:true,message:"Webhook hook method must be 'POST'. received %s",args:[e.method]};try{let t=new URL(e.url);if(t.protocol!=="http:"&&t.protocol!=="https:")return {error:!0,message:'Webhook hook URL must use http/https. Received "%s".',args:[t.protocol]}}catch(t){return {error:true,message:"Webhook hook URL is invalid. Received: %s. error: %s",args:[e.url,JSON.stringify(t)]}}return {error:false,message:null}}requestHeaders(e,t,r){let s=new Headers(r);return s.append("Content-Type","application/json"),s.append("X-Fakelab-Webhook",`name=${e},event=${t}`),s}dispose(){a.processHandlersRegistered||(a.processHandlersRegistered=true,["SIGINT","SIGTERM","SIGQUIT"].forEach(e=>process.on(e,()=>{this.activated=false,this.history.clear(),this.flush(e),process.exit(0);})));}};var x=class{constructor(e){this.$emitter=e;this.subscribe=this.subscribe.bind(this);}subscribe(e,t,r){return this.$emitter.on(t,r),()=>{o.info("Webhook %s unsubscribed.",o.blue(e)),this.$emitter.off(t,r);}}clear(){this.$emitter.all.clear();}};var _=class extends x{constructor(t){let r=re();super(r);this.hooks=t;this.captured=this.captured.bind(this),this.refreshed=this.refreshed.bind(this),this.deleted=this.deleted.bind(this),this.$emitter.all.clear();}captured(t){let r=this.getTriggeredHook("snapshot:captured");r?(o.info("Dispatching %s for event %s.",o.blue(r.name),o.blue("snapshot:captured")),this.$emitter.emit("snapshot:captured",t)):o.warn("Webhook skipped: missing trigger event %s.",o.blue("snapshot:captured"));}refreshed(t){let r=this.getTriggeredHook("snapshot:refreshed");r?(o.info("Dispatching %s for event %s.",o.blue(r.name),o.blue("snapshot:refreshed")),this.$emitter.emit("snapshot:refreshed",t)):o.warn("Webhook skipped: missing trigger event %s.",o.blue("snapshot:refreshed"));}deleted(t){let r=this.getTriggeredHook("snapshot:deleted");r?(o.info("Dispatching %s for event %s.",o.blue(r.name),o.blue("snapshot:deleted")),this.$emitter.emit("snapshot:deleted",t)):o.warn("Webhook skipped: missing trigger event %s.",o.blue("snapshot:deleted"));}getTriggeredHook(t){return this.hooks.find(r=>r.trigger.event===t)}};var F=class extends x{constructor(){let e=re();super(e),this.started=this.started.bind(this),this.shutdown=this.shutdown.bind(this);}started({pathPrefix:e,port:t}){this.$emitter.emit("server:started",{port:t,prefix:e});}shutdown({pathPrefix:e,port:t}){this.$emitter.emit("server:shutdown",{port:t,prefix:e});}};var G=`import { faker } from "@faker-js/faker/locale/LOCALE";
1
+ import v from'path';import g from'fs-extra';import {Command}from'commander';import W from'express';import He from'cors';import Me from'http';import Be from'express-ejs-layouts';import {Project,Node}from'ts-morph';import w from'picocolors';import {JSONFilePreset}from'lowdb/node';import {fileURLToPath}from'url';import {createHash}from'crypto';import {createHandler}from'graphql-http/lib/use/express';import {GraphQLString,GraphQLFloat,GraphQLBoolean,GraphQLList,GraphQLObjectType,GraphQLInputObjectType,GraphQLNonNull,GraphQLID,GraphQLSchema,GraphQLInt}from'graphql';import xe from'qs';import qe from'figlet';import De from'mitt';import {transform}from'esbuild';import Ie from'chokidar';import {bundleRequire}from'bundle-require';import $e from'joycon';var de=new Intl.ListFormat("en",{style:"long",type:"unit"}),o=class{static label(e){switch(e){case "info":return w.blueBright(e.toUpperCase());case "error":return w.redBright(e.toUpperCase());case "warn":return w.yellowBright(e.toUpperCase());case "success":return w.greenBright(e.toUpperCase())}}static log(e,t){return [w.dim(`[${new Date().toISOString()}]`),this.label(e),t].join(" ")}static blue(e){return w.blueBright(e)}static red(e){return w.redBright(e)}static yellow(e){return w.yellowBright(e)}static green(e){return w.greenBright(e)}static info(e,...t){console.log(this.log("info",e),...t);}static warn(e,...t){console.log(this.log("warn",e),...t);}static error(e,...t){console.error(this.log("error",e),...t);}static success(e,...t){console.log(this.log("success",e),...t);}static debug(e,...t){typeof process>"u"||!process.env.DEBUG||console.log(this.log("info",e),...t);}static list(e){return de.format(e)}};var O=class{constructor(e){this.faker=e;}JSDOC_FAKER_FIELD="faker";FAKER_TAG_REGEX=/^([a-zA-Z0-9._]+)(?:\((.*)\))?$/;boolMapping={true:true,false:false};string(e){return this.execute(e,this.faker.word.noun)}int(e){return this.execute(e,this.faker.number.int)}bool(e){return this.execute(e,this.faker.datatype.boolean)}bigInt(e){return this.execute(e,this.faker.number.bigInt)}litbool(e){return this.boolMapping[e]}async object(e,t){let r={};return await Promise.all(e.map(async s=>{let i=s.getTypeAtLocation(s.getValueDeclarationOrThrow());r[s.getName()]=await t(i,this,this.readJSDocTags(s));})),r}async union(e){let t=await Promise.all(e);return t[Math.floor(Math.random()*t.length)]}async intersection(e){let t=await Promise.all(e);return t[Math.floor(Math.random()*t.length)]}async tuple(e){return await Promise.all(e)}evalArgs(e){if(!(!e||!e.trim()))return Function(`"use strict"; return (${e});`)()}readJSDocTags(e){let t=e.getJsDocTags().filter(r=>r.getName()===this.JSDOC_FAKER_FIELD);return t.length===0?[]:t.map(r=>{let[s]=r.getText();if(!s)return;let i=s.text.trim().match(this.FAKER_TAG_REGEX);if(!i)return;let[,a,n]=i,c=this.evalArgs(n);return {path:a,args:c}})}execute(e,t){if(!e)return t();let r=e.path.split("."),s=this.faker;for(let i of r)s=s[i],s||(o.error("Invalid faker module path: (%s)",e.path),process.exit(1));typeof s!="function"&&(o.error("Unresolvable faker function. (%s)",e.path),process.exit(1));try{return e.args?s(e.args):s()}catch(i){return o.error("Passed invalid arguments to faker function. error: %s",i),s()}}};var d=v.dirname(fileURLToPath(import.meta.url)),m=process.cwd();var k=class p{constructor(e){this.config=e;this.enabled=this.enabled.bind(this),this.options=this.config.options.database(),this.options.enabled||g.rmSync(p.DATABASE_DIR,{force:true,recursive:true});}options;static DATABASE_DIR=v.resolve(m,".fakelab/db");static register(e){return new p(e)}enabled(){return this.options.enabled??false}async initialize(){if(this.enabled()){if(!this.config.enabled()){o.warn("Fakelab is disabled. Skipping database initialization.");return}try{await g.ensureDir(p.DATABASE_DIR),await this.modifyGitignoreFile(".fakelab/*");}catch(e){o.error("Could not create database. error: %s",e);}}}async modifyGitignoreFile(e){try{let t=v.resolve(m,".gitignore");if((await g.readFile(t,{encoding:"utf8"})).split(`
2
+ `).some(s=>s.trim()===e.trim()))return;await g.appendFile(t,`
3
+ ${e}`);}catch(t){o.warn("Cannot modify .gitignore file. error: %s",t);}}};var ie=`import { faker } from "@faker-js/faker/locale/LOCALE";
12
4
  const _fakelab = {};
13
5
  _fakelab.url = () => "http://localhost:PORT/PREFIX/";
14
6
 
@@ -23,7 +15,9 @@ const functions = {
23
15
  };
24
16
 
25
17
 
26
- _fakelab.genSync = function (name, options) {
18
+ _fakelab.enabled = () => FAKELAB_ENABLED;
19
+
20
+ _fakelab.gen = function (name, options) {
27
21
  const count = _count(options);
28
22
 
29
23
  if(count === null) return functions[name]();
@@ -31,23 +25,73 @@ _fakelab.genSync = function (name, options) {
31
25
  return Array.from({length:count},() => functions[name]())
32
26
  }
33
27
 
34
- _fakelab.gen = async function (name, options) {
35
- return _fakelab.genSync(name, options);
36
- }
37
28
 
29
+ _fakelab.fetch = async function (name, options) {
30
+ console.warn("[fakelab] fetch() is disabled in headless mode.")
31
+
32
+ return {}
33
+ };
34
+
35
+ _fakelab.enabled = _fakelab.enabled.bind(_fakelab);
38
36
  _fakelab.url = _fakelab.url.bind(_fakelab);
39
37
  _fakelab.gen = _fakelab.gen.bind(_fakelab);
40
- _fakelab.genSync = _fakelab.genSync.bind(_fakelab);
38
+ _fakelab.fetch = _fakelab.fetch.bind(_fakelab);
41
39
 
42
40
  const fakelab = Object.freeze(_fakelab);
43
41
 
44
42
 
45
43
  export {fakelab};
46
- `;var P=class{constructor(e){this.config=e;}HEADLESS_FILENAME="runtime.js";JSDOC_FAKER_FIELD="faker";FAKER_TAG_REGEX=/^([a-zA-Z0-9._]+)(?:\((.*)\))?$/;async generate(e){try{let t=await this.config.files(e),s=await new k(t,this.config.tsConfig()).entities(),i=[],n=new Map;for(let[u]of s.entries())n.set(u.toLowerCase(),u);for(let[u,f]of s.entries()){let w=this.generateFunction(f.type,u,n);i.push(w);}let{locale:p}=this.config.options.faker(),{code:l}=await transform(this.generateSource(i,p),{minify:!0,platform:"browser",target:"es2022"}),c=b.resolve(h,this.HEADLESS_FILENAME);return await d.ensureDir(b.dirname(c)),await Promise.all([d.writeFile(c,l)]),!0}catch(t){return o.error(t instanceof Error?t.message:t),false}}replacer(e,t){let r=e;for(let s in t)r=r.replace(new RegExp(s,"g"),t[s].toString().trim());return r}generateSource(e,t){let{pathPrefix:r,port:s}=this.config.options.server();return this.replacer(G,{PORT:s,PREFIX:r,LOCALE:t,FUNCTIONS:e.join(`,
47
- `)})}generateFunction(e,t,r){let i=this.extractProperties(e).map(n=>this.generateProperty(n,r,1));return `${t}(){return {${i.join(",")}};}`}extractProperties(e){return e.isObject()?e.getProperties().map(r=>{let s=r.getTypeAtLocation(r.getValueDeclarationOrThrow()),i=this.extractFakerTag(r);return {name:r.getName(),type:s,fakerTag:i,array:s.isArray()}}):[]}extractFakerTag(e){let t=e.getJsDocTags().filter(s=>s.getName()===this.JSDOC_FAKER_FIELD);if(t.length===0)return;let[r]=t[0].getText();if(r)return r.text.trim()}generateProperty(e,t,r=0){let{name:s,type:i,fakerTag:n,array:p}=e,l=" ".repeat(r);if(n){let c=this.parseFakerTag(n);return p?`${s}: [${c}]`:`${s}: ${c}`}if(i.isArray()){let u=i.getArrayElementTypeOrThrow().getSymbol();if(u){let f=u.getName().toLowerCase();if(t.has(f))return `${s}: [this.${f}()]`}}else {let c=i.getSymbol();if(c){let u=c.getName().toLowerCase();if(t.has(u))return `${s}: this.${u}()`}}if(i.isString())return p?`${s}: [faker.word.noun()]`:`${s}: faker.word.noun()`;if(i.isNumber())return p?`${s}: [faker.number.int()]`:`${s}: faker.number.int()`;if(i.isBoolean())return p?`${s}: [faker.datatype.boolean()]`:`${s}: faker.datatype.boolean()`;if(i.isArray()){let c=i.getArrayElementTypeOrThrow();return [this.generateProperty({name:s,type:c,array:true},t,r+1)]}if(i.isObject()){let c=this.extractProperties(i);if(c.length===0)return `${s}: {}`;let u=c.map(w=>this.generateProperty(w,t,r+1)),f=" ".repeat(r+1);return `${s}: {
48
- ${f}${u.map(w=>w).join(`,
49
- ${f}`)}
50
- ${l}}`}return `${s}: null`}parseFakerTag(e){let t=e.match(this.FAKER_TAG_REGEX);if(!t)return "faker.word.noun()";let[,r,s]=t;return s&&s.trim()?`faker.${r}(${s})`:`faker.${r}()`}};var O=class a{constructor(e,t){this.options=e;this.config=t;this.start=this.start.bind(this),this.xPoweredMiddleware=this.xPoweredMiddleware.bind(this),this.setupApplication=this.setupApplication.bind(this),this.setupTemplateEngine=this.setupTemplateEngine.bind(this),this.loadLocalEnv(),this.initWebhook(),process.on("SIGINT",()=>{let r=this.config.options.server(this.options.pathPrefix,this.options.port);this.subscriber?.shutdown(r),process.exit(0);});}webhook;subscriber;initWebhook(){this.config.options.webhook().enabled&&(this.subscriber=new F,o.warn("Initializating webhook..."),this.webhook=new E(this.subscriber,this.config));}static init(e,t){let r=new a(e,t);return r.webhook&&!r.webhook.isActivated()&&r.webhook.activate(),r}async shouldRunHeadlessMode(){if(this.options.headless||this.config.isHeadless()){let t=await new P(this.config).generate(this.options.source);return t||o.error("Headless mode failed. Falling back to standard server mode."),t}return false}async start(){if(await this.shouldRunHeadlessMode()){o.info("Headless mode enabled. Server startup skipped.");return}let e=$(),t=$.Router(),r=ae.createServer(e),s=N.initHandlers(this.config);this.setupApplication(e,s),this.setupTemplateEngine(e),await this.config.initializeRuntimeConfig(h,this.options);let i=y.register(this.config);await i.initialize(),await new L(t,this.config,s,i,this.options).register(),e.use(t),this.run(r,i,this.options);}setupApplication(e,t){e.disable("x-powered-by"),e.use($.json()),e.use(oe({methods:"GET"})),e.use($.static(h+"/public")),e.use(this.xPoweredMiddleware),e.use(t.middleware);}setupTemplateEngine(e){e.set("views",b.join(h,"views")),e.set("view engine","ejs"),e.use(pe),e.set("layout","layouts/main");}listen(e,t){this.subscriber?.started(t),e.enabled()&&o.info("database: %s",y.DATABASE_DIR),o.info("Server listening to http://localhost:%d",t.port),console.log(ce.textSync("FAKELAB"));}run(e,t,r){let s=this.config.options.server(r.pathPrefix,r.port);e.listen(s.port,"localhost",()=>this.listen(t,s));}xPoweredMiddleware(e,t,r){t.setHeader("x-powered-by","fakelab"),r();}loadLocalEnv(){}};var de="fakelab.config.ts";async function j(){try{let e=await new ue().resolve({files:[de]});if(!e)throw new Error("No fakelab config file is detected.");let t=b.resolve(e);if(!await d.pathExists(t))throw new Error(`Config file not found: ${t}`);return (await bundleRequire({filepath:t,cwd:m})).mod.default}catch(a){throw a instanceof Error&&a.stack&&o.debug("Stack trace: %s",a.stack),a}}var A=class a{constructor(e,t){this.options=e;this.config=t;this.capture=this.capture.bind(this),this.initWebhook();}TARGET_LANGUAGE="typescript";DEFAULT_TYPE_NAME="Fakelab";static SNAPSHOT_DIR=b.resolve(m,".fakelab/snapshots");static _instance;subscriber;webhook;static async init(e){try{let t=await j();return this._instance||(this._instance=new a(e,t)),this._instance.webhook&&!this._instance.webhook.isActivated()&&this._instance.webhook.activate(),this._instance}catch{process.exit(1);}}static async prepare(e){try{let t=await j(),r=this._instance||new a({},t),{enabled:s,sources:i}=t.options.snapshot();return r.webhook&&!r.webhook.isActivated()&&r.webhook.activate(),s&&e.freshSnapshots&&await r.updateAll(i,!0),r}catch{process.exit(1);}}async capture(e){let{enabled:t,sources:r}=this.config.options.snapshot();if(!t){o.warn("Snapshot is not enabled. Capture Skipped.");return}await d.ensureDir(a.SNAPSHOT_DIR),await d.ensureFile(b.resolve(a.SNAPSHOT_DIR,"__schema.json"));let s=await this.readSnapshotSchema();if(await this.duplicateExists(s)&&(o.error("Snapshot source names must be unique."),process.exit(1)),!e)return this.options.refresh?await this.refresh(r,this.options.refresh,s):this.options.delete?await this.delete(r,this.options.delete,s):await this.updateAll(r);let i=this.suffix(s.sources);this.options?.refresh&&o.warn("--refresh flag has no effect when used with url. Refresh skipped."),this.options?.delete&&o.warn("--delete flag has no effect when used with url. Delete skipped.");let n=await this.fetch({url:e,name:this.options?.name||i});await this.save({url:e,name:this.options?.name||i},n,s),await this.modifyGitignoreFile(".fakelab/*");}async save(e,t,r){try{let s=r.sources.find(n=>n.url===e.url);if(s){o.warn("%s snapshot is already captured. Use \x1B[36mnpx fakelab snapshot --refresh %s\x1B[0m to update.",e.url,s.name);return}let i=this.snapshotName(e.url,!1);o.info("Capturing %s snapshot...",i),e.name||this.options?.name||o.warn("Snapshot source name not found. Auto-generating a name."),await this.write(e.url,t,e.name||this.options?.name),this.subscriber?.captured({url:e.url,name:e.name??this.options?.name,content:t}),o.success("Snapshot %s captured successfully.",o.blue(i));}catch(s){o.error("Cannot save the captured snapshot."),s instanceof Error&&o.debug(s.message),process.exit(1);}}async refresh(e,t,r){let s=e.find(p=>p.name===t.trim());if(s||(s=(r.sources||[]).find(p=>p.name===t)),!s){o.warn("Snapshot source not found. Refresh skipped.");return}o.info("Refreshing %s snapshot source...",o.blue(s.name));let i=b.resolve(a.SNAPSHOT_DIR,this.snapshotName(s.url));if(await d.exists(i)){let p=await this.fetch(s);this.subscriber?.refreshed({url:s.url,name:s.name??this.options?.name,content:p}),await d.writeFile(i,p);}else {let p=await this.fetch(s);this.subscriber?.refreshed({url:s.url,name:s.name??this.options?.name,content:p}),await this.save(s,p,r);}o.success("Snapshot source %s refreshed successfully.",o.blue(s.name));}async delete(e,t,r){let s=e.find(n=>n.name===t.trim());if(s||(s=(r.sources||[]).find(n=>n.name===t)),!s){o.warn("Snapshot source not found. Delete skipped.");return}o.info("Deleting %s snapshot source...",o.blue(s.name));let i=b.resolve(a.SNAPSHOT_DIR,this.snapshotName(s.url));await d.rm(i,{force:true}),await this.updateSnapshotSchema({url:s.url,delete:true}),this.subscriber?.deleted({url:s.url,name:s.name??this.options?.name}),o.success("Snapshot source %s deleted successfully.",o.blue(s.name));}async updateAll(e,t=false){o.info(t?"Refreshing all snapshots...":"Updating all snapshots...");let r=await this.readSnapshotSchema();try{await Promise.all(e.map(async s=>{let i=b.resolve(a.SNAPSHOT_DIR,this.snapshotName(s.url));if(await d.exists(i)){let p=await this.fetch(s);await d.writeFile(i,p);}else {let p=await this.fetch(s);await this.save(s,p,r);}})),o.success("All snapshots are updated.");}catch(s){o.error("Failed to update.",s),process.exit(1);}}async fetch({name:e,url:t,headers:r}){this.isValidUrl(t)||(o.error("Invalid snapshot URL. Please provide a valid http URL."),process.exit(1));let i=await(await fetch(t,{headers:r})).text();this.isValidJSON(i)||(o.error("Invalid snapshot response format. Expected JSON but received non-JSON data."),process.exit(1));let n=await import('quicktype-core'),p=n.jsonInputForTargetLanguage(this.TARGET_LANGUAGE);await p.addSource({name:e,samples:[i]});let l=new n.InputData;l.addInput(p);let{lines:c}=await n.quicktype({inputData:l,lang:this.TARGET_LANGUAGE,rendererOptions:{"just-types":true}});return c.join(`
51
- `)}async write(e,t,r,s){await d.writeFile(b.resolve(a.SNAPSHOT_DIR,this.snapshotName(e)),t),await this.updateSnapshotSchema({url:e,name:r,headers:s});}async readSnapshotSchema(){let e={sources:[]};try{e=await d.readJSON(b.resolve(a.SNAPSHOT_DIR,"__schema.json"));}catch{}return e.sources||(e.sources=[]),e}async updateSnapshotSchema(e){let t=await this.readSnapshotSchema(),r=t.sources||[],s=r.findIndex(i=>i.url===e.url);if(e.delete)r.splice(s,1);else if(s===-1)r.push({...e,name:e.name||this.suffix(r)});else {let i=r[s].name,n=r[s].headers;r.splice(s,1,{...r[s],name:e.name||i,headers:e.headers||n});}t.sources=r,await d.writeJSON(b.resolve(a.SNAPSHOT_DIR,"__schema.json"),t);}initWebhook(){let{enabled:e}=this.config.options.snapshot();if(e){let t=this.config.options.webhook();t.enabled&&(this.subscriber=new _(t.hooks),o.warn("Initializating webhook..."),this.webhook=new E(this.subscriber,this.config));}}snapshotName(e,t=true){return e.replace(/^https?:\/\//,"").replace(/[/:?.&=#]/g,"_")+(t?".ts":"")}suffix(e){let t=e.filter(r=>r.name.startsWith(this.DEFAULT_TYPE_NAME));return `${this.DEFAULT_TYPE_NAME}${t.length}`}isValidUrl(e){try{let t=new URL(e);return !(t.protocol!=="http:"&&t.protocol!=="https:")}catch{return false}}isValidJSON(e){try{return JSON.parse(e),!0}catch{return false}}async duplicateExists(e){let t=e.sources.map(r=>r.name);return new Set(t).size!==t.length}async modifyGitignoreFile(e){try{let t=b.resolve(m,".gitignore");if((await d.readFile(t,{encoding:"utf8"})).split(`
52
- `).some(s=>s.trim()===e.trim()))return;await d.appendFile(t,`
53
- ${e}`);}catch(t){o.warn("Cannot modify .gitignore. error: %s",t);}}};var I=new Command,H=d.readJSONSync(b.join(h,"../package.json"));I.name(H.name).description(H.description).version(H.version);I.command("serve").description("start server").option("-s, --source <char>","config source path").option("-x, --pathPrefix <char>","server url path prefix").option("-p, --port <number>","server port number",parseInt).option("-l, --locale <char>","faker custom locale").option("-h, --headless","headless mode").option("-c, --ts-config <char>","ts config path").option("-f, --fresh-snapshots","capture or refresh all snapshots").action(async a=>{let e=await A.prepare(a);O.init(a,e.config).start();});I.command("snapshot").description("capture a url response to a fakelab entity").argument("[string]","url to capture").option("-s, --name <string>","specify snapshot source name").option("-r, --refresh <string>","refresh the specified snapshot").option("-d, --delete <string>","delete the specified snapshot").action(async(a,e)=>{(await A.init(e)).capture(a);});I.parse();
44
+ `,ae=`declare function gen<T extends keyof Runtime$>(name: T): Runtime$[T];
45
+ declare function gen<T extends keyof Runtime$>(name: T, options: Options): Runtime$[T][];
46
+
47
+ declare function fetch<T extends keyof Runtime$>(name: T, options?: Options): never;
48
+
49
+ declare function enabled(): boolean;
50
+ declare function url(): string;
51
+
52
+ declare const fakelab: {
53
+ gen: typeof gen;
54
+ fetch: typeof fetch;
55
+ url: typeof url;
56
+ enabled: typeof enabled;
57
+ };
58
+
59
+ type Options = { count: number };
60
+
61
+ interface Runtime$ {}
62
+
63
+ export { fakelab };
64
+ `;var A=class p{constructor(e,t,r=false){this.files=e;this.tsConfigFilePath=t;this.headless=r;let i=new Project({tsConfigFilePath:this.tsConfigFilePath}).addSourceFilesAtPaths(this.files);this.__targets=i.flatMap(a=>{let n=a.getInterfaces(),c=a.getTypeAliases(),u=a.getExportDeclarations().flatMap(l=>l.getNamedExports().flatMap(h=>h.getLocalTargetDeclarations()));return [...n,...c,...u]}),this.generateTypeUtils();}__targets;static async init(e,t,r=false){let s=new p(e,t,r);return await s.generateInFileEntitiyMap(),s}normalizePath(e){return e.split(v.sep).join(v.posix.sep)}address(e,t){let r=this.normalizePath(m);return `${e.replace(r,"")}/${t}`}getDeclarationTextFromType(e){let t=e.getSymbol()??e.getAliasSymbol();if(!t)return null;let r=t.getDeclarations(),s=r.find(Node.isInterfaceDeclaration),i=r.find(Node.isTypeAliasDeclaration),a=r.find(Node.isExportDeclaration),n=s??i??a;return n?n.getText().replace(/export|interface|type|;|\s+/g,""):null}getDeclarationsMap(){return [...new Set(this.__targets.map(e=>{let t=e.getName(),r=e.getSourceFile().getFilePath();return `${t.toLowerCase()}: import("${r}").${t}`}))]}generateTypeUtils(){let e=this.getDeclarationsMap();if(e.length===0)return;let t=`
65
+ interface $$ {
66
+ ${e.join(`
67
+ `)}
68
+ }`;g.appendFile(v.resolve(d,"type-utils.d.ts"),t);}argsToString(e){let t=Object.entries(e??{}).filter(([,r])=>r!=null);return t.length===0?null:t.map(r=>r.join("_")).join("_")}async typeToString(e,t){let r=this.argsToString(t),s=this.getDeclarationTextFromType(e);return r&&(s+=`(${r})`),s}async generateInFileEntitiyMap(){this.headless&&await g.writeFile(v.resolve(d,"runtime.d.ts"),ae);let e=this.getDeclarationsMap();if(e.length===0)return;let t=`
69
+ interface Runtime$ {
70
+ ${e.join(`
71
+ `)}
72
+ }`;await g.appendFile(v.resolve(d,"runtime.d.ts"),t);}async entities(){let e=await Promise.all(this.__targets.map(async t=>{let r=t.getName().toLowerCase(),s=t.getType(),i=this.normalizePath(t.getSourceFile().getDirectoryPath()),a=t.getSourceFile().getBaseName(),n=this.address(i,a),c=v.resolve(k.DATABASE_DIR,`${r}.json`),u=this.address(this.normalizePath(k.DATABASE_DIR),v.basename(c)),l=i.includes("/.fakelab/snapshots"),h=await JSONFilePreset(c,[]);return [r,{type:s,filepath:n,snapshot:l,table:h,tablepath:u}]}));return new Map(e.sort((t,r)=>Number(t[1].snapshot)-Number(r[1].snapshot)))}async initFakerLibrary(e){let{faker:t}=await import(`@faker-js/faker/locale/${e.locale}`);return t}};var X=class p{constructor(e){this.config=e;this.config.options.cache().enabled||this.clear();}static CACHE_DIR=v.resolve(m,".fakelab/cache");__key__(e){return createHash("sha1").update(e).digest("hex").slice(0,24)}async get(e,t){let{enabled:r,ttl:s}=this.config.options.cache();if(!r||t===null)return null;let i=this.__key__(t),a=v.resolve(p.CACHE_DIR,`${e}_${i}.json`);if(!await g.exists(a))return null;try{let n=await this.ensureStats(a);if(n&&s>0&&Date.now()-n.mtimeMs>s)return null;let c=await g.readFile(a,"utf8");return {json:c,data:JSON.parse(c)}}catch(n){return n instanceof Error&&o.error(n.message),null}}async set(e,t,r){let{enabled:s}=this.config.options.cache();if(!s||t===null)return;let i=this.__key__(t),a=v.resolve(p.CACHE_DIR),n=v.resolve(a,`${e}_${i}.json`),c=n+".tmp";await g.ensureDir(a),await g.writeFile(c,r),await g.rename(c,n);}async delete(e,t){let{enabled:r}=this.config.options.cache();if(!r||t===null)return;let s=this.__key__(t),i=v.resolve(p.CACHE_DIR,`${e}_${s}.json`);await g.rm(i,{force:true});}async clear(){await g.rm(p.CACHE_DIR,{recursive:true,force:true});}async ensureStats(e){try{return await g.stat(e)}catch{return null}}};function ne(p){return new X(p)}async function x(p,e,t=[],r=0){if(p.isString())return e.string(t[r]);if(p.isNumber())return e.int(t[r]);if(p.isBoolean())return e.bool(t[r]);if(p.isBigInt())return e.bigInt(t[r]);if(p.isBooleanLiteral())return e.litbool(p.getText());if(p.isLiteral())return p.getLiteralValue();if(!p.isUndefined()){if(p.isUnion()){let s=p.getUnionTypes();return await e.union(s.map((i,a)=>x(i,e,t,a)))}if(p.isTuple()){let s=p.getTupleElements();return await e.tuple(s.map((i,a)=>x(i,e,t,a)))}if(p.isIntersection()){let s=p.getIntersectionTypes();return await e.intersection(s.map(i=>x(i,e,t,r)))}if(p.isArray()){let s=p.getArrayElementTypeOrThrow();return [await x(s,e,t,r)]}if(p.isObject()){let s=p.getProperties();return await e.object(s,(i,a,n)=>x(i,a,n,r))}return null}}function ve({each:p}){return {resolve:async t=>await Promise.all(Array.from({length:t},p))}}async function oe(p,e,t){let r=await p.files(e.source,t),s=await A.init(r,p.getTSConfigFilePath(e.tsConfigPath)),i=await s.entities(),a=await s.initFakerLibrary(p.options.faker(e.locale)),n=new O(a),c=ne(p);async function u(l,h,f){let b=await s.typeToString(h,{...f,...p.options.faker()}),y=await c.get(l,b);if(y)return {...y,fromCache:true};let fe=ve({each:()=>x(h,n)}),te=await(f.count?fe.resolve(parseInt(String(f.count))):x(h,n)),re=JSON.stringify(te,null,2);return c.set(l,b,re),{data:te,json:re,fromCache:false}}return {entities:i,build:u}}var P=class{constructor(e){this.builder=e;}typeMap=new Map;tsTypeToGraphQLType(e,t=false){if(e.isString())return GraphQLString;if(e.isNumber())return GraphQLFloat;if(e.isBoolean())return GraphQLBoolean;if(e.isBigInt())return GraphQLString;if(e.isArray()){let r=e.getArrayElementTypeOrThrow();return new GraphQLList(this.tsTypeToGraphQLType(r,t))}if(e.isUnion())return GraphQLString;if(e.isObject()){let r=this.getTypeName(e);if(!r)return GraphQLString;let s=this.typeMap.get(r);if(s)return t&&s instanceof GraphQLObjectType?void 0:s;let i=this.createObjectType(e,r,t);if(i)return this.typeMap.set(r,i),i}return GraphQLString}getTypeName(e){let t=e.getSymbol();return t?t.getName():null}createObjectType(e,t,r){let s=e.getProperties(),i={};for(let a of s){let n=a.getTypeAtLocation(a.getValueDeclarationOrThrow()),c=this.tsTypeToGraphQLType(n,r);c&&(i[a.getName()]={type:c});}return Object.keys(i).length===0?null:r?new GraphQLInputObjectType({name:`${t}Input`,fields:i}):new GraphQLObjectType({name:t,fields:i})}generateSchema(){let e={},t={};for(let[i,a]of this.builder.entities){let n=this.tsTypeToGraphQLType(a.type);if(n instanceof GraphQLObjectType||n instanceof GraphQLList){let c=n instanceof GraphQLList?n:new GraphQLList(n);e[i]={type:c,args:{count:{type:GraphQLInt}},resolve:async(l,h)=>{let{data:f}=await this.builder.build(i.toLowerCase(),a.type,{count:h.count||1});return Array.isArray(f)?f:[f]}},e[`${i}ById`]={type:n instanceof GraphQLList?n.ofType:n,args:{id:{type:new GraphQLNonNull(GraphQLID)}},resolve:async(l,h,f)=>{if(!f.database?.enabled())throw new Error("Database is not enabled");return await a.table.read(),a.table.data.find(y=>String(y.id)===h.id)||null}},e[`all${i.charAt(0).toUpperCase()+i.slice(1)}`]={type:new GraphQLList(n instanceof GraphQLList?n.ofType:n),resolve:async(l,h,f)=>{if(!f.database?.enabled())throw new Error("Database is not enabled");return await a.table.read(),a.table.data}};let u=this.tsTypeToGraphQLType(a.type,true);u instanceof GraphQLInputObjectType&&(t[`create${i.charAt(0).toUpperCase()+i.slice(1)}`]={type:n instanceof GraphQLList?n.ofType:n,args:{input:{type:new GraphQLNonNull(u)}},resolve:async(l,h,f)=>{if(!f.database?.enabled())throw new Error("Database is not enabled");await a.table.read();let b={...h.input,id:h.input.id||`${Date.now()}-${Math.random()}`};return await a.table.update(y=>y.push(b)),b}}),t[`delete${i.charAt(0).toUpperCase()+i.slice(1)}`]={type:GraphQLBoolean,args:{id:{type:new GraphQLNonNull(GraphQLID)}},resolve:async(l,h,f)=>{if(!f.database?.enabled())throw new Error("Database is not enabled");await a.table.read();let b=a.table.data.findIndex(y=>String(y.id)===h.id);return b!==-1?(await a.table.update(y=>y.splice(b,1)),true):false}},t[`flush${i.charAt(0).toUpperCase()+i.slice(1)}`]={type:GraphQLBoolean,resolve:async(l,h,f)=>{if(!f.database?.enabled())throw new Error("Database is not enabled");return await a.table.update(b=>b.length=0),true}};}}let r=new GraphQLObjectType({name:"Query",fields:e}),s=new GraphQLObjectType({name:"Mutation",fields:t});return new GraphQLSchema({query:r,mutation:Object.keys(t).length>0?s:void 0})}};var I=class{constructor(e,t,r,s){this.builder=e;this.network=t;this.database=r;this.config=s;}schema=null;async applyNetworkHandlers(e){if(this.network.offline()){let{status:t,message:r}=this.network.state("offline");return e.status(t).json({errors:[{message:r}]}),true}if(await this.network.wait(),this.network.timeout())return true;if(this.network.error()){let{status:t,message:r}=this.network.state("error");return e.status(t).json({errors:[{message:r}]}),true}return false}getSchema(){if(!this.schema){let e=new P(this.builder);this.schema=e.generateSchema();}return this.schema}createMiddleware(){let e=this.getSchema();return async(t,r,s)=>!this.config.enabled()||await this.applyNetworkHandlers(r)?void 0:createHandler({schema:e,context:()=>({database:this.database,network:this.network}),formatError:a=>a})(t,r,s)}buildQuery(e,t){let r=this.extractFields(t,new Set,1);return `query {
73
+ ${e}(count: 1) {
74
+ ${r.join(`
75
+ `)}
76
+ }
77
+ }`}extractFields(e,t=new Set,r=1){let s=[],i=e.getSymbol()?.getName();if(i&&t.has(i))return s;i&&t.add(i);let a=e.getProperties(),n=" ".repeat(r);for(let c of a){let u=c.getTypeAtLocation(c.getValueDeclarationOrThrow()),l=c.getName();if(u.isArray()){let h=u.getArrayElementTypeOrThrow();if(h.isObject()&&!t.has(h.getSymbol()?.getName()||"")){let f=this.extractFields(h,t,r+1);f.length>0?s.push(`${n}${n}${l} {
78
+ ${f.join(`
79
+ `)}
80
+ ${n}}`):s.push(`${n}${n}${l}`);}else s.push(`${n}${n}${l}`);}else if(u.isObject()&&!t.has(u.getSymbol()?.getName()||"")){let h=this.extractFields(u,t,r+1);h.length>0?s.push(`${n}${n}${l} {
81
+ ${h.join(`
82
+ `)}
83
+ ${n}${n}}`):s.push(`${n}${n}${l}`);}else s.push(`${n}${n}${l}`);}return i&&t.delete(i),s}};var j=class{constructor(e,t,r,s,i){this.builder=e;this.database=t;this.config=r;this.graphqlBuilder=s;this.pkg=i;this.isFakelabEnabled=this.config.enabled(),this.isDatabaseEnabled=this.database.enabled();}isFakelabEnabled;isDatabaseEnabled;async handleQueries(e){let t=e.query.count;return t?{count:t.toString()}:{}}index(){return (e,t)=>{t.render("index",{name:null,entities:this.builder.entities,version:this.pkg.version,isFakelabEnabled:this.isFakelabEnabled,isDatabaseEnabled:this.isDatabaseEnabled});}}preview(e){return async(t,r)=>{if(!this.isFakelabEnabled)return r.redirect("/");let s=`${t.protocol}://${t.host}/`,i=t.params.name,a=await this.handleQueries(t),n=xe.stringify(a,{addQueryPrefix:true}),c=this.builder.entities.get(i.toLowerCase());if(c){let{json:u,fromCache:l}=await this.builder.build(i.toLowerCase(),c.type,a),h=c.filepath,f=this.config.options.graphQL();r.render("preview",{name:i,suffix:c.snapshot?"(snapshot)":"",filepath:h,address:s,search:n,json:u,prefix:e,fromCache:l,entities:this.builder.entities,version:this.pkg.version,isDatabaseEnabled:this.isDatabaseEnabled,isFakelabEnabled:this.isFakelabEnabled,isGraphqlEnabled:f.enabled});}else r.redirect("/");}}db(){return (e,t)=>{if(!this.isFakelabEnabled)return t.redirect("/");this.isDatabaseEnabled?t.render("database",{name:null,entities:this.builder.entities,version:this.pkg.version}):t.redirect("/");}}table(e){return async(t,r)=>{if(!this.isFakelabEnabled)return r.redirect("/");let s=`${t.protocol}://${t.host}/`,i=t.params.name,a=this.builder.entities.get(i.toLowerCase());if(!this.isDatabaseEnabled)r.redirect("/");else if(a){await a.table.read();let c=a.table.data.length>0,u=JSON.stringify(a.table.data,null,2),l=a.filepath;r.render("table",{name:i,filepath:l,address:s,prefix:e,json:u,hasData:c,entities:this.builder.entities,version:this.pkg.version,isFakelabEnabled:this.isFakelabEnabled});}else r.redirect("/database");}}graphql(e){return (t,r)=>{if(!this.isFakelabEnabled)return r.redirect("/");let{enabled:s}=this.config.options.graphQL();s?r.render("graphql",{name:null,address:"",prefix:e,query:"",entities:this.builder.entities,version:this.pkg.version,isDatabaseEnabled:this.isDatabaseEnabled,isFakelabEnabled:this.isFakelabEnabled}):r.redirect("/");}}graphqlEntity(e){return (t,r)=>{if(!this.isFakelabEnabled)return r.redirect("/");let s=`${t.protocol}://${t.host}/`,i=t.params.name,a=this.builder.entities.get(i.toLowerCase()),{enabled:n}=this.config.options.graphQL();if(!n)r.redirect("/");else if(a){let c=this.graphqlBuilder.buildQuery(i.toLowerCase(),a.type);r.render("graphql",{name:i,address:s,prefix:e,query:c,entities:this.builder.entities,version:this.pkg.version,isDatabaseEnabled:this.isDatabaseEnabled,isFakelabEnabled:this.isFakelabEnabled});}else r.redirect("/graphql");}}};var H=class{constructor(e,t,r,s){this.builder=e;this.network=t;this.database=r;this.config=s;}SEED_MERGE_THRESHOLD=1e3;async handleQueries(e){let t=e.query.count;return t?{count:t.toString()}:{}}async applyNetworkHandlers(e){if(this.network.offline()){let{status:t,message:r}=this.network.state("offline");return e.status(t).json({message:r}),true}if(await this.network.wait(),this.network.timeout())return true;if(this.network.error()){let{status:t,message:r}=this.network.state("error");return e.status(t).json({message:r}),true}return false}entity(){return async(e,t)=>{if(!this.config.enabled())return t.status(500).json({error:"Fakelab is disabled."});try{if(await this.applyNetworkHandlers(t))return;let r=e.params.name,s=await this.handleQueries(e),i=this.builder.entities.get(r.toLowerCase());if(i){let{data:a}=await this.builder.build(r.toLowerCase(),i.type,s);t.status(200).json(a);}else t.status(400).json({message:"The entity is not exists"});}catch(r){t.status(500).send(r);}}}getTable(){return async(e,t)=>{if(!this.config.enabled())return t.status(500).json({error:"Fakelab is disabled."});try{if(!this.database.enabled())return t.status(403).json({message:"database is not enabled or initialized."});if(await this.applyNetworkHandlers(t))return;let r=e.params.name,s=this.builder.entities.get(r.toLowerCase());s?(await s.table.read(),t.status(200).json(s.table.data)):t.status(400).json({message:`${r} table is not exists`});}catch(r){t.status(500).send(r);}}}updateTable(){return async(e,t)=>{if(!this.config.enabled())return t.status(500).json({error:"Fakelab is disabled."});try{if(!this.database.enabled())return t.status(403).json({message:"database is not enabled or initialized."});if(await this.applyNetworkHandlers(t))return;let r=e.params.name,s=await this.handleQueries(e),i=this.builder.entities.get(r.toLowerCase());if(i){let{data:a}=await this.builder.build(r.toLowerCase(),i.type,s);await i.table.update(n=>n.push(...Array.isArray(a)?a:[a])),t.status(200).json({success:!0});}else t.status(400).json({success:!1,message:`${r} table is not exists`});}catch(r){t.status(500).send(r);}}}insert(){return async(e,t)=>{if(!this.config.enabled())return t.status(500).json({error:"Fakelab is disabled."});try{if(!this.database.enabled())return t.status(403).json({message:"database is not enabled or initialized."});let r=e.body?.count||1,s=e.body?.strategy||"reset",i=e.params.name,a=this.builder.entities.get(i.toLowerCase());if(a){let{data:n}=await this.builder.build(i.toLowerCase(),a.type,{count:r});if(await a.table.read(),s==="once"&&a.table.data.length>0)return t.status(200).json({message:`${i} entity was seeded once before.`});await a.table.update(c=>{s!=="merge"&&(c.length=0);let u=Array.isArray(n)?n:[n];c.length+u.length<this.SEED_MERGE_THRESHOLD&&c.push(...u);}),t.status(200).json({success:!0});}else t.status(400).json({success:!1,message:"The table is not exists"});}catch(r){t.status(500).send(r);}}}flush(){return async(e,t)=>{if(!this.config.enabled())return t.status(500).json({error:"Fakelab is disabled."});try{if(!this.database.enabled())return t.status(403).json({message:"database is not enabled or initialized."});let r=e.params.name,s=this.builder.entities.get(r.toLowerCase());s?(await s.table.update(i=>i.length=0),t.status(200).json({success:!0})):t.status(400).json({success:!1,message:"The table is not exists"});}catch(r){t.status(500).send(r);}}}_update(){return async(e,t)=>{if(!this.config.enabled())return t.status(500).json({error:"Fakelab is disabled."});try{if(!this.database.enabled())return t.status(403).json({message:"database is not enabled or initialized."});let r=e.params.name,s=await this.handleQueries(e),i=this.builder.entities.get(r.toLowerCase());if(i){let{data:a}=await this.builder.build(r.toLowerCase(),i.type,s);await i.table.update(n=>n.push(a)),t.status(301).redirect(`/database/${r.toLowerCase()}`);}else t.status(400).redirect("/database");}catch{t.status(500).redirect("/database");}}}_clear(){return async(e,t)=>{if(!this.config.enabled())return t.status(500).json({error:"Fakelab is disabled."});try{if(!this.database.enabled())return t.status(403).json({message:"database is not enabled or initialized."});let r=e.params.name,s=this.builder.entities.get(r.toLowerCase());s?(await s.table.read(),s.table.data.length>0&&await s.table.update(i=>i.length=0),t.status(301).redirect(`/database/${r.toLowerCase()}`)):t.status(400).redirect("/database");}catch{t.status(500).redirect("/database");}}}};var Ce=g.readJSONSync(v.join(d,"../package.json")),G=class{constructor(e,t,r,s,i){this.router=e;this.config=t;this.network=r;this.database=s;this.options=i;}instantiateRegistryHandlers(e,t){let r=new I(t,this.network,this.database,e),s=new H(t,this.network,this.database,e),i=new j(t,this.database,e,r,Ce);return {gql:r,handler:s,renderer:i}}async register({fresh:e}){let t=await oe(this.config,this.options,e),{gql:r,handler:s,renderer:i}=this.instantiateRegistryHandlers(this.config,t),{pathPrefix:a}=this.config.options.server(this.options.pathPrefix,this.options.port);this.router.get("/",i.index()),this.router.get("/graphql",i.graphql(a)),this.router.get("/graphql/:name",i.graphqlEntity(a)),this.router.get("/entities/:name",i.preview(a)),this.router.get("/database",i.db()),this.router.get("/database/:name",i.table(a)),this.router.all(`/${a}/graphql`,r.createMiddleware()),this.router.get(`/${a}/:name`,s.entity()),this.router.get(`/${a}/database/:name`,s.getTable()),this.router.post(`/${a}/database/:name`,s.updateTable()),this.router.post(`/${a}/database/insert/:name`,s.insert()),this.router.post(`/${a}/database/flush/:name`,s.flush()),this.router.post("/__update/:name",s._update()),this.router.post("/__delete/:name",s._clear());}};var M=class p{constructor(e){this.config=e;this.options=this.config.options.network(),this.timeout=this.timeout.bind(this),this.error=this.error.bind(this),this.state=this.state.bind(this),this.wait=this.wait.bind(this),this.offline=this.offline.bind(this);}options;static initHandlers(e){return new p(e)}timeout(){let e=this.chance(this.options.timeoutRate);return e&&o.debug("Network timeout..."),e}error(){let e=this.chance(this.options.errorRate);return e&&o.debug("Network error..."),e}state(e){switch(e){case "error":{let t=this.options.errors?.statusCodes||[],r=t.length>0?t[Math.floor(Math.random()*t.length)]:500,s=this.options.errors?.messages?.[r]??"Network error";return {status:r,message:s}}case "offline":return {status:503,message:"Network offline"};default:return {status:500,message:"Server unknown error"}}}async wait(){let e=this.resolveDelay();e>0&&(o.debug("Network waiting (%d ms)...",e),await this.sleep(e));}offline(){return this.options.offline??false}resolveDelay(){let e=this.options.delay;return typeof e=="number"?Math.round(e):Array.isArray(e)?this.random(e):0}chance(e=0){return Math.random()<e}random([e,t]){return Math.round(Math.random()*(t-e)+e)}sleep(e){return new Promise(t=>setTimeout(t,e))}};var R=class p{constructor(e,t){this.subscriber=e;this.config=t;this.options=this.config.options.webhook(),this.dispose();}options;unsubs=new Set;history=new Set;activated=false;static processHandlersRegistered=false;isActivated(){return this.activated}activate(){if(this.flush("REACTIVATE"),!this.options.enabled){o.warn("Webhook is disabled. Skipping activation.");return}if(this.options.hooks.length===0){o.debug?.("Webhook enabled but no hooks configured. Skipping activation.");return}this.activated=true;for(let e of this.options.hooks){let{error:t,message:r,args:s=[]}=this.validateHook(e);if(t){o.error(r,...s);continue}if(this.history.has(e.name))continue;let i=new AbortController,a=this.subscriber.subscribe(e.name,e.trigger.event,n=>this.handle(e,n,i.signal));this.history.add(e.name),this.unsubs.add(n=>{try{a();}finally{this.history.delete(e.name),i.abort(n);}});}}flush(e){this.subscriber.clear();for(let t of this.unsubs)t(e);this.unsubs.clear();}async handle({name:e,method:t,url:r,headers:s,transform:i,trigger:a},n,c){let u=n;try{u=typeof i=="function"?i(n):n;}catch(l){o.error("Webhook %s payload transformation failed. error: %s",o.blue(e),l);}c.aborted?o.error("Webhook %s aborted",o.blue(e)):c.addEventListener("abort",()=>{o.error("Webhook %s aborted",o.blue(e));});try{o.info("Delivering %s to %s",o.blue(e),o.blue(r)),(await fetch(r,{method:t,body:JSON.stringify(u),signal:c,headers:this.requestHeaders(e,a.event,s)})).ok?o.success("Webhook %s delivered successfully.",o.blue(e)):o.error("Webhook %s request failed.",o.blue(e));}catch(l){o.error("Webhook %s network error: %s",o.blue(e),l);}}validateHook(e){if(e.method.toUpperCase()!=="POST")return {error:true,message:"Webhook hook method must be 'POST'. received %s",args:[e.method]};try{let t=new URL(e.url);if(t.protocol!=="http:"&&t.protocol!=="https:")return {error:!0,message:'Webhook hook URL must use http/https. Received "%s".',args:[t.protocol]}}catch(t){return {error:true,message:"Webhook hook URL is invalid. Received: %s. error: %s",args:[e.url,JSON.stringify(t)]}}return {error:false,message:null}}requestHeaders(e,t,r){let s=new Headers(r);return s.append("Content-Type","application/json"),s.append("X-Fakelab-Webhook",`name=${e},event=${t}`),s}dispose(){p.processHandlersRegistered||(p.processHandlersRegistered=true,["SIGINT","SIGTERM","SIGQUIT"].forEach(e=>process.on(e,()=>{this.activated=false,this.history.clear(),this.flush(e),process.exit(0);})));}};var C=class{constructor(e){this.$emitter=e;this.subscribe=this.subscribe.bind(this);}subscribe(e,t,r){return this.$emitter.on(t,r),()=>{o.info("Webhook %s unsubscribed.",o.blue(e)),this.$emitter.off(t,r);}}clear(){this.$emitter.all.clear();}};var B=class extends C{constructor(t){let r=De();super(r);this.hooks=t;this.captured=this.captured.bind(this),this.refreshed=this.refreshed.bind(this),this.deleted=this.deleted.bind(this),this.$emitter.all.clear();}captured(t){let r=this.getTriggeredHook("snapshot:captured");r?(o.info("Dispatching %s for event %s.",o.blue(r.name),o.blue("snapshot:captured")),this.$emitter.emit("snapshot:captured",t)):o.warn("Webhook skipped: missing trigger event %s.",o.blue("snapshot:captured"));}refreshed(t){let r=this.getTriggeredHook("snapshot:refreshed");r?(o.info("Dispatching %s for event %s.",o.blue(r.name),o.blue("snapshot:refreshed")),this.$emitter.emit("snapshot:refreshed",t)):o.warn("Webhook skipped: missing trigger event %s.",o.blue("snapshot:refreshed"));}deleted(t){let r=this.getTriggeredHook("snapshot:deleted");r?(o.info("Dispatching %s for event %s.",o.blue(r.name),o.blue("snapshot:deleted")),this.$emitter.emit("snapshot:deleted",t)):o.warn("Webhook skipped: missing trigger event %s.",o.blue("snapshot:deleted"));}getTriggeredHook(t){return this.hooks.find(r=>r.trigger.event===t)}};var q=class extends C{constructor(){let e=De();super(e),this.started=this.started.bind(this),this.shutdown=this.shutdown.bind(this);}started({pathPrefix:e,port:t}){this.$emitter.emit("server:started",{port:t,prefix:e});}shutdown({pathPrefix:e,port:t}){this.$emitter.emit("server:shutdown",{port:t,prefix:e});}};var U=class{constructor(e){this.config=e;}HEADLESS_FILENAME="runtime.js";JSDOC_FAKER_FIELD="faker";FAKER_TAG_REGEX=/^([a-zA-Z0-9._]+)(?:\((.*)\))?$/;async generate(e){try{let t=await this.config.files(e.source,!1),s=await(await A.init(t,this.config.getTSConfigFilePath(e.tsConfigPath),!0)).entities(),i=[],a=new Map;for(let[l]of s.entries())a.set(l.toLowerCase(),l);for(let[l,h]of s.entries()){let f=this.generateFunction(h.type,l,a);i.push(f);}let{locale:n}=this.config.options.faker(),{code:c}=await transform(this.generateSource(i,n),{minify:!0,platform:"browser",target:"es2022"}),u=v.resolve(d,this.HEADLESS_FILENAME);return await g.ensureDir(v.dirname(u)),await Promise.all([g.writeFile(u,c)]),!0}catch(t){return o.error(t instanceof Error?t.message:t),false}}replacer(e,t){let r=e;for(let s in t)r=r.replace(new RegExp(s,"g"),t[s].toString().trim());return r}generateSource(e,t){let{pathPrefix:r,port:s}=this.config.options.server();return this.replacer(ie,{FAKELAB_ENABLED:this.config.enabled(),PORT:s,PREFIX:r,LOCALE:t,FUNCTIONS:e.join(`,
84
+ `)})}generateFunction(e,t,r){let i=this.extractProperties(e).map(a=>this.generateProperty(a,r,1));return `${t}(){return {${i.join(",")}};}`}extractProperties(e){return e.isObject()?e.getProperties().map(r=>{let s=r.getTypeAtLocation(r.getValueDeclarationOrThrow()),i=this.extractFakerTag(r);return {name:r.getName(),type:s,fakerTag:i,array:s.isArray()}}):[]}extractFakerTag(e){let t=e.getJsDocTags().filter(s=>s.getName()===this.JSDOC_FAKER_FIELD);if(t.length===0)return;let[r]=t[0].getText();if(r)return r.text.trim()}generateProperty(e,t,r=0){let{name:s,type:i,fakerTag:a,array:n}=e,c=" ".repeat(r);if(a){let u=this.parseFakerTag(a);return n?`${s}: [${u}]`:`${s}: ${u}`}if(i.isArray()){let l=i.getArrayElementTypeOrThrow().getSymbol();if(l){let h=l.getName().toLowerCase();if(t.has(h))return `${s}: [this.${h}()]`}}else {let u=i.getSymbol();if(u){let l=u.getName().toLowerCase();if(t.has(l))return `${s}: this.${l}()`}}if(i.isString())return n?`${s}: [faker.word.noun()]`:`${s}: faker.word.noun()`;if(i.isNumber())return n?`${s}: [faker.number.int()]`:`${s}: faker.number.int()`;if(i.isBoolean())return n?`${s}: [faker.datatype.boolean()]`:`${s}: faker.datatype.boolean()`;if(i.isArray()){let u=i.getArrayElementTypeOrThrow();return [this.generateProperty({name:s,type:u,array:true},t,r+1)]}if(i.isObject()){let u=this.extractProperties(i);if(u.length===0)return `${s}: {}`;let l=u.map(f=>this.generateProperty(f,t,r+1)),h=" ".repeat(r+1);return `${s}: {
85
+ ${h}${l.map(f=>f).join(`,
86
+ ${h}`)}
87
+ ${c}}`}return `${s}: null`}parseFakerTag(e){let t=e.match(this.FAKER_TAG_REGEX);if(!t)return "faker.word.noun()";let[,r,s]=t;return s&&s.trim()?`faker.${r}(${s})`:`faker.${r}()`}};var Pe="fakelab.config.ts";async function F(){try{let e=await new $e().resolve({files:[Pe]});if(!e)throw new Error("No fakelab config file is detected.");let t=v.resolve(e);if(!await g.pathExists(t))throw new Error(`Config file not found: ${t}`);return (await bundleRequire({filepath:t,cwd:m})).mod.default}catch(p){throw p instanceof Error&&p.stack&&o.debug("Stack trace: %s",p.stack),p}}var Q=class p{constructor(e,t,r,s){this.app=e;this.initial=t;this.config=r;this.options=s;this.router=this.initial,this.middleware=this.middleware.bind(this),this.app.use(this.middleware),this.app.get(this.RELOAD_PATH,(i,a)=>this.subscribe(a)),this.ping();}router;clients=new Set;_ref={current:null};_pingTimer;RELOAD_PATH="/__reload";headers={"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"};database;static register(e,t,r,s){return new p(e,t,r,s)}set(e){this.router=e;}subscribe(e){e.writeHead(200,this.headers),e.write(`
88
+ `),this.clients.add(e),e.on("close",()=>{this.clients.delete(e);});}broadcast(){for(let e of this.clients)e.write(`event: reload
89
+ data: ${Date.now()}
90
+
91
+ `);}watch(){let e=this.createTrigger(),t=Ie.watch(this.watcherPaths(),{ignoreInitial:true,persistent:true,ignorePermissionErrors:true,ignored:["**/*.js","**/*.jsx","**/*.json","**/*.map","**/*.d.ts","**/node_modules/**"]});return t.on("add",e),t.on("change",e),t.on("unlink",e),t.on("unlinkDir",e),t.on("error",r=>{o.error("Hot reload: watcher error \u274C. error: %s",r instanceof Error?r.message:String(r));}),()=>{t.close(),this._ref.current=null;for(let r of this.clients)try{r.end();}catch{}this.clients.clear(),this._pingTimer&&clearInterval(this._pingTimer),this._pingTimer=void 0;}}async onReady(e){let t=W.Router();this._ref.current=e,this.database=await e(t,false),this.set(t);}ping(){this._pingTimer||(this._pingTimer=setInterval(()=>{for(let e of this.clients)try{e.write(`event: ping
92
+ data: ${Date.now()}
93
+
94
+ `);}catch{this.clients.delete(e);}},25e3));}async reload(){if(!this._ref.current)return;let e=W.Router(),t=await this._ref.current(e,true);this.database=t,this.set(e);}async prepareConfig(e,t){return t?await F():e}watcherPaths(){let e=v.resolve(m,"fakelab.config.ts");return [...this.config.getSourceFiles(this.options.source),e]}middleware(e,t,r){return this.router(e,t,r)}createTrigger(){let e,t=false,r=false,s=()=>{e&&clearTimeout(e),e=setTimeout(async()=>{if(t){r=true;return}t=true;let i=Date.now();o.info("Hot reload: change detected.");try{o.info("Hot reload: rebuilding routes..."),await this.reload(),this.broadcast();let a=Date.now()-i;o.success("Hot reload: completed in %dms \u2705",a);}catch(a){o.error("Hot reload: rebuild failed \u274C. error: %s",a instanceof Error?a.message:String(a));}finally{t=false,r&&(r=false,s());}},250);};return s}};var J=class p{constructor(e,t){this.options=e;this.config=t;this.start=this.start.bind(this),this.xPoweredMiddleware=this.xPoweredMiddleware.bind(this),this.setupApplication=this.setupApplication.bind(this),this.setupTemplateEngine=this.setupTemplateEngine.bind(this),this.loadLocalEnv(),this.initWebhook(),process.on("SIGINT",()=>{let r=this.config.options.server(this.options.pathPrefix,this.options.port);this.subscriber?.shutdown(r),process.exit(0);});}webhook;subscriber;initWebhook(){if(this.config.options.webhook().enabled){if(!this.config.enabled()){o.warn("Fakelab is disabled. Skipped webhook initialization.");return}this.subscriber=new q,o.warn("Initializating webhook..."),this.webhook=new R(this.subscriber,this.config);}}static init(e,t){let r=new p(e,t);return r.webhook&&!r.webhook.isActivated()&&r.webhook.activate(),r}async shouldRunHeadlessMode(){if(this.options.headless||this.config.isHeadless()){let t=await new U(this.config).generate(this.options);return t||o.error("Headless mode failed. Falling back to standard server mode."),t}return false}async start(){if(await this.shouldRunHeadlessMode()){o.info("Headless mode enabled. Server startup skipped.");return}let e=W(),t=W.Router(),r=Me.createServer(e);this.setupApplication(e),this.setupTemplateEngine(e),await this.config.initializeRuntimeConfig(d,this.options);let s=Q.register(e,t,this.config,this.options);await s.onReady(async(a,n)=>{await this.config.initializeRuntimeConfig(d,this.options);let c=await s.prepareConfig(this.config,n),u=M.initHandlers(c),l=k.register(c);return await l.initialize(),await new G(a,c,u,l,this.options).register({fresh:n}),l});let i=s.watch();process.on("SIGINT",i),s.database&&this.run(r,s.database,this.options);}setupApplication(e){e.disable("x-powered-by"),e.use(W.json()),e.use(He({methods:["GET","POST","OPTIONS"]})),e.use(W.static(d+"/public")),e.use(this.xPoweredMiddleware);}setupTemplateEngine(e){e.set("views",v.join(d,"views")),e.set("view engine","ejs"),e.use(Be),e.set("layout","layouts/main");}listen(e,t){this.subscriber?.started(t),e.enabled()&&this.config.enabled()&&o.info("database: %s",k.DATABASE_DIR),o.info("Server%s listening at http://localhost:%d",this.config.enabled()?"":"(disabled)",t.port),this.config.enabled()&&console.log(qe.textSync("FAKELAB"));}run(e,t,r){let s=this.config.options.server(r.pathPrefix,r.port);e.listen(s.port,"localhost",()=>this.listen(t,s));}xPoweredMiddleware(e,t,r){t.setHeader("x-powered-by","fakelab"),r();}loadLocalEnv(){try{process.env.NODE_ENV==="development"&&process.loadEnvFile("./.env.local");}catch(e){process.env.NODE_ENV==="development"&&o.warn("Cannot load .env.local file for debugging. error: %s",e);}}};var $=class p{constructor(e,t){this.options=e;this.config=t;this.capture=this.capture.bind(this),this.initWebhook();}TARGET_LANGUAGE="typescript";DEFAULT_TYPE_NAME="Fakelab";static SNAPSHOT_DIR=v.resolve(m,".fakelab/snapshots");static _instance;subscriber;webhook;static async init(e){try{let t=await F();return this._instance||(this._instance=new p(e,t)),this._instance.webhook&&!this._instance.webhook.isActivated()&&this._instance.webhook.activate(),this._instance}catch{process.exit(1);}}static async prepare(e){try{let t=await F(),r=this._instance||new p({},t),{enabled:s,sources:i}=t.options.snapshot();return r.webhook&&!r.webhook.isActivated()&&r.webhook.activate(),s&&r.config.enabled()&&e.freshSnapshots&&await r.updateAll(i,!0),r}catch{process.exit(1);}}async capture(e){if(!this.config.enabled()){o.error("Fakelab is disabled. Capture Skipped.");return}let{enabled:t,sources:r}=this.config.options.snapshot();if(!t){o.warn("Snapshot is disabled. Capture Skipped.");return}await g.ensureDir(p.SNAPSHOT_DIR),await g.ensureFile(v.resolve(p.SNAPSHOT_DIR,"__schema.json"));let s=await this.readSnapshotSchema();if(await this.duplicateExists(s)&&(o.error("Snapshot source names must be unique."),process.exit(1)),!e)return this.options.refresh?await this.refresh(r,this.options.refresh,s):this.options.delete?await this.delete(r,this.options.delete,s):await this.updateAll(r);let i=this.suffix(s.sources);this.options?.refresh&&o.warn("--refresh flag has no effect when used with url. Refresh skipped."),this.options?.delete&&o.warn("--delete flag has no effect when used with url. Delete skipped.");let a=await this.fetch({url:e,name:this.options?.name||i});await this.save({url:e,name:this.options?.name||i},a,s),await this.modifyGitignoreFile(".fakelab/*");}async save(e,t,r){try{let s=r.sources.find(a=>a.url===e.url);if(s){o.warn("%s snapshot is already captured. Use \x1B[36mnpx fakelab snapshot --refresh %s\x1B[0m to update.",e.url,s.name);return}let i=this.snapshotName(e.url,!1);o.info("Capturing %s snapshot...",i),e.name||this.options?.name||o.warn("Snapshot source name not found. Auto-generating a name."),await this.write(e.url,t,e.name||this.options?.name),this.subscriber?.captured({url:e.url,name:e.name??this.options?.name,content:t}),o.success("Snapshot %s captured successfully.",o.blue(i));}catch(s){o.error("Cannot save the captured snapshot."),s instanceof Error&&o.debug(s.message),process.exit(1);}}async refresh(e,t,r){let s=e.find(n=>n.name===t.trim());if(s||(s=(r.sources||[]).find(n=>n.name===t)),!s){o.warn("Snapshot source not found. Refresh skipped.");return}o.info("Refreshing %s snapshot source...",o.blue(s.name));let i=v.resolve(p.SNAPSHOT_DIR,this.snapshotName(s.url));if(await g.exists(i)){let n=await this.fetch(s);this.subscriber?.refreshed({url:s.url,name:s.name??this.options?.name,content:n}),await g.writeFile(i,n);}else {let n=await this.fetch(s);this.subscriber?.refreshed({url:s.url,name:s.name??this.options?.name,content:n}),await this.save(s,n,r);}o.success("Snapshot source %s refreshed successfully.",o.blue(s.name));}async delete(e,t,r){let s=e.find(a=>a.name===t.trim());if(s||(s=(r.sources||[]).find(a=>a.name===t)),!s){o.warn("Snapshot source not found. Delete skipped.");return}o.info("Deleting %s snapshot source...",o.blue(s.name));let i=v.resolve(p.SNAPSHOT_DIR,this.snapshotName(s.url));await g.rm(i,{force:true}),await this.updateSnapshotSchema({url:s.url,delete:true}),this.subscriber?.deleted({url:s.url,name:s.name??this.options?.name}),o.success("Snapshot source %s deleted successfully.",o.blue(s.name));}async updateAll(e,t=false){o.info(t?"Refreshing all snapshots...":"Updating all snapshots...");let r=await this.readSnapshotSchema();try{await Promise.all(e.map(async s=>{let i=v.resolve(p.SNAPSHOT_DIR,this.snapshotName(s.url));if(await g.exists(i)){let n=await this.fetch(s);await g.writeFile(i,n);}else {let n=await this.fetch(s);await this.save(s,n,r);}})),o.success("All snapshots are updated.");}catch(s){o.error("Failed to update.",s),process.exit(1);}}async fetch({name:e,url:t,headers:r}){this.isValidUrl(t)||(o.error("Invalid snapshot URL. Please provide a valid http URL."),process.exit(1));let i=await(await fetch(t,{headers:r})).text();this.isValidJSON(i)||(o.error("Invalid snapshot response format. Expected JSON but received non-JSON data."),process.exit(1));let a=await import('quicktype-core'),n=a.jsonInputForTargetLanguage(this.TARGET_LANGUAGE);await n.addSource({name:e,samples:[i]});let c=new a.InputData;c.addInput(n);let{lines:u}=await a.quicktype({inputData:c,lang:this.TARGET_LANGUAGE,rendererOptions:{"just-types":true}});return u.join(`
95
+ `)}async write(e,t,r,s){await g.writeFile(v.resolve(p.SNAPSHOT_DIR,this.snapshotName(e)),t),await this.updateSnapshotSchema({url:e,name:r,headers:s});}async readSnapshotSchema(){let e={sources:[]};try{e=await g.readJSON(v.resolve(p.SNAPSHOT_DIR,"__schema.json"));}catch{}return e.sources||(e.sources=[]),e}async updateSnapshotSchema(e){let t=await this.readSnapshotSchema(),r=t.sources||[],s=r.findIndex(i=>i.url===e.url);if(e.delete)r.splice(s,1);else if(s===-1)r.push({...e,name:e.name||this.suffix(r)});else {let i=r[s].name,a=r[s].headers;r.splice(s,1,{...r[s],name:e.name||i,headers:e.headers||a});}t.sources=r,await g.writeJSON(v.resolve(p.SNAPSHOT_DIR,"__schema.json"),t);}initWebhook(){if(!this.config.enabled())return;let{enabled:e}=this.config.options.snapshot();if(e){let t=this.config.options.webhook();t.enabled&&(this.subscriber=new B(t.hooks),o.warn("Initializating webhook..."),this.webhook=new R(this.subscriber,this.config));}}snapshotName(e,t=true){return e.replace(/^https?:\/\//,"").replace(/[/:?.&=#]/g,"_")+(t?".ts":"")}suffix(e){let t=e.filter(r=>r.name.startsWith(this.DEFAULT_TYPE_NAME));return `${this.DEFAULT_TYPE_NAME}${t.length}`}isValidUrl(e){try{let t=new URL(e);return !(t.protocol!=="http:"&&t.protocol!=="https:")}catch{return false}}isValidJSON(e){try{return JSON.parse(e),!0}catch{return false}}async duplicateExists(e){let t=e.sources.map(r=>r.name);return new Set(t).size!==t.length}async modifyGitignoreFile(e){try{let t=v.resolve(m,".gitignore");if((await g.readFile(t,{encoding:"utf8"})).split(`
96
+ `).some(s=>s.trim()===e.trim()))return;await g.appendFile(t,`
97
+ ${e}`);}catch(t){o.warn("Cannot modify .gitignore. error: %s",t);}}};var z=new Command,ee=g.readJSONSync(v.join(d,"../package.json"));z.name(ee.name).description(ee.description).version(ee.version);z.command("serve").description("start server").option("-s, --source <char>","config source path").option("-x, --pathPrefix <char>","server url path prefix").option("-p, --port <number>","server port number",parseInt).option("-l, --locale <char>","faker custom locale").option("-h, --headless","headless mode").option("-t, --ts-config-path <char>","typescript config file path","tsconfig.json").option("-f, --fresh-snapshots","capture or refresh all snapshots").action(async p=>{let e=await $.prepare(p);J.init(p,e.config).start();});z.command("snapshot").description("capture a url response to a fakelab entity").argument("[string]","url to capture").option("-s, --name <string>","specify snapshot source name").option("-r, --refresh <string>","refresh the specified snapshot").option("-d, --delete <string>","delete the specified snapshot").action(async(p,e)=>{(await $.init(e)).capture(p);});z.parse();
package/lib/main.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- declare const FAKER_LOCALES: readonly ["af", "ar", "az", "bn", "cs", "cy", "da", "de", "dv", "el", "en", "eo", "es", "fa", "fi", "fr", "he", "hr", "hu", "hy", "id", "it", "ja", "ka", "ko", "ku", "lv", "mk", "nb", "ne", "nl", "pl", "pt", "ro"];
2
- type FakerLocale = (typeof FAKER_LOCALES)[number];
1
+ import { allLocales } from '@faker-js/faker';
2
+
3
+ type FakerLocale = keyof typeof allLocales;
3
4
 
4
5
  type ServerEvent = "server:started" | "server:shutdown";
5
6
  type SnapshotEvent = "snapshot:captured" | "snapshot:refreshed" | "snapshot:deleted";
@@ -18,7 +19,7 @@ type ServerOptions = {
18
19
  */
19
20
  pathPrefix?: string;
20
21
  /**
21
- * Includes the snapshot typescript sources if exists.
22
+ * Includes snapshot sources if exists.
22
23
  * @default true
23
24
  */
24
25
  includeSnapshots?: boolean;
@@ -27,7 +28,7 @@ type FakerEngineOptions = {
27
28
  /**
28
29
  * Locale used by the faker engine when generating mock data.
29
30
  * Controls language-specific values such as names, addresses, etc.
30
- * @example "fa"
31
+ * @example "en"
31
32
  */
32
33
  locale?: FakerLocale;
33
34
  };
@@ -88,7 +89,7 @@ type NetworkOptions = NetworkBehaviourOptions & {
88
89
  };
89
90
  type SnapshotDataSource = {
90
91
  /**
91
- * http url
92
+ * Target http or https URL
92
93
  * @example "https://api.example.com/users"
93
94
  */
94
95
  url: string;
@@ -163,11 +164,46 @@ type WebhookOptions = {
163
164
  */
164
165
  hooks: Hook[];
165
166
  };
167
+ type GraphQLOptions = {
168
+ /**
169
+ * Enables graphQL.
170
+ */
171
+ enabled: boolean;
172
+ };
173
+ type CacheOptions = {
174
+ /**
175
+ * Enables the file-based cache.
176
+ *
177
+ * @default true
178
+ */
179
+ enabled: boolean;
180
+ /**
181
+ * Time-to-live (TTL) for cache entries in milliseconds.
182
+ *
183
+ * When set, cached files older than this value are considered expired
184
+ * and will be ignored or regenerated on the next request.
185
+ *
186
+ * @default 15 * 60 * 1000 // 15 minutes
187
+ */
188
+ ttl?: number;
189
+ };
166
190
  type ConfigOptions = {
167
191
  /**
168
192
  * Path or paths to the source files that define the typescript types.
169
193
  */
170
194
  sourcePath: string | string[];
195
+ /**
196
+ * Enables or disables Fakelab.
197
+ *
198
+ * When set to `false`, Fakelab will not initialize or start any services,
199
+ * regardless of other configuration options.
200
+ *
201
+ * This is useful for conditionally enabling Fakelab based on the current
202
+ * runtime environment (for example, `process.env.NODE_ENV === "development"`).
203
+ *
204
+ * @default true
205
+ */
206
+ enabled?: boolean;
171
207
  /**
172
208
  * Enables headless mode.
173
209
  *
@@ -180,7 +216,19 @@ type ConfigOptions = {
180
216
  * @see {@link https://alirezahematidev.github.io/fakelab/docs/guides/headless|Headless Documentation}
181
217
  */
182
218
  headless?: boolean;
183
- tsConfigPath?: string;
219
+ /**
220
+ * Path to a custom typescript config file.
221
+ *
222
+ * If not specified, Fakelab attempts to resolve the nearest `tsconfig.json`
223
+ * automatically.
224
+ */
225
+ tsConfigFilePath?: string;
226
+ /**
227
+ * Cache configuration.
228
+ *
229
+ * @see {@link https://alirezahematidev.github.io/fakelab/docs/guides/cache|Cache Documentation}
230
+ */
231
+ cache?: CacheOptions;
184
232
  /**
185
233
  * Server-related configuration.
186
234
  * @see {@link https://alirezahematidev.github.io/fakelab/docs/guides/server-command|Server Documentation}
@@ -203,6 +251,7 @@ type ConfigOptions = {
203
251
  network?: NetworkOptions;
204
252
  /**
205
253
  * Snapshot configuration.
254
+ *
206
255
  * @see {@link https://alirezahematidev.github.io/fakelab/docs/guides/snapshot|Snapshot Documentation}
207
256
  */
208
257
  snapshot?: SnapshotOptions;
@@ -211,6 +260,11 @@ type ConfigOptions = {
211
260
  * @see {@link https://alirezahematidev.github.io/fakelab/docs/guides/webhook|Webhook Documentation}
212
261
  */
213
262
  webhook?: WebhookOptions;
263
+ /**
264
+ * GraphQL configuration.
265
+ * @see {@link https://alirezahematidev.github.io/fakelab/docs/guides/graphQL|GraphQL Documentation}
266
+ */
267
+ graphQL?: GraphQLOptions;
214
268
  };
215
269
  type ServerCLIOptions = {
216
270
  source?: string;
@@ -218,7 +272,7 @@ type ServerCLIOptions = {
218
272
  port?: number;
219
273
  locale?: string;
220
274
  freshSnapshots?: boolean;
221
- tsConfig?: string;
275
+ tsConfigPath?: string;
222
276
  headless?: boolean;
223
277
  };
224
278
  type ErrorStatusCode = 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511;
@@ -235,16 +289,22 @@ declare class Config {
235
289
  snapshot: () => Required<SnapshotOptions>;
236
290
  faker: (locale?: FakerLocale) => Required<FakerEngineOptions>;
237
291
  webhook: () => Required<WebhookOptions>;
292
+ graphQL: () => Required<GraphQLOptions>;
293
+ cache: () => Required<CacheOptions>;
238
294
  };
239
- tsConfig(): string;
295
+ getTSConfigFilePath(tsConfigFilePath?: string): string;
240
296
  isHeadless(): boolean;
297
+ private _cacheOptions;
241
298
  private _serverOptions;
242
299
  private _databaseOptions;
243
300
  private _networkOptions;
244
301
  private _snapshotOptions;
302
+ private _graphQLOptions;
245
303
  private _fakerOptions;
246
304
  private _webhookOptions;
247
- files(_sourcePath?: string): Promise<string[]>;
305
+ enabled(): boolean;
306
+ getSourceFiles(_sourcePath?: string): string[];
307
+ files(_sourcePath: string | undefined, fresh: boolean): Promise<string[]>;
248
308
  initializeRuntimeConfig(dirname: string, options: ServerCLIOptions): Promise<void>;
249
309
  private getSnapshotSourceFiles;
250
310
  private tryStat;
package/lib/main.js CHANGED
@@ -1,4 +1,4 @@
1
- import h from'fast-glob';import c from'path';import C from'fs-extra';import {stat,access,constants}from'fs/promises';import k from'is-glob';import i from'picocolors';import {fileURLToPath}from'url';import {transform}from'esbuild';var S=new Intl.ListFormat("en",{style:"long",type:"unit"}),o=class{static label(t){switch(t){case "info":return i.blueBright(t.toUpperCase());case "error":return i.redBright(t.toUpperCase());case "warn":return i.yellowBright(t.toUpperCase());case "success":return i.greenBright(t.toUpperCase())}}static log(t,e){return [i.dim(`[${new Date().toISOString()}]`),this.label(t),e].join(" ")}static blue(t){return i.blueBright(t)}static red(t){return i.redBright(t)}static yellow(t){return i.yellowBright(t)}static green(t){return i.greenBright(t)}static info(t,...e){console.log(this.log("info",t),...e);}static warn(t,...e){console.log(this.log("warn",t),...e);}static error(t,...e){console.error(this.log("error",t),...e);}static success(t,...e){console.log(this.log("success",t),...e);}static debug(t,...e){typeof process>"u"||!process.env.DEBUG||console.log(this.log("info",t),...e);}static list(t){return S.format(t)}};var d=["af","ar","az","bn","cs","cy","da","de","dv","el","en","eo","es","fa","fi","fr","he","hr","hu","hy","id","it","ja","ka","ko","ku","lv","mk","nb","ne","nl","pl","pt","ro"];function b(){let a=Intl.DateTimeFormat().resolvedOptions().locale;if(!a)return "en";let[t]=a.split("-"),e=t.toLowerCase();return d.includes(e)?e:"en"}c.dirname(fileURLToPath(import.meta.url));var g=process.cwd();var p=class{constructor(t,e={}){this.dirname=t;this._args=e;}transformOptions={minify:true,platform:"browser",target:"es2022"};replacer(t,e){let r=t;for(let s in e)r=r.replace(new RegExp(s,"g"),e[s].toString().trim());return r}async prepare(t,e){let r=this.replacer(t,this._args),s=c.resolve(this.dirname,e);try{let{code:n}=await transform(r,this.transformOptions);return {code:n,filepath:s}}catch(n){return n instanceof Error&&o.warn(n.message),{code:r,filepath:s}}}};var m=`const _fakelab = {};
1
+ import g from'fast-glob';import l from'path';import P from'fs-extra';import {stat,access,constants}from'fs/promises';import y from'is-glob';import p from'picocolors';import {allLocales}from'@faker-js/faker';import {fileURLToPath}from'url';import {transform}from'esbuild';var L=new Intl.ListFormat("en",{style:"long",type:"unit"}),n=class{static label(t){switch(t){case "info":return p.blueBright(t.toUpperCase());case "error":return p.redBright(t.toUpperCase());case "warn":return p.yellowBright(t.toUpperCase());case "success":return p.greenBright(t.toUpperCase())}}static log(t,e){return [p.dim(`[${new Date().toISOString()}]`),this.label(t),e].join(" ")}static blue(t){return p.blueBright(t)}static red(t){return p.redBright(t)}static yellow(t){return p.yellowBright(t)}static green(t){return p.greenBright(t)}static info(t,...e){console.log(this.log("info",t),...e);}static warn(t,...e){console.log(this.log("warn",t),...e);}static error(t,...e){console.error(this.log("error",t),...e);}static success(t,...e){console.log(this.log("success",t),...e);}static debug(t,...e){typeof process>"u"||!process.env.DEBUG||console.log(this.log("info",t),...e);}static list(t){return L.format(t)}};var O="api",m=5200;var b=Object.keys(allLocales);function _(){let a=Intl.DateTimeFormat().resolvedOptions().locale;if(!a)return "en";let[t]=a.split("-");return b.includes(t)?t:"en"}l.dirname(fileURLToPath(import.meta.url));var k=process.cwd();var c=class{constructor(t,e={}){this.dirname=t;this._args=e;}transformOptions={minify:true,platform:"browser",target:"es2022"};replacer(t,e){let r=t;for(let o in e)r=r.replace(new RegExp(o,"g"),e[o].toString().trim());return r}async prepare(t,e){let r=this.replacer(t,this._args),o=l.resolve(this.dirname,e);try{let{code:s}=await transform(r,this.transformOptions);return {code:s,filepath:o}}catch(s){return s instanceof Error&&n.warn(s.message),{code:r,filepath:o}}}};var w=`const _fakelab = {};
2
2
  _fakelab.url = () => "http://localhost:PORT/PREFIX/";
3
3
 
4
4
  function _count(opts = {}) {
@@ -7,7 +7,9 @@ function _count(opts = {}) {
7
7
  return null;
8
8
  }
9
9
 
10
- _fakelab.gen = async function (name, options) {
10
+ _fakelab.enabled = () => FAKELAB_ENABLED;
11
+
12
+ _fakelab.fetch = async function (name, options) {
11
13
  const count = _count(options);
12
14
  const search = count !== null ? "?count=" + count : "";
13
15
 
@@ -18,20 +20,24 @@ _fakelab.gen = async function (name, options) {
18
20
  return res.json();
19
21
  };
20
22
 
21
- _fakelab.genSync = function (_name, _options) {
22
- console.warn("[fakelab] genSync() is only available in headless mode.");
23
- return {};
24
- };
23
+ _fakelab.gen = function (name, options) {
24
+ const count = _count(options);
25
+
26
+ if(count === null) return functions[name]();
27
+
28
+ return Array.from({length:count},() => functions[name]())
29
+ }
25
30
 
31
+ _fakelab.enabled = _fakelab.enabled.bind(_fakelab);
26
32
  _fakelab.url = _fakelab.url.bind(_fakelab);
33
+ _fakelab.fetch = _fakelab.fetch.bind(_fakelab);
27
34
  _fakelab.gen = _fakelab.gen.bind(_fakelab);
28
- _fakelab.genSync = _fakelab.genSync.bind(_fakelab);
29
35
 
30
36
  const fakelab = Object.freeze(_fakelab);
31
37
 
32
38
 
33
39
  export {fakelab};
34
- `;var l=class a extends p{constructor(e,r,s){super(e,{PORT:r,PREFIX:s});this.dirname=e;this.port=r;this.prefix=s;}SOURCE_FILENAME="runtime.js";static init(e,r,s){return new a(e,r,s)}prepare(){return super.prepare(m,this.SOURCE_FILENAME)}};var O=`const _database = {};
40
+ `;var f=class a extends c{constructor(e,r,o,s){super(e,{PORT:r,PREFIX:o,FAKELAB_ENABLED:s});this.dirname=e;this.port=r;this.prefix=o;this.enabled=s;}SOURCE_FILENAME="runtime.js";static init(e,r,o,s){return new a(e,r,o,s)}prepare(){return super.prepare(w,this.SOURCE_FILENAME)}};var E=`const _database = {};
35
41
  const url = "http://localhost:PORT/PREFIX/";
36
42
 
37
43
  _database.enabled = () => ENABLED;
@@ -74,4 +80,4 @@ database.seed.bind(database);
74
80
  database.flush.bind(database);
75
81
 
76
82
  export { database };
77
- `;var f=class a extends p{constructor(e,r,s,n){super(e,{PORT:r,PREFIX:s,ENABLED:n});this.dirname=e;this.port=r;this.prefix=s;this.enabled=n;}SOURCE_FILENAME="database.js";static init(e,r,s,n){return new a(e,r,s,n)}prepare(){return super.prepare(O,this.SOURCE_FILENAME)}};var u=class{constructor(t){this.configOptions=t;this.files=this.files.bind(this),this._serverOptions=this._serverOptions.bind(this),this._databaseOptions=this._databaseOptions.bind(this),this._networkOptions=this._networkOptions.bind(this),this._snapshotOptions=this._snapshotOptions.bind(this),this._fakerOptions=this._fakerOptions.bind(this),this._webhookOptions=this._webhookOptions.bind(this),this.NETWORK_DEFAULT_OPTIONS=Object.freeze({delay:this.configOptions.network?.delay||0,errorRate:this.configOptions.network?.errorRate||0,timeoutRate:this.configOptions.network?.timeoutRate||0,offline:this.configOptions.network?.offline??false});}FAKELAB_PERSIST_DIR=".fakelab";NETWORK_DEFAULT_OPTIONS;get options(){return {server:this._serverOptions,database:this._databaseOptions,network:this._networkOptions,snapshot:this._snapshotOptions,faker:this._fakerOptions,webhook:this._webhookOptions}}tsConfig(){return this.configOptions.tsConfigPath||"tsconfig.json"}isHeadless(){return this.configOptions.headless??false}_serverOptions(t,e){return {pathPrefix:t||this.configOptions.server?.pathPrefix||"api",port:e||this.configOptions.server?.port||5200,includeSnapshots:this.configOptions.server?.includeSnapshots??true}}_databaseOptions(){return {enabled:this.configOptions.database?.enabled??false}}_networkOptions(){let t=this.configOptions.network?.preset,e=this.configOptions.network?.presets??{};return !t||!e[t]?this.NETWORK_DEFAULT_OPTIONS:{...e[t],...this.configOptions.network??{}}}_snapshotOptions(){return {enabled:this.configOptions.snapshot?.enabled??false,sources:this.configOptions.snapshot?.sources||[]}}_fakerOptions(t){let e=(t||this.configOptions.faker?.locale)?.toLowerCase();return e&&d.includes(e)?{locale:e}:{locale:b()}}_webhookOptions(){return {enabled:this.configOptions.webhook?.enabled??false,hooks:this.configOptions.webhook?.hooks??[]}}async files(t){let e=this.resolveSourcePath(t||this.configOptions.sourcePath),r=Array.from(new Set((await Promise.all(e.map(s=>this.resolveTSFiles(s)))).flat()));if(this._serverOptions().includeSnapshots){let s=await this.getSnapshotSourceFiles();r.push(...s);}if(r.length===0){let s=e.map(n=>c.basename(n));s.length===0?o.error("No source path found."):o.error("No Typescript files found in: %s",o.list(s)),process.exit(1);}return r}async initializeRuntimeConfig(t,e){let{port:r,pathPrefix:s}=this._serverOptions(e.pathPrefix,e.port),n=l.init(t,r,s),_=f.init(t,r,s,this.options.database().enabled),w=await Promise.all([n.prepare(),_.prepare()]);await Promise.all(w.map(({filepath:y,code:E})=>C.writeFile(y,E)));}async getSnapshotSourceFiles(){let t=await h(".fakelab/snapshots/**/*.ts",{absolute:true,ignore:["**/*.d.ts"],cwd:g});return t.length>0&&o.info("Snapshot(s): %s",o.list(t.map(e=>c.parse(e).name))),t}async tryStat(t){try{return await stat(t)}catch{return null}}async isReadable(t){try{return await access(t,constants.R_OK),!0}catch{return false}}resolveSourcePath(t){return (Array.isArray(t)?t:[t]).map(r=>k(r,{strict:true})?r:c.resolve(r))}async resolveTSFiles(t){if(k(t,{strict:true}))return o.info("Source: %s",t),h(t,{absolute:true,ignore:["**/*.d.ts"]});let e=c.resolve(t),r=e.endsWith(".ts")?e:e+".ts";return (await this.tryStat(r))?.isFile()?(await this.isReadable(r)||(o.error("Cannot read file: %s",r),process.exit(1)),o.info("Source: %s",r),[r]):(await this.tryStat(e))?.isDirectory()?(o.info("Source: %s",e),h("**/*.ts",{cwd:e,absolute:true,ignore:["**/*.d.ts"]})):(o.warn("invalid source: [REDACTED]/%s",c.basename(r)),[])}};function D(a){return new u(a)}export{D as defineConfig};
83
+ `;var u=class a extends c{constructor(e,r,o,s){super(e,{PORT:r,PREFIX:o,ENABLED:s});this.dirname=e;this.port=r;this.prefix=o;this.enabled=s;}SOURCE_FILENAME="database.js";static init(e,r,o,s){return new a(e,r,o,s)}prepare(){return super.prepare(E,this.SOURCE_FILENAME)}};var h=class{constructor(t){this.configOptions=t;this.files=this.files.bind(this),this._serverOptions=this._serverOptions.bind(this),this._databaseOptions=this._databaseOptions.bind(this),this._networkOptions=this._networkOptions.bind(this),this._snapshotOptions=this._snapshotOptions.bind(this),this._fakerOptions=this._fakerOptions.bind(this),this._webhookOptions=this._webhookOptions.bind(this),this._graphQLOptions=this._graphQLOptions.bind(this),this._cacheOptions=this._cacheOptions.bind(this),this.NETWORK_DEFAULT_OPTIONS=Object.freeze({delay:this.configOptions.network?.delay||0,errorRate:this.configOptions.network?.errorRate||0,timeoutRate:this.configOptions.network?.timeoutRate||0,offline:this.configOptions.network?.offline??false});}FAKELAB_PERSIST_DIR=".fakelab";NETWORK_DEFAULT_OPTIONS;get options(){return {server:this._serverOptions,database:this._databaseOptions,network:this._networkOptions,snapshot:this._snapshotOptions,faker:this._fakerOptions,webhook:this._webhookOptions,graphQL:this._graphQLOptions,cache:this._cacheOptions}}getTSConfigFilePath(t){return t||this.configOptions.tsConfigFilePath||"tsconfig.json"}isHeadless(){return this.configOptions.headless??false}_cacheOptions(){return {enabled:this.configOptions.cache?.enabled??true,ttl:this.configOptions.cache?.ttl||900*1e3}}_serverOptions(t,e){return {pathPrefix:t||this.configOptions.server?.pathPrefix||O,port:e||this.configOptions.server?.port||m,includeSnapshots:this.configOptions.server?.includeSnapshots??true}}_databaseOptions(){return {enabled:this.configOptions.database?.enabled??false}}_networkOptions(){let t=this.configOptions.network?.preset,e=this.configOptions.network?.presets??{};return !t||!e[t]?this.NETWORK_DEFAULT_OPTIONS:{...e[t],...this.configOptions.network??{}}}_snapshotOptions(){return {enabled:this.configOptions.snapshot?.enabled??false,sources:this.configOptions.snapshot?.sources||[]}}_graphQLOptions(){return {enabled:this.configOptions.graphQL?.enabled??false}}_fakerOptions(t){let e=t||this.configOptions.faker?.locale;return e&&b.includes(e)?{locale:e}:{locale:_()}}_webhookOptions(){return {enabled:this.configOptions.webhook?.enabled??false,hooks:this.configOptions.webhook?.hooks??[]}}enabled(){return this.configOptions.enabled??true}getSourceFiles(t){let e=t||this.configOptions.sourcePath;return this.resolveSourcePath(e)}async files(t,e){let r=t||this.configOptions.sourcePath;if(!this.enabled())return [];let o=this.resolveSourcePath(r),s=o.length>0?Array.from(new Set((await Promise.all(o.map(i=>this.resolveTSFiles(i,e)))).flat())):[];if(this._serverOptions().includeSnapshots){let i=await this.getSnapshotSourceFiles();s.push(...i);}if(s.length===0){let i=o.map(d=>l.basename(d));i.length===0?n.error("No source path found."):n.error("No Typescript files found in: %s",n.list(i)),process.exit(1);}return s}async initializeRuntimeConfig(t,e){if(!this.enabled()){n.warn("Fakelab is disabled. Skipping runtime initialization.");return}let{port:r,pathPrefix:o}=this._serverOptions(e.pathPrefix,e.port),s=f.init(t,r,o,this.enabled()),i=u.init(t,r,o,this.options.database().enabled),d=await Promise.all([s.prepare(),i.prepare()]);await Promise.all(d.map(({filepath:S,code:F})=>P.writeFile(S,F)));}async getSnapshotSourceFiles(){let t=await g(".fakelab/snapshots/**/*.ts",{absolute:true,ignore:["**/*.d.ts"],cwd:k});return t.length>0&&n.info("Snapshot(s): %s",n.list(t.map(e=>l.parse(e).name))),t}async tryStat(t){try{return await stat(t)}catch{return null}}async isReadable(t){try{return await access(t,constants.R_OK),!0}catch{return false}}resolveSourcePath(t){return (Array.isArray(t)?t:[t]).map(r=>y(r,{strict:true})?r:l.resolve(r))}async resolveTSFiles(t,e){if(y(t,{strict:true}))return e||n.info("Source: %s",t),g(t,{absolute:true,ignore:["**/*.d.ts"]});let r=l.resolve(t),o=r.endsWith(".ts")?r:r+".ts";return (await this.tryStat(o))?.isFile()?(await this.isReadable(o)||(n.error("Cannot read file: %s",o),process.exit(1)),e||n.info("Source: %s",o),[o]):(await this.tryStat(r))?.isDirectory()?(e||n.info("Source: %s",r),g("**/*.ts",{cwd:r,absolute:true,ignore:["**/*.d.ts"]})):(e||n.warn("invalid source: [REDACTED]/%s",l.basename(o)),[])}};function N(a){return new h(a)}export{N as defineConfig};