fakelab 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -37,7 +37,7 @@ Fakelab allows you to control generated mock data using JSDoc tags.
37
37
  You simply annotate your TypeScript interfaces with the @faker tag, and Fakelab uses the corresponding [faker](https://fakerjs.dev/)
38
38
  method when generating mock values.
39
39
 
40
- interfaces folder:
40
+ `/interfaces/user.ts`:
41
41
 
42
42
  ```typescript
43
43
  export interface User {
package/lib/cli.js CHANGED
@@ -1 +1,2 @@
1
- import {Command}from'commander';import w from'express';import q from'cors';import F from'path';import {fileURLToPath}from'url';import {bundleRequire}from'bundle-require';import O from'joycon';import'fast-glob';import I from'fs-extra';import'fs/promises';import u from'picocolors';import $ from'express-ejs-layouts';import B from'http';import'ejs';import U from'qs';import h from'zod';import {Project}from'ts-morph';import {v4}from'uuid';import J from'figlet';var c=class t{static prefix(e){let r=new Date().toLocaleTimeString();return {info:u.blue,warn:u.yellow,error:u.red,success:u.green,debug:u.magenta}[e](`[${r}] FAKELAB_${e.toUpperCase()}`)}static log(e,r,...s){let o=u.white,n=t.prefix(e);(e==="error"?console.error:e==="warn"?console.warn:console.log)(n,o(r),...s);}static info(e,...r){this.log("info",e,...r);}static warn(e,...r){this.log("warn",e,...r);}static error(e,...r){this.log("error",e,...r);}static success(e,...r){this.log("success",e,...r);}};async function k(){try{let e=await new O().resolve({files:["fakelab.config.ts"]});return e||(c.error("No fakelab config file is detected."),process.exit(1)),(await bundleRequire({filepath:e})).mod.default}catch{c.error("Could not load the config file."),process.exit(1);}}var g=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,r){let s={};return await Promise.all(e.map(async o=>{let n=o.getTypeAtLocation(o.getValueDeclarationOrThrow());s[o.getName()]=await r(n,this,this.readJSDocTags(o));})),s}async union(e){let r=await Promise.all(e);return r[Math.floor(Math.random()*r.length)]}async intersection(e){let r=await Promise.all(e);return r[Math.floor(Math.random()*r.length)]}evalArgs(e){if(!(!e||!e.trim()))return Function(`"use strict"; return (${e});`)()}readJSDocTags(e){let r=e.getJsDocTags().filter(s=>s.getName()===this.JSDOC_FAKER_FIELD);return r.length===0?[]:r.map(s=>{let[o]=s.getText();if(!o)return;let n=o.text.trim().match(this.FAKER_TAG_REGEX);if(!n)return;let[,i,a]=n,f=this.evalArgs(a);return {path:i,args:f}})}execute(e,r){if(!e)return r();let s=e.path.split("."),o=this.faker;for(let n of s)o=o[n],o||(c.error("Invalid faker module path:",e.path),process.exit(1));typeof o!="function"&&(c.error("Unresolvable faker function.",e.path),process.exit(1));try{return e.args?o(e.args):o()}catch{return c.error("Passed invalid arguments to faker function."),o()}}};var d=class{constructor(e){this.files=e;let s=new Project({tsConfigFilePath:"tsconfig.json"}).addSourceFilesAtPaths(e);this.__targets=s.flatMap(o=>o.getInterfaces());}__targets;async run(e){return await e()}entities(){return new Map(this.__targets.map(e=>[e.getName().toLowerCase(),e.getType()]))}async loadFaker(e){let{faker:r}=await import(`@faker-js/faker/locale/${e.locale}`);return r}};async function p(t,e,r=[],s=0){if(t.isString())return e.string(r[s]);if(t.isNumber())return e.int(r[s]);if(t.isBoolean())return e.bool(r[s]);if(t.isBigInt())return e.bigInt(r[s]);if(t.isBooleanLiteral())return e.litbool(t.getText());if(t.isLiteral())return t.getLiteralValue();if(!t.isUndefined()){if(t.isUnion()){let o=t.getUnionTypes();return await e.union(o.map((n,i)=>p(n,e,r,i)))}if(t.isIntersection()){let o=t.getIntersectionTypes();return await e.intersection(o.map((n,i)=>p(n,e,r,i)))}if(t.isArray()){let o=t.getArrayElementTypeOrThrow();return [await p(o,e,r,s)]}if(t.isObject()){let o=t.getProperties();return await e.object(o,(n,i,a)=>p(n,i,a,s))}return null}}function _({each:t}){return {resolve:async r=>await Promise.all(Array.from({length:r},(s,o)=>t(o)))}}function R(t,e){return t==="uuid"?v4():e+1}function j(t,e,r){return async s=>{let o=await p(t,e);return r.uid&&typeof o=="object"?{[r.uid]:R(r.s,s),...o}:o}}async function x(t){let e=new d(t.files),r=await e.loadFaker(t.fakerOptions),s=new g(r),o=e.entities();async function n(i,a){let f=_({each:j(i,s,a)}),l=await(a.count?f.resolve(parseInt(a.count)):p(i,s)),m=JSON.stringify(l,null,2);return {data:l,json:m}}return {entities:o,forge:n}}var N=fileURLToPath(import.meta.url),G=F.dirname(N),T=I.readJSONSync(F.join(G,"../package.json")),y=class{constructor(e,r){this.router=e;this.config=r;this.prefix=this.config.serverOptions.pathPrefix;}prefix;get querySchema(){return h.object({count:h.string().optional(),uid:h.string().optional(),s:h.string().optional()})}async handleQueries(e){let{success:r,data:s,error:o}=await this.querySchema.safeParseAsync(e.query);return r?s:(c.warn(o.message),{})}async register(){let{entities:e,forge:r}=await x(this.config);this.router.get("/",(s,o)=>{let n=s.path;o.render("index",{currentPath:n,entities:e,version:T.version});}),this.router.get("/:name",async(s,o)=>{let n=`${s.protocol}://${s.host}/`,i=s.path,a=s.params.name,f=await this.handleQueries(s),l=U.stringify(f,{addQueryPrefix:true}),m=e.get(a.toLowerCase());if(m){let{json:P}=await r(m,f);o.render("preview",{name:a,currentPath:i,address:n,search:l,json:P,entities:e,version:T.version,prefix:this.prefix});}else o.redirect("/");}),this.router.get(`/${this.prefix}/:name`,async(s,o)=>{try{let n=s.params.name,i=await this.handleQueries(s),a=e.get(n.toLowerCase());if(a){let{data:f}=await r(a,i);o.status(200).json(f);}else o.status(400).json({message:"The requested interface is not found"});}catch(n){o.status(500).send(n);}});}};var K=fileURLToPath(import.meta.url),E=F.dirname(K);function X(t,e){let r=e.serverOptions.port;t.listen(r,"localhost",async()=>{c.info(`Server: http://localhost:${r}`),console.log(await J.text("FAKELAB"));});}function z(t,e,r){e.setHeader("x-powered-by","fakelab"),r();}function Q(t){t.disable("x-powered-by"),t.use(w.json()),t.use(q({methods:"GET"})),t.use(w.static(E+"/public")),t.use(z);}function V(t){t.set("views",F.join(E,"views")),t.set("view engine","ejs"),t.use($),t.set("layout","layouts/main");}async function L(t){let e=w(),r=w.Router(),s=B.createServer(e);Q(e),V(e),await new y(r,t).register(),e.use(r),X(s,t);}var v=new Command;v.name("falekab").description("CLI to generate mock data").version("0.0.0");v.command("serve").description("start server").action(async()=>{let t=await k();L(t);});v.parse();
1
+ import P from'path';import {fileURLToPath}from'url';import $ from'fs-extra';import l from'zod';import {Command}from'commander';import k from'express';import M from'cors';import K from'express-ejs-layouts';import q from'http';import'ejs';import G from'qs';import {Project}from'ts-morph';import {v4}from'uuid';import u from'picocolors';import Q from'figlet';import {bundleRequire}from'bundle-require';import ee from'joycon';var a=class t{static prefix(e){let r=new Date().toLocaleTimeString();return {info:u.blue,warn:u.yellow,error:u.red,success:u.green,debug:u.magenta}[e](`[${r}] FAKELAB_${e.toUpperCase()}`)}static log(e,r,...n){let o=u.white,s=t.prefix(e);(e==="error"?console.error:e==="warn"?console.warn:console.log)(s,o(r),...n);}static info(e,...r){this.log("info",e,...r);}static warn(e,...r){this.log("warn",e,...r);}static error(e,...r){this.log("error",e,...r);}static success(e,...r){this.log("success",e,...r);}};var d=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,r){let n={};return await Promise.all(e.map(async o=>{let s=o.getTypeAtLocation(o.getValueDeclarationOrThrow());n[o.getName()]=await r(s,this,this.readJSDocTags(o));})),n}async union(e){let r=await Promise.all(e);return r[Math.floor(Math.random()*r.length)]}async intersection(e){let r=await Promise.all(e);return r[Math.floor(Math.random()*r.length)]}evalArgs(e){if(!(!e||!e.trim()))return Function(`"use strict"; return (${e});`)()}readJSDocTags(e){let r=e.getJsDocTags().filter(n=>n.getName()===this.JSDOC_FAKER_FIELD);return r.length===0?[]:r.map(n=>{let[o]=n.getText();if(!o)return;let s=o.text.trim().match(this.FAKER_TAG_REGEX);if(!s)return;let[,i,c]=s,p=this.evalArgs(c);return {path:i,args:p}})}execute(e,r){if(!e)return r();let n=e.path.split("."),o=this.faker;for(let s of n)o=o[s],o||(a.error("Invalid faker module path:",e.path),process.exit(1));typeof o!="function"&&(a.error("Unresolvable faker function.",e.path),process.exit(1));try{return e.args?o(e.args):o()}catch{return a.error("Passed invalid arguments to faker function."),o()}}};var y=class{constructor(e){this.files=e;let n=new Project({tsConfigFilePath:"tsconfig.json"}).addSourceFilesAtPaths(e);this.__targets=n.flatMap(o=>o.getInterfaces());}__targets;async run(e){return await e()}normalizePath(e){return e.split(P.sep).join(P.posix.sep)}entities(){let e=this.__targets.map(r=>{let n=r.getName().toLowerCase(),o=r.getType(),s=this.normalizePath(process.cwd()),c=this.normalizePath(r.getSourceFile().getDirectoryPath()).replace(s,""),p=r.getSourceFile().getBaseName(),f=`${c}/${p}`;return [n,{type:o,filepath:f}]});return new Map(e)}async loadFaker(e,r){let{faker:n}=await import(`@faker-js/faker/locale/${r||e.locale}`);return n}};async function m(t,e,r=[],n=0){if(t.isString())return e.string(r[n]);if(t.isNumber())return e.int(r[n]);if(t.isBoolean())return e.bool(r[n]);if(t.isBigInt())return e.bigInt(r[n]);if(t.isBooleanLiteral())return e.litbool(t.getText());if(t.isLiteral())return t.getLiteralValue();if(!t.isUndefined()){if(t.isUnion()){let o=t.getUnionTypes();return await e.union(o.map((s,i)=>m(s,e,r,i)))}if(t.isIntersection()){let o=t.getIntersectionTypes();return await e.intersection(o.map((s,i)=>m(s,e,r,i)))}if(t.isArray()){let o=t.getArrayElementTypeOrThrow();return [await m(o,e,r,n)]}if(t.isObject()){let o=t.getProperties();return await e.object(o,(s,i,c)=>m(s,i,c,n))}return null}}function R({each:t}){return {resolve:async r=>await Promise.all(Array.from({length:r},(n,o)=>t(o)))}}function D(t,e){return t==="uuid"?v4():e+1}function N(t,e,r){return async n=>{let o=await m(t,e);return r.uid&&typeof o=="object"?{[r.uid]:D(r.s,n),...o}:o}}async function L(t,e){let r=await t.files(e.source),n=new y(r),o=await n.loadFaker(t.fakerOpts(e.locale)),s=new d(o),i=n.entities();async function c(p,f){let g=R({each:N(p,s,f)}),h=await(f.count?g.resolve(parseInt(f.count)):m(p,s)),x=JSON.stringify(h,null,2);return {data:h,json:x}}return {entities:i,forge:c}}var B=fileURLToPath(import.meta.url),J=P.dirname(B),F=$.readJSONSync(P.join(J,"../package.json")),v=class{constructor(e,r,n){this.router=e;this.config=r;this.opts=n;let{pathPrefix:o}=this.config.serverOpts(this.opts.pathPrefix);this.prefix=o;}prefix;get querySchema(){return l.object({count:l.string().optional(),uid:l.string().optional(),strategy:l.string().optional()})}async handleQueries(e){let{success:r,data:n,error:o}=await this.querySchema.safeParseAsync(e.query);return r?n:(a.warn(o.message),{})}async register(){let{entities:e,forge:r}=await L(this.config,this.opts);this.router.get("/",(n,o)=>{let s=n.path;o.render("index",{currentPath:s,entities:e,version:F.version});}),this.router.get("/:name",async(n,o)=>{let s=`${n.protocol}://${n.host}/`,i=n.path,c=n.params.name,p=await this.handleQueries(n),f=G.stringify(p,{addQueryPrefix:true}),g=e.get(c.toLowerCase());if(g){let{json:h}=await r(g.type,p),x=g.filepath;o.render("preview",{name:c,filepath:x,currentPath:i,address:s,search:f,json:h,entities:e,version:F.version,prefix:this.prefix});}else o.redirect("/");}),this.router.get(`/${this.prefix}/:name`,async(n,o)=>{try{let s=n.params.name,i=await this.handleQueries(n),c=e.get(s.toLowerCase());if(c){let{data:p}=await r(c.type,i);o.status(200).json(p);}else o.status(400).json({message:"The requested interface is not found"});}catch(s){o.status(500).send(s);}});}};var V=fileURLToPath(import.meta.url),C=P.dirname(V);function X(t,e,r){let{port:n}=e.serverOpts(r.pathPrefix,r.port);t.listen(n,"localhost",async()=>{a.info(`Server: http://localhost:${n}`),console.log(await Q.text("FAKELAB"));});}function H(t,e,r){e.setHeader("x-powered-by","fakelab"),r();}function Z(t){t.disable("x-powered-by"),t.use(k.json()),t.use(M({methods:"GET"})),t.use(k.static(C+"/public")),t.use(H);}function W(t){t.set("views",P.join(C,"views")),t.set("view engine","ejs"),t.use(K),t.set("layout","layouts/main");}async function _(t,e){let r=k(),n=k.Router(),o=q.createServer(r);Z(r),W(r),await new v(n,t,e).register(),r.use(n),X(o,t,e);}async function j(){try{let e=await new ee().resolve({files:["fakelab.config.ts"]});return e||(a.error("No fakelab config file is detected."),process.exit(1)),(await bundleRequire({filepath:e})).mod.default}catch{a.error("Could not load the config file."),process.exit(1);}}var T=new Command,ne=fileURLToPath(import.meta.url),se=P.dirname(ne),b=$.readJSONSync(P.join(se,"../package.json"));T.name(b.name).description(b.description).version(b.version);var ie=l.object({source:l.string().optional(),pathPrefix:l.string().optional(),port:l.number().int().optional(),locale:l.string().optional()});T.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").action(async t=>{let e=await ie.safeParseAsync(t);e.error&&(a.error(l.treeifyError(e.error).errors.join(`
2
+ `)),process.exit(1));let r=await j();_(r,e.data);});T.parse();
package/lib/main.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- type FakerLocale = "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
-
3
1
  type ServerOptions = {
4
2
  /**
5
3
  * @default 5200
@@ -14,14 +12,29 @@ type FakerEngineOptions = {
14
12
  locale?: FakerLocale;
15
13
  };
16
14
  type ConfigOptions = {
17
- sourcePath: string;
15
+ sourcePath: string | string[];
18
16
  server?: ServerOptions;
19
17
  faker?: FakerEngineOptions;
20
18
  };
21
- declare function defineConfig(options: ConfigOptions): Promise<{
19
+ type UserConfig = {
22
20
  files: string[];
23
21
  serverOptions: Required<ServerOptions>;
24
22
  fakerOptions: Required<FakerEngineOptions>;
25
- }>;
23
+ };
24
+ type FakerLocale = "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";
25
+
26
+ declare class Config {
27
+ private readonly opts;
28
+ constructor(opts: ConfigOptions);
29
+ files(_sourcePath?: string): Promise<string[]>;
30
+ serverOpts(prefix?: string, port?: number): Required<ServerOptions>;
31
+ fakerOpts(locale?: FakerLocale): Required<FakerEngineOptions>;
32
+ private tryStat;
33
+ private isReadable;
34
+ private resolveSourcePath;
35
+ private resolveTSFiles;
36
+ }
37
+
38
+ declare function defineConfig(options: ConfigOptions): Config;
26
39
 
27
- export { defineConfig };
40
+ export { type UserConfig, defineConfig };
package/lib/main.js CHANGED
@@ -1 +1,3 @@
1
- import'bundle-require';import'joycon';import u from'fast-glob';import g from'fs-extra';import p from'path';import {stat,access,constants}from'fs/promises';import i from'picocolors';var s=class{static PREFIX="api";static PORT=5200;static DEFAULT_LOCALE="en";static locale(){let e=Intl.DateTimeFormat().resolvedOptions().locale;if(!e)return this.DEFAULT_LOCALE;let[t]=e.split("-");return t.toLowerCase()}};var o=class r{static prefix(e){let t=new Date().toLocaleTimeString();return {info:i.blue,warn:i.yellow,error:i.red,success:i.green,debug:i.magenta}[e](`[${t}] FAKELAB_${e.toUpperCase()}`)}static log(e,t,...n){let a=i.white,l=r.prefix(e);(e==="error"?console.error:e==="warn"?console.warn:console.log)(l,a(t),...n);}static info(e,...t){this.log("info",e,...t);}static warn(e,...t){this.log("warn",e,...t);}static error(e,...t){this.log("error",e,...t);}static success(e,...t){this.log("success",e,...t);}};var f=".ts";async function w(r){if(r.endsWith(f))return r;let e=r+f;return await g.exists(e)?e:null}async function h(r){return await u("**/*.ts",{cwd:r,absolute:true,ignore:["**/*.d.ts"]})}async function y(r){let e=await w(r);try{if(e&&(await stat(e)).isFile()){if(await v(e))return o.info("Source:",e),[e];o.info("Could not access to the provided file path %s",p),process.exit(1);}}catch{o.error("The provided source path does not exists."),process.exit(1);}try{if((await stat(r)).isDirectory())return o.info("Source:",r),await h(r)}catch{o.error("The provided source path does not exists."),process.exit(1);}o.error("The provided source path does not exists."),process.exit(1);}async function x(r){let e=p.resolve(r.sourcePath),t=await y(e);t.length===0&&(o.error("No Typescript files found in %s",e),process.exit(1));let n={pathPrefix:r.server?.pathPrefix||s.PREFIX,port:r.server?.port||s.PORT},a={locale:r.faker?.locale||s.locale()};return {files:t,serverOptions:n,fakerOptions:a}}async function v(r){try{return await access(r,constants.R_OK),!0}catch{return false}}export{x as defineConfig};
1
+ import g from'fast-glob';import l from'path';import {stat,access,constants}from'fs/promises';import i from'picocolors';var s=class o{static prefix(t){let r=new Date().toLocaleTimeString();return {info:i.blue,warn:i.yellow,error:i.red,success:i.green,debug:i.magenta}[t](`[${r}] FAKELAB_${t.toUpperCase()}`)}static log(t,r,...e){let n=i.white,c=o.prefix(t);(t==="error"?console.error:t==="warn"?console.warn:console.log)(c,n(r),...e);}static info(t,...r){this.log("info",t,...r);}static warn(t,...r){this.log("warn",t,...r);}static error(t,...r){this.log("error",t,...r);}static success(t,...r){this.log("success",t,...r);}};function p(){let o=Intl.DateTimeFormat().resolvedOptions().locale;if(!o)return "en";let[t]=o.split("-");return t.toLowerCase()}var a=class{constructor(t){this.opts=t;this.files=this.files.bind(this),this.serverOpts=this.serverOpts.bind(this),this.fakerOpts=this.fakerOpts.bind(this);}async files(t){let r=this.resolveSourcePath(t||this.opts.sourcePath),e=Array.from(new Set((await Promise.all(r.map(n=>this.resolveTSFiles(n)))).flat()));return e.length===0&&(s.error(`No Typescript files found in:
2
+ %s`,r.join(`
3
+ `)),process.exit(1)),e}serverOpts(t,r){return {pathPrefix:t||this.opts.server?.pathPrefix||"api",port:r||this.opts.server?.port||5200}}fakerOpts(t){return {locale:t||this.opts.faker?.locale||p()}}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(e=>l.resolve(e))}async resolveTSFiles(t){let r=l.resolve(t),e=r.endsWith(".ts")?r:r+".ts";if((await this.tryStat(e))?.isFile()){if(!await this.isReadable(e))throw new Error(`Cannot read file: ${e}`);return s.info("Source:",e),[e]}if((await this.tryStat(r))?.isDirectory())return s.info("Source:",r),g("**/*.ts",{cwd:r,absolute:true,ignore:["**/*.d.ts"]});s.error(`Invalid source path: ${t}`),process.exit(1);}};function w(o){return new a(o)}export{w as defineConfig};
@@ -115,22 +115,46 @@ pre {
115
115
  padding: 12px 8px;
116
116
  border-radius: 4px;
117
117
  cursor: pointer;
118
+ display: flex;
119
+ gap: 8px;
120
+ justify-content: space-between;
121
+ align-items: center;
122
+ text-decoration: none;
123
+ &:hover > .item_name {
124
+ color: oklch(98.5% 0.002 247.85);
125
+ }
126
+ &:hover > .item_filepath {
127
+ color: oklch(98.463% 0.00181 249.248 / 0.8);
128
+ }
129
+ }
130
+
131
+ .sidebar_content_list_item .item_name {
118
132
  transition: all 250ms ease-in-out;
119
133
  color: oklch(98.463% 0.00181 249.248 / 0.726);
120
- text-decoration: none;
134
+ user-select: none;
121
135
  font-size: 14px;
122
- &:hover {
123
- color: oklch(98.5% 0.002 247.839);
124
- }
136
+ }
125
137
 
126
- > span {
127
- user-select: none;
128
- }
138
+ .sidebar_content_list_item .item_filepath {
139
+ transition: all 250ms ease-in-out;
140
+ white-space: nowrap;
141
+ line-clamp: 1;
142
+ text-overflow: ellipsis;
143
+ font-size: 12px;
144
+ color: oklch(98.463% 0.00181 249.248 / 0.6);
145
+ font-style: italic;
129
146
  }
130
147
 
131
148
  .sidebar_content_list_item.active {
132
- color: oklch(98.5% 0.002 247.839);
133
149
  background-color: oklch(37.306% 0.03436 260.191 / 0.26);
150
+
151
+ > .item_name {
152
+ color: oklch(98.5% 0.002 247.85);
153
+ }
154
+
155
+ > .item_filepath {
156
+ color: oklch(98.463% 0.00181 249.248 / 0.8);
157
+ }
134
158
  }
135
159
 
136
160
  .sidebar_content_leading .sidebar_content_title {
@@ -180,7 +204,13 @@ pre {
180
204
 
181
205
  .header .interface_name {
182
206
  font-size: 16px;
183
- color: oklch(98.463% 0.00181 249.248 / 0.61);
207
+ color: oklch(98.463% 0.00181 249.248 / 0.8);
208
+ }
209
+
210
+ .interface_name .interface_source_filepath {
211
+ font-size: 12px;
212
+ color: oklch(98.463% 0.00181 249.248 / 0.6);
213
+ font-style: italic;
184
214
  }
185
215
 
186
216
  .preview {
@@ -1,12 +1,32 @@
1
- let address_url = document.querySelector(".address_url");
2
- async function copy() {
1
+ function copyAddressUrl() {
2
+ var e = document.getElementById("request_address_url");
3
3
  try {
4
- await navigator.clipboard.writeText(address_url.textContent);
4
+ navigator.clipboard.writeText(e.textContent);
5
5
  } catch (e) {
6
6
  console.info(e);
7
7
  }
8
8
  }
9
- function open_settings() {
9
+ function copyJsonCode() {
10
+ var e = document.getElementById("source_code");
11
+ try {
12
+ navigator.clipboard.writeText(e.textContent);
13
+ } catch (e) {
14
+ console.info(e);
15
+ }
16
+ }
17
+ function downloadJsonCode(e) {
18
+ var t = document.getElementById("source_code").textContent.trim(),
19
+ t = new Blob([t], { type: "application/json" });
20
+ let n = URL.createObjectURL(t);
21
+ var t = e.getAttribute("data-source-name"),
22
+ d = new Date().toISOString().replace(/[:.]/g, "-");
23
+ (e.download = t + `-${d}.json`),
24
+ (e.href = n),
25
+ setTimeout(() => {
26
+ URL.revokeObjectURL(n), (e.download = ""), (e.href = "");
27
+ });
28
+ }
29
+ function openSettings() {
10
30
  document.getElementById("setting_overlay").setAttribute("data-visible", "true");
11
31
  }
12
32
  function handleCloseSetting(e) {
@@ -19,29 +39,29 @@ function submitForm(e) {
19
39
  e = new FormData(e.target),
20
40
  n = e.get("count")?.trim(),
21
41
  e = e.get("unique_id_key")?.trim(),
22
- i = new URLSearchParams(),
23
- d = document.getElementById("unique_id"),
24
- o = document.getElementById("unique_id_strategy"),
42
+ d = new URLSearchParams(),
43
+ o = document.getElementById("unique_id"),
44
+ i = document.getElementById("unique_id_strategy"),
25
45
  n =
26
- (n && 0 < Number(n) ? (Number(n), i.set("count", n.toString()), (window.location.search = i.toString())) : (i.delete("count"), t.add("count")),
27
- d.checked || (i.delete("uid"), t.add("uid")),
28
- e && d.checked ? (i.set("uid", e), o.checked && i.set("s", "uuid"), (window.location.search = i.toString())) : (i.delete("uid"), i.delete("s"), t.add("uid")),
46
+ (n && 0 < Number(n) ? (Number(n), d.set("count", n.toString()), (window.location.search = d.toString())) : (d.delete("count"), t.add("count")),
47
+ o.checked || (d.delete("uid"), t.add("uid")),
48
+ e && o.checked ? (d.set("uid", e), i.checked && d.set("strategy", "uuid"), (window.location.search = d.toString())) : (d.delete("uid"), d.delete("strategy"), t.add("uid")),
29
49
  t.has("uid")),
30
- d = t.has("count");
31
- d && n
50
+ o = t.has("count");
51
+ o && n
32
52
  ? window.location.href.includes("?") && (([href] = window.location.href.split("?")), (window.location.href = href))
33
- : ((d && !n) || (!d && n)) && (window.location.search = i.toString());
53
+ : ((o && !n) || (!o && n)) && (window.location.search = d.toString());
34
54
  }
35
55
  function resetForm(e) {
36
56
  e.preventDefault();
37
57
  e = new URLSearchParams(window.location.search);
38
- e.delete("count"), e.delete("uid"), e.delete("s"), window.location.href.includes("?") && (window.location.href = window.location.href.split("?")[0]);
58
+ e.delete("count"), e.delete("uid"), e.delete("strategy"), window.location.href.includes("?") && (window.location.href = window.location.href.split("?")[0]);
39
59
  }
40
60
  function onChangeGenerateUniqueId(e) {
41
61
  var t = document.getElementById("unique_id"),
42
62
  n = document.getElementById("unique_id_key_field"),
43
- i = document.getElementById("unique_strategy_container");
44
- (t.checked = !t.checked), n.setAttribute("data-visible", t.checked.toString()), i.setAttribute("data-visible", t.checked.toString());
63
+ d = document.getElementById("unique_strategy_container");
64
+ (t.checked = !t.checked), n.setAttribute("data-visible", t.checked.toString()), d.setAttribute("data-visible", t.checked.toString());
45
65
  }
46
66
  function onHandleUniqueIdStrategy(e) {
47
67
  var t = document.getElementById("unique_id_strategy");
@@ -52,11 +72,11 @@ window.addEventListener("click", handleCloseSetting),
52
72
  var e = document.getElementById("count"),
53
73
  t = document.getElementById("unique_id"),
54
74
  n = document.getElementById("unique_id_strategy"),
55
- i = document.getElementById("unique_id_key"),
56
- d = document.getElementById("unique_id_key_field"),
57
- o = document.getElementById("unique_strategy_container"),
75
+ d = document.getElementById("unique_id_key"),
76
+ o = document.getElementById("unique_id_key_field"),
77
+ i = document.getElementById("unique_strategy_container"),
58
78
  a = new URLSearchParams(window.location.search),
59
- u = a.get("count"),
60
- c = a.get("uid");
61
- u && (e.value = u), c && ((t.checked = !0), (i.value = c), d.setAttribute("data-visible", "true"), o.setAttribute("data-visible", "true"), a.has("s")) && (n.checked = !0);
79
+ c = a.get("count"),
80
+ r = a.get("uid");
81
+ c && (e.value = c), r && ((t.checked = !0), (d.value = r), o.setAttribute("data-visible", "true"), i.setAttribute("data-visible", "true"), a.has("s")) && (n.checked = !0);
62
82
  });
@@ -1 +1 @@
1
- <pre class="code loading"><code class="language-<%= lang %>"><%- code %></code></pre>
1
+ <pre class="code loading"><code id="source_code" class="language-<%= lang %>"><%- code %></code></pre>
@@ -6,9 +6,10 @@
6
6
  <span class="sidebar_content_title">Found interfaces</span>
7
7
  </div>
8
8
  <div class="sidebar_content_list">
9
- <% entities.forEach((_,name) => { %>
9
+ <% entities.forEach((entity,name) => { %>
10
10
  <a class="sidebar_content_list_item <%= currentPath === `/${name}` ? 'active' : '' %>" href="/<%= name %>">
11
- <span><%= name %></span>
11
+ <span class="item_name"><%= name %></span>
12
+ <span class="item_filepath"><%= entity.filepath %></span>
12
13
  </a>
13
14
  <% }) %>
14
15
  </div>
@@ -0,0 +1,60 @@
1
+ <div class="preview_toolbar loading">
2
+ <div class="toolbar_setting">
3
+ <div class="data_fetch_address">
4
+ <div class="address">
5
+ <span class="address_method">GET</span>
6
+ <span id="request_address_url" class="address_url"><%= address %><%= prefix %>/<%= name %><%= search %></span>
7
+ </div>
8
+ <img id="copy_btn" onclick="copyAddressUrl()" class="icon pointer_auto" src="/icons/copy.svg" width="16px" height="16px" />
9
+ </div>
10
+ <div id="overlay_trigger" class="toolbar_setting_option" onclick="openSettings()">
11
+ <img class="icon" src="/icons/settings.svg" width="18px" height="18px" />
12
+ </div>
13
+ <div id="setting_overlay" data-visible="false" class="toolbar_setting_overlay">
14
+ <span class="setting_title">Generator settings</span>
15
+ <form class="setting_form" onsubmit="submitForm(event)">
16
+ <div class="form_input">
17
+ <label for="count">Count</label>
18
+ <input id="count" autocomplete="off" name="count" type="text" />
19
+ <span>Set count field to generate array of data items.</span>
20
+ </div>
21
+ <div style="display: flex; flex-direction: column; gap: 8px">
22
+ <div class="form_input inline">
23
+ <label for="unique_id">Generate unique id</label>
24
+ <div class="switch" onclick="onChangeGenerateUniqueId()">
25
+ <input type="checkbox" id="unique_id" name="unique_id" />
26
+ <span class="slider"></span>
27
+ </div>
28
+ </div>
29
+ <div id="unique_id_key_field" class="form_input" data-visible="false">
30
+ <input id="unique_id_key" autocomplete="off" name="unique_id_key" type="text" value="_id" placeholder="unique id key" />
31
+ </div>
32
+ <div id="unique_strategy_container" class="unique_strategy" data-visible="false">
33
+ <span class="unique_id_hint">By default unique id is auto incremented integer.</span>
34
+ <div class="form_input inline">
35
+ <label for="unique_id_strategy">Generate as uuid</label>
36
+ <div class="switch" onclick="onHandleUniqueIdStrategy()">
37
+ <input type="checkbox" id="unique_id_strategy" name="unique_id_strategy" />
38
+ <span class="slider"></span>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ <div class="form_buttons">
44
+ <button class="form_reset_btn" type="button" onclick="resetForm(event)">Reset</button>
45
+ <button class="form_apply_btn" type="submit">Apply</button>
46
+ </div>
47
+ </form>
48
+ </div>
49
+ </div>
50
+
51
+ <div class="toolbar_actions">
52
+ <div class="toolbar_action_item" onclick="copyJsonCode()">
53
+ <img class="icon" src="/icons/copy.svg" width="18px" height="18px" />
54
+ </div>
55
+ <div class="divider"></div>
56
+ <a class="toolbar_action_item" data-source-name="<%= name %>" onclick="downloadJsonCode(this)">
57
+ <img class="icon" src="/icons/download.svg" width="18px" height="18px" />
58
+ </a>
59
+ </div>
60
+ </div>
@@ -7,195 +7,7 @@
7
7
  <div class="preview-loading loading">
8
8
  <span class="loader visible"></span><span class="error">Some libraries could not be loaded. check your internet connection and try again.</span>
9
9
  </div>
10
- <div class="preview_toolbar loading">
11
- <div class="toolbar_setting">
12
- <div class="data_fetch_address">
13
- <div class="address">
14
- <span class="address_method">GET</span>
15
- <span class="address_url"><%= address %><%= prefix %>/<%= name %><%= search %></span>
16
- </div>
17
- <img id="copy_btn" onclick="copy()" class="icon pointer_auto" src="/icons/copy.svg" width="16px" height="16px" />
18
- </div>
19
- <div id="overlay_trigger" class="toolbar_setting_option" onclick="open_settings()">
20
- <img class="icon" src="/icons/settings.svg" width="18px" height="18px" />
21
- </div>
22
- <div id="setting_overlay" data-visible="false" class="toolbar_setting_overlay">
23
- <span class="setting_title">Generator settings</span>
24
- <form class="setting_form" onsubmit="submitForm(event)">
25
- <div class="form_input">
26
- <label for="count">Count</label>
27
- <input id="count" autocomplete="off" name="count" type="text" />
28
- <span>Set count field to generate array of data items.</span>
29
- </div>
30
- <div style="display: flex; flex-direction: column; gap: 8px">
31
- <div class="form_input inline">
32
- <label for="unique_id">Generate unique id</label>
33
- <div class="switch" onclick="onChangeGenerateUniqueId()">
34
- <input type="checkbox" id="unique_id" name="unique_id" />
35
- <span class="slider"></span>
36
- </div>
37
- </div>
38
- <div id="unique_id_key_field" class="form_input" data-visible="false">
39
- <input id="unique_id_key" autocomplete="off" name="unique_id_key" type="text" value="_id" placeholder="unique id key" />
40
- </div>
41
- <div id="unique_strategy_container" class="unique_strategy" data-visible="false">
42
- <span class="unique_id_hint">By default unique id is auto incremented integer.</span>
43
- <div class="form_input inline">
44
- <label for="unique_id_strategy">Generate as uuid</label>
45
- <div class="switch" onclick="onHandleUniqueIdStrategy()">
46
- <input type="checkbox" id="unique_id_strategy" name="unique_id_strategy" />
47
- <span class="slider"></span>
48
- </div>
49
- </div>
50
- </div>
51
- </div>
52
- <div class="form_buttons">
53
- <button class="form_reset_btn" type="button" onclick="resetForm(event)">Reset</button>
54
- <button class="form_apply_btn" type="submit">Apply</button>
55
- </div>
56
- </form>
57
- </div>
58
- </div>
59
-
60
- <div class="toolbar_actions">
61
- <div class="toolbar_action_item">
62
- <img class="icon" src="/icons/copy.svg" width="18px" height="18px" />
63
- </div>
64
- <div class="divider"></div>
65
- <div class="toolbar_action_item">
66
- <img class="icon" src="/icons/download.svg" width="18px" height="18px" />
67
- </div>
68
- </div>
69
- </div>
70
- <%- include("components/code", { lang: "json", code: json }) %>
10
+ <%- include("components/toolbar", { address, prefix, name, search }) %> <%- include("components/code", { lang: "json", code: json }) %>
71
11
  </div>
72
12
  </main>
73
13
  <script src="/js/fakelab.min.js"></script>
74
- <!-- <script>
75
- const address_url = document.querySelector(".address_url");
76
-
77
- async function copy() {
78
- try {
79
- await navigator.clipboard.writeText(address_url.textContent);
80
- } catch (err) {
81
- console.info(err);
82
- }
83
- }
84
-
85
- function open_settings() {
86
- const element = document.getElementById("setting_overlay");
87
- element.setAttribute("data-visible", "true");
88
- }
89
-
90
- function handleCloseSetting(event) {
91
- const element = document.getElementById("setting_overlay");
92
-
93
- if (!event.target) return;
94
- if (!element.contains(event.target) && event.target.id !== "overlay_trigger") {
95
- element.setAttribute("data-visible", "false");
96
- }
97
- }
98
-
99
- window.addEventListener("click", handleCloseSetting);
100
-
101
- window.addEventListener("DOMContentLoaded", () => {
102
- const countEl = document.getElementById("count");
103
- const uniqueIdEl = document.getElementById("unique_id");
104
- const strategyEl = document.getElementById("unique_id_strategy");
105
- const uniqueIdKeyEl = document.getElementById("unique_id_key");
106
- const keyEl = document.getElementById("unique_id_key_field");
107
- const strategyContainerEl = document.getElementById("unique_strategy_container");
108
- const params = new URLSearchParams(window.location.search);
109
- const countValue = params.get("count");
110
- const uniqueIdValue = params.get("uid");
111
- if (countValue) countEl.value = countValue;
112
- if (uniqueIdValue) {
113
- uniqueIdEl.checked = true;
114
- uniqueIdKeyEl.value = uniqueIdValue;
115
- keyEl.setAttribute("data-visible", "true");
116
- strategyContainerEl.setAttribute("data-visible", "true");
117
- if (params.has("s")) {
118
- strategyEl.checked = true;
119
- }
120
- }
121
- });
122
-
123
- function submitForm(event) {
124
- event.preventDefault();
125
- const resetReasons = new Set();
126
- const formData = new FormData(event.target);
127
- const countValue = formData.get("count")?.trim();
128
- const uniqueIdValue = formData.get("unique_id_key")?.trim();
129
- const params = new URLSearchParams();
130
-
131
- const switcher = document.getElementById("unique_id");
132
- const strategySwitcher = document.getElementById("unique_id_strategy");
133
-
134
- if (countValue && Number(countValue) > 0) {
135
- const valueAsNumber = Number(countValue);
136
-
137
- params.set("count", countValue.toString());
138
- window.location.search = params.toString();
139
- } else {
140
- params.delete("count");
141
- resetReasons.add("count");
142
- }
143
-
144
- if (!switcher.checked) {
145
- params.delete("uid");
146
- resetReasons.add("uid");
147
- }
148
-
149
- if (uniqueIdValue && switcher.checked) {
150
- params.set("uid", uniqueIdValue);
151
-
152
- if (strategySwitcher.checked) {
153
- params.set("s", "uuid");
154
- }
155
-
156
- window.location.search = params.toString();
157
- } else {
158
- params.delete("uid");
159
- params.delete("s");
160
- resetReasons.add("uid");
161
- }
162
-
163
- const hasUidReason = resetReasons.has("uid");
164
- const hasCountReason = resetReasons.has("count");
165
-
166
- if (hasCountReason && hasUidReason) {
167
- if (window.location.href.includes("?")) {
168
- [href] = window.location.href.split("?");
169
-
170
- window.location.href = href;
171
- }
172
- } else if ((hasCountReason && !hasUidReason) || (!hasCountReason && hasUidReason)) {
173
- window.location.search = params.toString();
174
- }
175
- }
176
-
177
- function resetForm(event) {
178
- event.preventDefault();
179
- const params = new URLSearchParams(window.location.search);
180
- params.delete("count");
181
- params.delete("uid");
182
- params.delete("s");
183
- if (window.location.href.includes("?")) {
184
- window.location.href = window.location.href.split("?")[0];
185
- }
186
- }
187
-
188
- function onChangeGenerateUniqueId(element) {
189
- const switcher = document.getElementById("unique_id");
190
- const key = document.getElementById("unique_id_key_field");
191
- const strategy = document.getElementById("unique_strategy_container");
192
- switcher.checked = !switcher.checked;
193
- key.setAttribute("data-visible", switcher.checked.toString());
194
- strategy.setAttribute("data-visible", switcher.checked.toString());
195
- }
196
-
197
- function onHandleUniqueIdStrategy(element) {
198
- const strategy = document.getElementById("unique_id_strategy");
199
- strategy.checked = !strategy.checked;
200
- }
201
- </script> -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fakelab",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "A easy-config mock server for frontend developers.",