fakelab 0.0.16 → 0.0.17
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 +5 -7
- package/lib/cli.js +6 -4
- package/lib/main.d.ts +21 -1
- package/lib/main.js +100 -16
- package/lib/public/css/style.css +79 -0
- package/lib/public/icons/arrow-left.svg +1 -0
- package/lib/public/icons/database.svg +1 -0
- package/lib/public/icons/eraser.svg +1 -0
- package/lib/public/icons/insert.svg +1 -0
- package/lib/public/icons/trash.svg +1 -0
- package/lib/public/js/fakelab.min.js +2 -1
- package/lib/views/components/database/sidebar.ejs +30 -0
- package/lib/views/components/database/toolbar.ejs +40 -0
- package/lib/views/components/sidebar.ejs +11 -3
- package/lib/views/database.ejs +9 -0
- package/lib/views/index.ejs +1 -1
- package/lib/views/preview.ejs +1 -1
- package/lib/views/table.ejs +8 -0
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ create `fakelab.config.ts` file in the project root. and reference your typescri
|
|
|
27
27
|
import { defineConfig } from "fakelab";
|
|
28
28
|
|
|
29
29
|
export default defineConfig({
|
|
30
|
-
sourcePath: "./types", //
|
|
30
|
+
sourcePath: ["./types", "./fixtures/**/*.ts"], // supports glob pattern
|
|
31
31
|
faker: { locale: "en" }, // optional
|
|
32
32
|
server: { pathPrefix: "api/v1", port: 8080 }, // optional
|
|
33
33
|
});
|
|
@@ -85,11 +85,7 @@ export interface User {
|
|
|
85
85
|
|
|
86
86
|
## Fakelab Runtime
|
|
87
87
|
|
|
88
|
-
`fakelab/runtime` enables a **global
|
|
89
|
-
|
|
90
|
-
## Enabling the runtime (important)
|
|
91
|
-
|
|
92
|
-
To enable the global `fakelab` object, you **must import the runtime once** in your application.
|
|
88
|
+
`fakelab/runtime` enables a **global fakelab object** at runtime, allowing your frontend or Node environment to communicate with the running Fakelab mock server.
|
|
93
89
|
|
|
94
90
|
## `fakelab.URL`
|
|
95
91
|
|
|
@@ -107,7 +103,7 @@ Fetch mock data from the Fakelab server by **typescript interface/type name**.
|
|
|
107
103
|
### Signature
|
|
108
104
|
|
|
109
105
|
```ts
|
|
110
|
-
fakelab.fetch(name: string, count?: number): Promise<T
|
|
106
|
+
fakelab.fetch(name: string, count?: number): Promise<T>
|
|
111
107
|
```
|
|
112
108
|
|
|
113
109
|
### Parameters
|
|
@@ -127,6 +123,8 @@ const users = await fakelab.fetch("User", 10);
|
|
|
127
123
|
console.log(users);
|
|
128
124
|
```
|
|
129
125
|
|
|
126
|
+
**NOTE:** Set count to a negative number to get an empty array.
|
|
127
|
+
|
|
130
128
|
## Server Command
|
|
131
129
|
|
|
132
130
|
Run:
|
package/lib/cli.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
interface
|
|
1
|
+
import h from'path';import O from'fs-extra';import {Command}from'commander';import C from'express';import z from'cors';import K from'express-ejs-layouts';import V from'http';import'ejs';import {Project}from'ts-morph';import m from'winston';import b from'picocolors';import {JSONFilePreset}from'lowdb/node';import {fileURLToPath}from'url';import G from'qs';import W from'figlet';import {bundleRequire}from'bundle-require';import re from'joycon';function S(s){switch(s){case "info":return b.blueBright(s.toUpperCase());case "error":return b.redBright(s.toUpperCase());case "warn":return b.yellowBright(s.toUpperCase());default:return s.toUpperCase()}}var D=m.format.printf(({level:s,message:e,timestamp:t})=>`${b.dim(`[${t}]`)} ${S(s)} ${e}`),d=m.createLogger({format:m.format.combine(m.format.timestamp(),m.format.splat(),D),transports:[new m.transports.Console]}),j=new Intl.ListFormat("en",{style:"long",type:"unit"}),p=class{static info(e,...t){return d.info(e,...t)}static warn(e,...t){return d.warn(e,...t)}static error(e,...t){return d.error(e,...t)}static list(e){return j.format(e)}static close(){d.removeAllListeners(),d.close();}};var w=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 n=>{let o=n.getTypeAtLocation(n.getValueDeclarationOrThrow());r[n.getName()]=await t(o,this,this.readJSDocTags(n));})),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[n]=r.getText();if(!n)return;let o=n.text.trim().match(this.FAKER_TAG_REGEX);if(!o)return;let[,a,c]=o,i=this.evalArgs(c);return {path:a,args:i}})}execute(e,t){if(!e)return t();let r=e.path.split("."),n=this.faker;for(let o of r)n=n[o],n||(p.error("Invalid faker module path:",e.path),process.exit(1));typeof n!="function"&&(p.error("Unresolvable faker function.",e.path),process.exit(1));try{return e.args?n(e.args):n()}catch{return p.error("Passed invalid arguments to faker function."),n()}}};var u=h.dirname(fileURLToPath(import.meta.url)),F=process.cwd();var x=class{constructor(e,t){this.files=e;this.config=t;let n=new Project({tsConfigFilePath:"tsconfig.json"}).addSourceFilesAtPaths(e);this.__targets=n.flatMap(o=>{let a=o.getInterfaces(),c=o.getTypeAliases(),i=o.getExportDeclarations().flatMap(l=>l.getNamedExports().flatMap(f=>f.getLocalTargetDeclarations()));return [...a,...c,...i]}),this.generateInFileEntitiyMap(this.__targets);}__targets;async run(e){return await e()}normalizePath(e){return e.split(h.sep).join(h.posix.sep)}generateInFileEntitiyMap(e){let t=[...new Set(e.map(o=>{let a=o.getName(),c=o.getSourceFile().getFilePath();return `${a}: import("${c}").${a}`}))],{expose:r}=this.config.browserOpts(),n=`
|
|
2
|
+
interface Runtime$ {
|
|
3
|
+
${t.join(`
|
|
3
4
|
`)}
|
|
4
|
-
}
|
|
5
|
-
|
|
5
|
+
}`;r.mode==="global"&&(n=`
|
|
6
|
+
declare global {${n}
|
|
7
|
+
}`),O.appendFile(h.resolve(u,this.config.RUNTIME_DECL_FILENAME),n);}address(e,t){let r=this.normalizePath(F);return `${e.replace(r,"")}/${t}`}async entities(){let e=await Promise.all(this.__targets.map(async t=>{let r=t.getName().toLowerCase(),n=t.getType(),o=this.normalizePath(t.getSourceFile().getDirectoryPath()),a=t.getSourceFile().getBaseName(),c=this.address(o,a),i=this.config.getDatabaseDirectoryPath(),l=h.resolve(i,`${r}.json`),f=this.address(this.normalizePath(i),h.basename(l)),y=await JSONFilePreset(l,[]);return [r,{type:n,filepath:c,table:y,tablepath:f}]}));return new Map(e)}async initFakerLibrary(e){let{faker:t}=await import(`@faker-js/faker/locale/${e.locale}`);return t}};async function g(s,e,t=[],r=0){if(s.isString())return e.string(t[r]);if(s.isNumber())return e.int(t[r]);if(s.isBoolean())return e.bool(t[r]);if(s.isBigInt())return e.bigInt(t[r]);if(s.isBooleanLiteral())return e.litbool(s.getText());if(s.isLiteral())return s.getLiteralValue();if(!s.isUndefined()){if(s.isUnion()){let n=s.getUnionTypes();return await e.union(n.map((o,a)=>g(o,e,t,a)))}if(s.isIntersection()){let n=s.getIntersectionTypes();return await e.intersection(n.map((o,a)=>g(o,e,t,a)))}if(s.isArray()){let n=s.getArrayElementTypeOrThrow();return [await g(n,e,t,r)]}if(s.isObject()){let n=s.getProperties();return await e.object(n,(o,a,c)=>g(o,a,c,r))}return null}}function J({each:s}){return {resolve:async t=>await Promise.all(Array.from({length:t},s))}}async function R(s,e){let t=await s.files(e.source),r=new x(t,s),n=await r.entities(),o=await r.initFakerLibrary(s.fakerOpts(e.locale)),a=new w(o);async function c(i,l){let f=J({each:()=>g(i,a)}),y=await(l.count?f.resolve(parseInt(l.count)):g(i,a)),I=JSON.stringify(y,null,2);return {data:y,json:I}}return {entities:n,forge:c}}var v=class{constructor(e,t,r){this.builder=e;this.config=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.config.databaseEnabled()});}}preview(e){return async(t,r)=>{let n=`${t.protocol}://${t.host}/`,o=t.params.name,a=await this.handleQueries(t),c=G.stringify(a,{addQueryPrefix:true}),i=this.builder.entities.get(o.toLowerCase());if(i){let{json:l}=await this.builder.forge(i.type,a),f=i.filepath;r.render("preview",{name:o,filepath:f,address:n,search:c,json:l,prefix:e,entities:this.builder.entities,version:this.pkg.version,enabled:this.config.databaseEnabled()});}else r.redirect("/");}}database(){return (e,t)=>{this.config.databaseEnabled()?t.render("database",{name:null,entities:this.builder.entities,version:this.pkg.version}):t.redirect("/");}}table(e){return async(t,r)=>{let n=`${t.protocol}://${t.host}/`,o=t.params.name,a=this.builder.entities.get(o.toLowerCase());if(!this.config.databaseEnabled())r.redirect("/");else if(a){await a.table.read();let i=a.table.data.length>0,l=JSON.stringify(a.table.data,null,2),f=a.filepath;r.render("table",{name:o,filepath:f,address:n,prefix:e,json:l,hasData:i,entities:this.builder.entities,version:this.pkg.version});}else r.redirect("/database");}}};var k=class{constructor(e){this.builder=e;}async handleQueries(e){let t=e.query.count;return t?{count:t.toString()}:{}}entity(){return async(e,t)=>{try{let r=e.params.name,n=await this.handleQueries(e),o=this.builder.entities.get(r.toLowerCase());if(o){let{data:a}=await this.builder.forge(o.type,n);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)=>{try{let r=e.params.name,n=this.builder.entities.get(r.toLowerCase());n?(await n.table.read(),t.status(200).json(n.table.data)):t.status(400).json({message:"The table is not exists"});}catch(r){t.status(500).send(r);}}}updateTable(e=false){return async(t,r)=>{try{let n=t.params.name,o=await this.handleQueries(t),a=this.builder.entities.get(n.toLowerCase());if(a){let{data:c}=await this.builder.forge(a.type,o);await a.table.update(i=>i.push(c)),e?r.status(301).redirect(`/database/${n.toLowerCase()}`):r.status(200).json({success:!0});}else e?r.status(400).redirect("/database"):r.status(400).json({success:!1,message:"The table is not exists"});}catch(n){e?r.status(500).redirect("/database"):r.status(500).send(n);}}}clearTable(){return async(e,t)=>{try{let r=e.params.name,n=this.builder.entities.get(r.toLowerCase());n?(await n.table.read(),n.table.data.length>0&&await n.table.update(o=>o.length=0),t.status(301).redirect(`/database/${r.toLowerCase()}`)):t.status(400).redirect("/database");}catch{t.status(500).redirect("/database");}}}};var B=O.readJSONSync(h.join(u,"../package.json")),T=class{constructor(e,t,r){this.router=e;this.config=t;this.opts=r;let{pathPrefix:n}=this.config.serverOpts(this.opts.pathPrefix,this.opts.port);this.prefix=n;}prefix;async register(){let e=await R(this.config,this.opts),t=new v(e,this.config,B),r=new k(e);this.router.get("/",t.index()),this.router.get("/entities/:name",t.preview(this.prefix)),this.router.get("/database",t.database()),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("/__update/:name",r.updateTable(true)),this.router.post("/__delete/:name",r.clearTable());}};function X(s,e){s.databaseEnabled()&&p.info(`database: ${s.getDatabaseDirectoryPath()}`),p.info(`server: http://localhost:${e}`),console.log(W.textSync("FAKELAB"));}function H(s,e,t){let{port:r}=e.serverOpts(t.pathPrefix,t.port);s.listen(r,"localhost",()=>X(e,r)),s.on("close",()=>{p.close();});}function Z(s,e,t){e.setHeader("x-powered-by","fakelab"),t();}function Y(s){s.disable("x-powered-by"),s.use(C.json()),s.use(z({methods:"GET"})),s.use(C.static(u+"/public")),s.use(Z);}function ee(s){s.set("views",h.join(u,"views")),s.set("view engine","ejs"),s.use(K),s.set("layout","layouts/main");}async function A(s,e){let t=C(),r=C.Router(),n=V.createServer(t);Y(t),ee(t),await s.generateInFileRuntimeConfig(u,e),await new T(r,s,e).register(),t.use(r),H(n,s,e);}async function L(){try{let e=await new re().resolve({files:["fakelab.config.ts"]});return e||(p.error("No fakelab config file is detected."),process.exit(1)),(await bundleRequire({filepath:e})).mod.default}catch{p.error("Could not load the config file."),process.exit(1);}}var P=new Command,E=O.readJSONSync(h.join(u,"../package.json"));P.name(E.name).description(E.description).version(E.version);P.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 s=>{let e=await L();A(e,s);});P.parse();
|
package/lib/main.d.ts
CHANGED
|
@@ -14,10 +14,23 @@ type ServerOptions = {
|
|
|
14
14
|
type FakerEngineOptions = {
|
|
15
15
|
locale?: FakerLocale;
|
|
16
16
|
};
|
|
17
|
+
type DatabaseOptions = {
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
dest?: string;
|
|
20
|
+
};
|
|
21
|
+
type BrowserExposeOptions = {
|
|
22
|
+
name: string;
|
|
23
|
+
mode: "module" | "global";
|
|
24
|
+
};
|
|
25
|
+
type BrowserOptions = {
|
|
26
|
+
expose?: Partial<BrowserExposeOptions>;
|
|
27
|
+
};
|
|
17
28
|
type ConfigOptions = {
|
|
18
29
|
sourcePath: string | string[];
|
|
19
30
|
server?: ServerOptions;
|
|
20
31
|
faker?: FakerEngineOptions;
|
|
32
|
+
database?: DatabaseOptions;
|
|
33
|
+
browser?: BrowserOptions;
|
|
21
34
|
};
|
|
22
35
|
type ServerCLIOptions = {
|
|
23
36
|
source?: string;
|
|
@@ -28,11 +41,18 @@ type ServerCLIOptions = {
|
|
|
28
41
|
|
|
29
42
|
declare class Config {
|
|
30
43
|
private readonly opts;
|
|
44
|
+
readonly RUNTIME_SOURCE_FILENAME = "runtime.js";
|
|
45
|
+
readonly RUNTIME_DECL_FILENAME = "runtime.d.ts";
|
|
31
46
|
constructor(opts: ConfigOptions);
|
|
32
47
|
files(_sourcePath?: string): Promise<string[]>;
|
|
33
48
|
serverOpts(prefix?: string, port?: number): Required<ServerOptions>;
|
|
49
|
+
browserOpts(name?: string, mode?: "module" | "global"): Required<BrowserOptions>;
|
|
34
50
|
fakerOpts(locale?: FakerLocale): Required<FakerEngineOptions>;
|
|
35
|
-
generateInFileRuntimeConfig(
|
|
51
|
+
generateInFileRuntimeConfig(dirname: string, options: ServerCLIOptions): Promise<void>;
|
|
52
|
+
getDatabaseDirectoryPath(): string;
|
|
53
|
+
databaseEnabled(): boolean;
|
|
54
|
+
private tryPrepareDatabase;
|
|
55
|
+
private modifyGitignoreFile;
|
|
36
56
|
private tryStat;
|
|
37
57
|
private isReadable;
|
|
38
58
|
private resolveSourcePath;
|
package/lib/main.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import R from'fast-glob';import i from'path';import p from'fs-extra';import {stat,access,constants}from'fs/promises';import C from'is-glob';import l from'winston';import u from'picocolors';import {transform}from'esbuild';function N(o){switch(o){case "info":return u.blueBright(o.toUpperCase());case "error":return u.redBright(o.toUpperCase());case "warn":return u.yellowBright(o.toUpperCase());default:return o.toUpperCase()}}var k=l.format.printf(({level:o,message:t,timestamp:e})=>`${u.dim(`[${e}]`)} ${N(o)} ${t}`),f=l.createLogger({format:l.format.combine(l.format.timestamp(),l.format.splat(),k),transports:[new l.transports.Console]}),D=new Intl.ListFormat("en",{style:"long",type:"unit"}),s=class{static info(t,...e){return f.info(t,...e)}static warn(t,...e){return f.warn(t,...e)}static error(t,...e){return f.error(t,...e)}static list(t){return D.format(t)}static close(){f.removeAllListeners(),f.close();}};var w="fakelab",y="module",h=["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 T(){let o=Intl.DateTimeFormat().resolvedOptions().locale;if(!o)return "en";let[t]=o.split("-"),e=t.toLowerCase();return h.includes(e)?e:"en"}var O=`let fl = {};
|
|
2
|
+
let db = {};
|
|
3
|
+
fl.url = () => "http://localhost:PORT/PREFIX/";
|
|
4
|
+
fl.fetch = async function (name, count) {
|
|
4
5
|
const search = count ? "?count=" + count : "";
|
|
5
6
|
|
|
6
|
-
const response = await fetch(
|
|
7
|
+
const response = await fetch(fl.url() + name + search);
|
|
7
8
|
|
|
8
9
|
if (!response.ok) throw new Error("[fakelab] Failed to retreived mock data.");
|
|
9
10
|
|
|
@@ -11,25 +12,108 @@ global.fakelab.fetch = async function (name, count) {
|
|
|
11
12
|
|
|
12
13
|
return result;
|
|
13
14
|
};
|
|
14
|
-
|
|
15
|
+
|
|
16
|
+
db.enabled = () => ENABLED_COND;
|
|
17
|
+
db.get = async function (name) {
|
|
18
|
+
if (!db.enabled()) throw new Error("[fakelab] Database is not enabled.");
|
|
19
|
+
|
|
20
|
+
const response = await fetch(NAME.url() + "database/" + name);
|
|
21
|
+
|
|
22
|
+
if (!response.ok) throw new Error("[fakelab] Failed to retreived data from database.");
|
|
23
|
+
|
|
24
|
+
const result = await response.json();
|
|
25
|
+
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
db.post = async function (name) {
|
|
29
|
+
if (!db.enabled()) throw new Error("[fakelab] Database is not enabled.");
|
|
30
|
+
|
|
31
|
+
const response = await fetch(NAME.url() + "database/" + name, { method: "POST" });
|
|
32
|
+
|
|
33
|
+
if (!response.ok) throw new Error("[fakelab] Failed to post data to database.");
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const NAME = Object.freeze(fl);
|
|
37
|
+
const database = Object.freeze(db);
|
|
38
|
+
|
|
39
|
+
export { NAME, database };`,A=`declare function fetch<T extends keyof Runtime$, CT extends number | undefined = undefined>(name: T, count?: CT): Promise<Result$<Runtime$[T], CT>>;
|
|
40
|
+
declare function get<T extends keyof Runtime$>(name: T): Promise<Runtime$[T]>;
|
|
41
|
+
declare function post<T extends keyof Runtime$>(name: T): Promise<void>;
|
|
42
|
+
declare function enabled(): boolean;
|
|
43
|
+
declare function url(): string;
|
|
44
|
+
declare const NAME: {
|
|
45
|
+
fetch: typeof fetch;
|
|
46
|
+
url: typeof url;
|
|
47
|
+
};
|
|
48
|
+
declare const database: {
|
|
49
|
+
get: typeof get;
|
|
50
|
+
post: typeof post;
|
|
51
|
+
enabled: typeof enabled;
|
|
52
|
+
};
|
|
53
|
+
type Result$<T, CT> = CT extends number ? (CT extends 0 ? T : T[]) : T;
|
|
54
|
+
interface Runtime$ {}
|
|
55
|
+
|
|
56
|
+
export { NAME, database };`,L=`global.NAME = {};
|
|
57
|
+
global.NAME.database = {};
|
|
58
|
+
global.NAME.url = () => "http://localhost:PORT/PREFIX/";
|
|
59
|
+
global.NAME.fetch = async function (name, count) {
|
|
60
|
+
const search = count ? "?count=" + count : "";
|
|
61
|
+
|
|
62
|
+
const response = await fetch(global.NAME.url() + name + search);
|
|
63
|
+
|
|
64
|
+
if (!response.ok) throw new Error("[fakelab] Failed to retreived mock data.");
|
|
65
|
+
|
|
66
|
+
const result = await response.json();
|
|
67
|
+
|
|
68
|
+
return result;
|
|
69
|
+
};
|
|
70
|
+
global.NAME.database.enabled = () => ENABLED_COND;
|
|
71
|
+
global.NAME.database.get = async function (name) {
|
|
72
|
+
if (!global.NAME.database.enabled()) throw new Error("[fakelab] Database is not enabled.");
|
|
73
|
+
|
|
74
|
+
const response = await fetch(global.NAME.url() + "database/" + name);
|
|
75
|
+
|
|
76
|
+
if (!response.ok) throw new Error("[fakelab] Failed to retreived data from database.");
|
|
77
|
+
|
|
78
|
+
const result = await response.json();
|
|
79
|
+
|
|
80
|
+
return result;
|
|
81
|
+
};
|
|
82
|
+
global.NAME.database.post = async function (name) {
|
|
83
|
+
if (!global.NAME.database.enabled()) throw new Error("[fakelab] Database is not enabled.");
|
|
84
|
+
|
|
85
|
+
const response = await fetch(global.NAME.url() + "database/" + name, { method: "POST" });
|
|
86
|
+
|
|
87
|
+
if (!response.ok) throw new Error("[fakelab] Failed to post data to database.");
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const NAME = Object.freeze(fl);
|
|
91
|
+
const database = Object.freeze(db);
|
|
92
|
+
|
|
93
|
+
export { NAME, database };`,M=`export {};
|
|
15
94
|
|
|
16
95
|
declare global {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
96
|
+
const database: {
|
|
97
|
+
enabled(): boolean;
|
|
98
|
+
get<T extends keyof Runtime$>(name: T): Promise<Runtime$[T]>;
|
|
99
|
+
post(name: keyof Runtime$): Promise<void>;
|
|
100
|
+
};
|
|
101
|
+
const NAME: {
|
|
102
|
+
fetch<T extends keyof Runtime$, CT extends number | undefined = undefined>(name: T, count?: CT): Promise<Result$<Runtime$[T], CT>>;
|
|
103
|
+
url(): string;
|
|
104
|
+
database: typeof database;
|
|
20
105
|
};
|
|
21
|
-
type
|
|
22
|
-
interface
|
|
106
|
+
type Result$<T, CT> = CT extends number ? (CT extends 0 ? T : T[]) : T;
|
|
107
|
+
interface Runtime$ {}
|
|
23
108
|
interface Window {
|
|
24
|
-
readonly
|
|
109
|
+
readonly NAME: typeof NAME;
|
|
25
110
|
}
|
|
26
111
|
|
|
27
112
|
namespace NodeJS {
|
|
28
113
|
interface Global {
|
|
29
|
-
|
|
114
|
+
NAME: typeof NAME;
|
|
30
115
|
}
|
|
31
116
|
}
|
|
32
|
-
}
|
|
33
|
-
`
|
|
34
|
-
%s
|
|
35
|
-
`)),process.exit(1)),r}serverOpts(e,t){return {pathPrefix:e||this.opts.server?.pathPrefix||"api",port:t||this.opts.server?.port||5200}}fakerOpts(e){let t=(e||this.opts.faker?.locale)?.toLowerCase();return t&&f.includes(t)?{locale:t}:{locale:h()}}async generateInFileRuntimeConfig(e,t){let{port:r,pathPrefix:n}=this.serverOpts(t.pathPrefix,t.port),c=l.resolve(e,"runtime.js"),u=l.resolve(e,"runtime.d.ts");await Promise.all([m.writeFile(c,i.source(r,n)),m.writeFile(u,i.decl())]);}async tryStat(e){try{return await stat(e)}catch{return null}}async isReadable(e){try{return await access(e,constants.R_OK),!0}catch{return false}}resolveSourcePath(e){return (Array.isArray(e)?e:[e]).map(r=>l.resolve(r))}async resolveTSFiles(e){let t=l.resolve(e),r=t.endsWith(".ts")?t:t+".ts";if((await this.tryStat(r))?.isFile()){if(!await this.isReadable(r))throw new Error(`Cannot read file: ${r}`);return s.info("Source:",r),[r]}if((await this.tryStat(t))?.isDirectory())return s.info("Source:",t),k("**/*.ts",{cwd:t,absolute:true,ignore:["**/*.d.ts"]});s.error(`Invalid source path: ${e}`),process.exit(1);}};function b(o){return new p(o)}export{b as defineConfig};
|
|
117
|
+
}`;var g=class{constructor(t,e,r,a){this.port=t;this.prefix=e;this.browserOptions=r;this.dbOptions=a;this.name=this.browserOptions?.expose?.name||"fakelab",this.databaseEnabled=this.dbOptions?.enabled??true?"true":"false";}name;databaseEnabled;transformOptions={minify:true,platform:"browser",target:"es2022"};replacer(t,e){let r=t;for(let a in e)r=r.replace(new RegExp(a,"g"),e[a].toString().trim());return r}async prepareGlobalSource(){let t=this.replacer(L,{NAME:this.name,PORT:this.port,PREFIX:this.prefix,ENABLED_COND:this.databaseEnabled});try{let{code:e}=await transform(t,this.transformOptions);return e}catch(e){return e instanceof Error&&s.warn(e.message),t}}globalDeclaration(){return this.replacer(M,{NAME:this.name})}},b=class o extends g{constructor(e,r,a,c){super(e,r,a,c);this.port=e;this.prefix=r;this.browserOptions=a;this.dbOptions=c;}static init(e,r,a,c){let E=new o(e,r,a,c);return new Proxy(E,{get(n,d){return d==="prepareSource"?a?.expose?.mode==="global"?n.prepareGlobalSource:n.prepareSource:d==="declaration"?a?.expose?.mode==="global"?n.globalDeclaration:n.declaration:n[d]}})}async prepareSource(){let e=this.replacer(O,{NAME:this.name,PORT:this.port,PREFIX:this.prefix,ENABLED_COND:this.databaseEnabled});try{let{code:r}=await transform(e,this.transformOptions);return r}catch(r){return r instanceof Error&&s.warn(r.message),e}}declaration(){return this.replacer(A,{NAME:this.name})}};var m=class{constructor(t){this.opts=t;this.files=this.files.bind(this),this.serverOpts=this.serverOpts.bind(this),this.fakerOpts=this.fakerOpts.bind(this),this.browserOpts=this.browserOpts.bind(this);}RUNTIME_SOURCE_FILENAME="runtime.js";RUNTIME_DECL_FILENAME="runtime.d.ts";async files(t){let e=this.resolveSourcePath(t||this.opts.sourcePath),r=Array.from(new Set((await Promise.all(e.map(a=>this.resolveTSFiles(a)))).flat()));return r.length===0&&(s.error("No Typescript files found in: %s",s.list(e.map(a=>i.basename(a)))),process.exit(1)),r}serverOpts(t,e){return {pathPrefix:t||this.opts.server?.pathPrefix||"api",port:e||this.opts.server?.port||5200}}browserOpts(t,e){return {expose:{mode:e||this.opts.browser?.expose?.mode||y,name:t||this.opts.browser?.expose?.name||w}}}fakerOpts(t){let e=(t||this.opts.faker?.locale)?.toLowerCase();return e&&h.includes(e)?{locale:e}:{locale:T()}}async generateInFileRuntimeConfig(t,e){let{port:r,pathPrefix:a}=this.serverOpts(e.pathPrefix,e.port);await this.tryPrepareDatabase();let c=i.resolve(t,this.RUNTIME_SOURCE_FILENAME),E=i.resolve(t,this.RUNTIME_DECL_FILENAME),n=b.init(r,a,this.opts.browser,this.opts.database),d=await n.prepareSource();await Promise.all([p.writeFile(c,d),p.writeFile(E,n.declaration())]);}getDatabaseDirectoryPath(){let t=this.opts.database?.dest||"db";return i.resolve(process.cwd(),t)}databaseEnabled(){return this.opts.database?.enabled??false}async tryPrepareDatabase(){if(this.databaseEnabled())try{let t=this.opts.database?.dest||"db";await p.ensureDir(this.getDatabaseDirectoryPath()),await this.modifyGitignoreFile(t);}catch{s.error("Could not create database.");}else (!this.opts.database||!this.databaseEnabled())&&await p.rm(this.getDatabaseDirectoryPath(),{force:true,recursive:true});}async modifyGitignoreFile(t){try{let e=i.resolve(process.cwd(),".gitignore");if((await p.readFile(e,{encoding:"utf8"})).split(`
|
|
118
|
+
`).some(a=>a.trim()===t.trim()))return;await p.appendFile(e,`
|
|
119
|
+
${t}`);}catch{s.error("Could not modify .gitignore file.");}}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=>C(r,{strict:true})?r:i.resolve(r))}async resolveTSFiles(t){if(C(t,{strict:true}))return s.info("source: %s",t),R(t,{absolute:true,ignore:["**/*.d.ts"]});let e=i.resolve(t),r=e.endsWith(".ts")?e:e+".ts";return (await this.tryStat(r))?.isFile()?(await this.isReadable(r)||(s.error("Cannot read file: %s",r),process.exit(1)),s.info("source: %s",r),[r]):(await this.tryStat(e))?.isDirectory()?(s.info("source: %s",e),R("**/*.ts",{cwd:e,absolute:true,ignore:["**/*.d.ts"]})):(s.warn("invalid source: [REDACTED]/%s",i.basename(r)),[])}};function U(o){return new m(o)}export{U as defineConfig};
|
package/lib/public/css/style.css
CHANGED
|
@@ -88,6 +88,31 @@ pre {
|
|
|
88
88
|
padding-block: 16px;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
.sidebar_content .database_section {
|
|
92
|
+
display: flex;
|
|
93
|
+
flex: 1;
|
|
94
|
+
max-height: fit-content;
|
|
95
|
+
padding-inline: 16px;
|
|
96
|
+
padding-bottom: 16px;
|
|
97
|
+
}
|
|
98
|
+
.database_section .database_btn {
|
|
99
|
+
display: flex;
|
|
100
|
+
gap: 8px;
|
|
101
|
+
align-items: center;
|
|
102
|
+
flex: 1;
|
|
103
|
+
padding: 12px;
|
|
104
|
+
background: linear-gradient(to right bottom, oklch(60% 0.118 184.704), oklch(60.9% 0.126 221.723));
|
|
105
|
+
color: #ffffff;
|
|
106
|
+
text-decoration: none;
|
|
107
|
+
font-size: 14px;
|
|
108
|
+
font-weight: 500;
|
|
109
|
+
font-family: inter;
|
|
110
|
+
border-radius: 999px;
|
|
111
|
+
&:hover {
|
|
112
|
+
background: linear-gradient(to right bottom, oklch(70.4% 0.14 182.503), oklch(71.5% 0.143 215.221));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
91
116
|
.sidebar_content .sidebar_content_leading {
|
|
92
117
|
display: flex;
|
|
93
118
|
gap: 8px;
|
|
@@ -311,6 +336,60 @@ pre {
|
|
|
311
336
|
}
|
|
312
337
|
}
|
|
313
338
|
|
|
339
|
+
.toolbar_setting .toolbar_db_action {
|
|
340
|
+
display: flex;
|
|
341
|
+
align-items: center;
|
|
342
|
+
justify-content: center;
|
|
343
|
+
border-radius: 4px;
|
|
344
|
+
border: none;
|
|
345
|
+
gap: 6px;
|
|
346
|
+
min-width: fit-content;
|
|
347
|
+
white-space: nowrap;
|
|
348
|
+
outline: none;
|
|
349
|
+
background: oklch(27.8% 0.033 256.848);
|
|
350
|
+
box-shadow: 0px 0px 2px hsl(220, 43%, 10%);
|
|
351
|
+
height: 32px;
|
|
352
|
+
padding-inline: 12px;
|
|
353
|
+
color: #fff;
|
|
354
|
+
font-size: 12px;
|
|
355
|
+
cursor: pointer;
|
|
356
|
+
transition: background 250ms ease-in-out;
|
|
357
|
+
&:hover {
|
|
358
|
+
background: hsl(216, 31%, 19%);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.toolbar_setting .toolbar_clear_action {
|
|
363
|
+
display: flex;
|
|
364
|
+
align-items: center;
|
|
365
|
+
justify-content: center;
|
|
366
|
+
border-radius: 4px;
|
|
367
|
+
gap: 6px;
|
|
368
|
+
border: none;
|
|
369
|
+
min-width: fit-content;
|
|
370
|
+
white-space: nowrap;
|
|
371
|
+
outline: none;
|
|
372
|
+
background: oklch(27.8% 0.033 256.848);
|
|
373
|
+
box-shadow: 0px 0px 2px hsl(220, 43%, 10%);
|
|
374
|
+
height: 32px;
|
|
375
|
+
padding-inline: 12px;
|
|
376
|
+
color: #fff;
|
|
377
|
+
font-size: 12px;
|
|
378
|
+
cursor: pointer;
|
|
379
|
+
transition: background 250ms ease-in-out;
|
|
380
|
+
&:hover {
|
|
381
|
+
background: hsl(216, 31%, 19%);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.toolbar_clear_action.disabled {
|
|
386
|
+
cursor: default;
|
|
387
|
+
opacity: 0.5;
|
|
388
|
+
&:hover {
|
|
389
|
+
background: oklch(27.8% 0.033 256.848);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
314
393
|
.toolbar_setting .toolbar_setting_overlay {
|
|
315
394
|
display: none;
|
|
316
395
|
position: absolute;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-left-icon lucide-arrow-left"><path d="m12 19-7-7 7-7"/><path d="M19 12H5"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-database-icon lucide-database"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5V19A9 3 0 0 0 21 19V5"/><path d="M3 12A9 3 0 0 0 21 12"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-eraser-icon lucide-eraser"><path d="M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21"/><path d="m5.082 11.09 8.828 8.828"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-between-horizontal-start-icon lucide-between-horizontal-start"><rect width="13" height="7" x="8" y="3" rx="1"/><path d="m2 9 3 3-3 3"/><rect width="13" height="7" x="8" y="14" rx="1"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-trash-icon lucide-trash"><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
|
@@ -27,10 +27,11 @@ function downloadJsonCode(e) {
|
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
function openSettings() {
|
|
30
|
-
document.getElementById("setting_overlay")
|
|
30
|
+
document.getElementById("setting_overlay")?.setAttribute("data-visible", "true");
|
|
31
31
|
}
|
|
32
32
|
function handleCloseSetting(e) {
|
|
33
33
|
var t = document.getElementById("setting_overlay");
|
|
34
|
+
if (!t) return;
|
|
34
35
|
e.target && !t.contains(e.target) && "overlay_trigger" !== e.target.id && t.setAttribute("data-visible", "false");
|
|
35
36
|
}
|
|
36
37
|
function submitForm(e) {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<aside class="sidebar">
|
|
2
|
+
<a class="logo" href="/"><img src="/icons/logo.svg" width="20px" height="20px" /><span>Fakelab</span></a>
|
|
3
|
+
<div class="sidebar_content">
|
|
4
|
+
<div class="database_section">
|
|
5
|
+
<a href="/" class="database_btn">
|
|
6
|
+
<img src="/icons/arrow-left.svg" width="18px" height="18px" />
|
|
7
|
+
<span>Dashboard</span>
|
|
8
|
+
</a>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="sidebar_content_leading">
|
|
11
|
+
<div class="sidebar_content_indicator_icon"></div>
|
|
12
|
+
<span class="sidebar_content_title">Registered entities</span>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="sidebar_content_list">
|
|
15
|
+
<% entities.forEach((entity,entityName) => { %>
|
|
16
|
+
<a class="sidebar_content_list_item <%= entityName === name ? 'active' : '' %>" href="/database/<%= entityName %>">
|
|
17
|
+
<span class="item_name"><%= entityName %></span>
|
|
18
|
+
<span class="item_filepath"><%= entity.tablepath %></span>
|
|
19
|
+
</a>
|
|
20
|
+
<% }) %>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
<a class="sidebar_docs" href="https://fakerjs.dev/" target="_blank" rel="noopener noreferrer">
|
|
24
|
+
<span>Fakerjs documents</span>
|
|
25
|
+
<img class="icon" src="/icons/link.svg" width="18px" height="18px" />
|
|
26
|
+
</a>
|
|
27
|
+
<div class="sidebar_footer">
|
|
28
|
+
<span>app version: <%= version %></span>
|
|
29
|
+
</div>
|
|
30
|
+
</aside>
|
|
@@ -0,0 +1,40 @@
|
|
|
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 %>/database/<%= name %></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
|
+
<form action="/__update/<%= name %>" method="POST">
|
|
11
|
+
<button class="toolbar_db_action" type="submit">
|
|
12
|
+
<img src="/icons/insert.svg" width="16px" height="16px" />
|
|
13
|
+
Add a record
|
|
14
|
+
</button>
|
|
15
|
+
</form>
|
|
16
|
+
<% if (hasData) { %>
|
|
17
|
+
<form action="/__delete/<%= name %>" method="POST">
|
|
18
|
+
<button class="toolbar_clear_action" type="submit">
|
|
19
|
+
<img src="/icons/eraser.svg" width="16px" height="16px" />
|
|
20
|
+
Clear
|
|
21
|
+
</button>
|
|
22
|
+
</form>
|
|
23
|
+
<% } else { %>
|
|
24
|
+
<button class="toolbar_clear_action disabled" type="submit">
|
|
25
|
+
<img src="/icons/eraser.svg" width="16px" height="16px" />
|
|
26
|
+
Clear
|
|
27
|
+
</button>
|
|
28
|
+
<% } %>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div class="toolbar_actions">
|
|
32
|
+
<div class="toolbar_action_item" onclick="copyJsonCode()">
|
|
33
|
+
<img class="icon" src="/icons/copy.svg" width="18px" height="18px" />
|
|
34
|
+
</div>
|
|
35
|
+
<div class="divider"></div>
|
|
36
|
+
<a class="toolbar_action_item" data-source-name="<%= name %>" onclick="downloadJsonCode(this)">
|
|
37
|
+
<img class="icon" src="/icons/download.svg" width="18px" height="18px" />
|
|
38
|
+
</a>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
<aside class="sidebar">
|
|
2
2
|
<a class="logo" href="/"><img src="/icons/logo.svg" width="20px" height="20px" /><span>Fakelab</span></a>
|
|
3
3
|
<div class="sidebar_content">
|
|
4
|
+
<% if (enabled) { %>
|
|
5
|
+
<div class="database_section">
|
|
6
|
+
<a href="/database" class="database_btn">
|
|
7
|
+
<img src="/icons/database.svg" width="18px" height="18px" />
|
|
8
|
+
<span>Database</span>
|
|
9
|
+
</a>
|
|
10
|
+
</div>
|
|
11
|
+
<% } %>
|
|
4
12
|
<div class="sidebar_content_leading">
|
|
5
13
|
<div class="sidebar_content_indicator_icon"></div>
|
|
6
14
|
<span class="sidebar_content_title">Found interfaces</span>
|
|
7
15
|
</div>
|
|
8
16
|
<div class="sidebar_content_list">
|
|
9
|
-
<% entities.forEach((entity,
|
|
10
|
-
<a class="sidebar_content_list_item <%=
|
|
11
|
-
<span class="item_name"><%=
|
|
17
|
+
<% entities.forEach((entity,entityName) => { %>
|
|
18
|
+
<a class="sidebar_content_list_item <%= entityName === name ? 'active' : '' %>" href="/entities/<%= entityName %>">
|
|
19
|
+
<span class="item_name"><%= entityName %></span>
|
|
12
20
|
<span class="item_filepath"><%= entity.filepath %></span>
|
|
13
21
|
</a>
|
|
14
22
|
<% }) %>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<%- include("components/database/sidebar", { entities, version, name }) %> <% title = "Fakelab | Database" %>
|
|
2
|
+
<main class="main">
|
|
3
|
+
<header class="header"></header>
|
|
4
|
+
<div class="preview">
|
|
5
|
+
<div class="nodata">
|
|
6
|
+
<span>Select a entity to load the table.</span>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
</main>
|
package/lib/views/index.ejs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<%- include("components/sidebar", {
|
|
1
|
+
<%- include("components/sidebar", { entities, version, enabled, name }) %> <% title = "Fakelab" %>
|
|
2
2
|
<main class="main">
|
|
3
3
|
<header class="header"></header>
|
|
4
4
|
<div class="preview">
|
package/lib/views/preview.ejs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<%- include("components/sidebar", {
|
|
1
|
+
<%- include("components/sidebar", {entities, name, version, enabled }) %> <% title = name %>
|
|
2
2
|
<main class="main">
|
|
3
3
|
<header class="header">
|
|
4
4
|
<span class="interface_name"><%= name %></span>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<%- include("components/database/sidebar", { entities, name, version }) %> <% title = name %>
|
|
2
|
+
<main class="main">
|
|
3
|
+
<header class="header">
|
|
4
|
+
<span class="interface_name"><%= name %></span>
|
|
5
|
+
</header>
|
|
6
|
+
<div class="preview"><%- include("components/database/toolbar", { address, prefix, name, hasData }) %><%- include("components/code", { lang: "json", code: json }) %></div>
|
|
7
|
+
</main>
|
|
8
|
+
<script src="/js/fakelab.min.js"></script>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fakelab",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "A easy-config mock server for frontend developers.",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"types": "./lib/main.d.ts",
|
|
13
13
|
"default": "./lib/main.js"
|
|
14
14
|
},
|
|
15
|
-
"./
|
|
15
|
+
"./browser": {
|
|
16
16
|
"types": "./lib/runtime.d.ts",
|
|
17
17
|
"node": "./lib/runtime.js",
|
|
18
18
|
"default": "./lib/runtime.js"
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"@types/express": "^5.0.6",
|
|
61
61
|
"@types/express-ejs-layouts": "^2.5.4",
|
|
62
62
|
"@types/fs-extra": "^11.0.4",
|
|
63
|
+
"@types/is-glob": "^4.0.4",
|
|
63
64
|
"@types/node": "^24.10.1",
|
|
64
65
|
"release-it": "^19.0.6",
|
|
65
66
|
"tsup": "^8.5.1",
|
|
@@ -73,15 +74,19 @@
|
|
|
73
74
|
"commander": "^14.0.2",
|
|
74
75
|
"cors": "^2.8.5",
|
|
75
76
|
"ejs": "^3.1.10",
|
|
77
|
+
"esbuild": "^0.27.2",
|
|
76
78
|
"express": "^5.2.1",
|
|
77
79
|
"express-ejs-layouts": "^2.5.1",
|
|
78
80
|
"fast-glob": "^3.3.3",
|
|
79
81
|
"figlet": "^1.9.4",
|
|
80
82
|
"fs-extra": "^11.3.2",
|
|
83
|
+
"is-glob": "^4.0.3",
|
|
81
84
|
"joycon": "^3.1.1",
|
|
85
|
+
"lowdb": "^7.0.1",
|
|
82
86
|
"picocolors": "^1.1.1",
|
|
83
87
|
"qs": "^6.14.0",
|
|
84
|
-
"ts-morph": "^27.0.2"
|
|
88
|
+
"ts-morph": "^27.0.2",
|
|
89
|
+
"winston": "^3.19.0"
|
|
85
90
|
},
|
|
86
91
|
"bin": {
|
|
87
92
|
"fakelab": "bin/run.js"
|