nothing-browser 0.0.18 → 0.0.20
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/LICENSE +20 -20
- package/README.md +253 -253
- package/dist/piggy/register/index.d.ts.map +1 -1
- package/dist/piggy.js +14 -3
- package/dist/register/index.js +14 -3
- package/nothing_browser_pig_pink.svg +58 -58
- package/package.json +4 -3
- package/piggy/cache/memory.d.ts +6 -6
- package/piggy/cache/memory.ts +37 -37
- package/piggy/client/index.d.ts +78 -78
- package/piggy/client/index.ts +567 -567
- package/piggy/human/index.d.ts +6 -6
- package/piggy/human/index.ts +52 -52
- package/piggy/intercept/scripts.d.ts +12 -12
- package/piggy/intercept/scripts.ts +152 -152
- package/piggy/launch/detect.d.ts +2 -2
- package/piggy/launch/detect.ts +42 -42
- package/piggy/launch/spawn.d.ts +5 -5
- package/piggy/launch/spawn.ts +164 -164
- package/piggy/logger/index.d.ts +2 -2
- package/piggy/logger/index.ts +58 -58
- package/piggy/open/index.d.ts +3 -3
- package/piggy/open/index.ts +4 -4
- package/piggy/pool/index.d.ts +11 -11
- package/piggy/pool/index.ts +74 -74
- package/piggy/register/index.d.ts +6 -6
- package/piggy/register/index.ts +517 -506
- package/piggy/server/index.d.ts +57 -57
- package/piggy/server/index.ts +189 -189
- package/piggy/store/index.d.ts +25 -25
- package/piggy/store/index.ts +229 -229
- package/piggy.ts +216 -216
package/piggy/server/index.d.ts
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
// piggy/server/index.d.ts
|
|
2
|
-
import { Elysia } from "elysia";
|
|
3
|
-
|
|
4
|
-
export type BeforeMiddleware = (ctx: {
|
|
5
|
-
params: Record<string, string>;
|
|
6
|
-
query: Record<string, string>;
|
|
7
|
-
body: any;
|
|
8
|
-
headers: Record<string, string>;
|
|
9
|
-
set: any;
|
|
10
|
-
}) => void | Promise<void>;
|
|
11
|
-
|
|
12
|
-
export type RouteHandler = (
|
|
13
|
-
params: Record<string, string>,
|
|
14
|
-
query: Record<string, string>,
|
|
15
|
-
body: any
|
|
16
|
-
) => Promise<any>;
|
|
17
|
-
|
|
18
|
-
export interface RouteParameter {
|
|
19
|
-
name: string;
|
|
20
|
-
in: "query" | "path" | "header" | "cookie";
|
|
21
|
-
description?: string;
|
|
22
|
-
required?: boolean;
|
|
23
|
-
schema?: Record<string, any>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface RouteDetail {
|
|
27
|
-
tags?: string[];
|
|
28
|
-
summary?: string;
|
|
29
|
-
description?: string;
|
|
30
|
-
deprecated?: boolean;
|
|
31
|
-
hide?: boolean;
|
|
32
|
-
parameters?: RouteParameter[];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface RouteConfig {
|
|
36
|
-
path: string;
|
|
37
|
-
method: "GET" | "POST" | "PUT" | "DELETE";
|
|
38
|
-
handler: RouteHandler;
|
|
39
|
-
ttl: number;
|
|
40
|
-
before: BeforeMiddleware[];
|
|
41
|
-
detail?: RouteDetail;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export declare const routeRegistry: Map<string, RouteConfig>;
|
|
45
|
-
export declare const keepAliveSites: Set<string>;
|
|
46
|
-
|
|
47
|
-
export declare function startServer(
|
|
48
|
-
port: number,
|
|
49
|
-
hostname?: string,
|
|
50
|
-
openapiOpts?: {
|
|
51
|
-
title?: string;
|
|
52
|
-
version?: string;
|
|
53
|
-
description?: string;
|
|
54
|
-
path?: string;
|
|
55
|
-
}
|
|
56
|
-
): Promise<Elysia>;
|
|
57
|
-
|
|
1
|
+
// piggy/server/index.d.ts
|
|
2
|
+
import { Elysia } from "elysia";
|
|
3
|
+
|
|
4
|
+
export type BeforeMiddleware = (ctx: {
|
|
5
|
+
params: Record<string, string>;
|
|
6
|
+
query: Record<string, string>;
|
|
7
|
+
body: any;
|
|
8
|
+
headers: Record<string, string>;
|
|
9
|
+
set: any;
|
|
10
|
+
}) => void | Promise<void>;
|
|
11
|
+
|
|
12
|
+
export type RouteHandler = (
|
|
13
|
+
params: Record<string, string>,
|
|
14
|
+
query: Record<string, string>,
|
|
15
|
+
body: any
|
|
16
|
+
) => Promise<any>;
|
|
17
|
+
|
|
18
|
+
export interface RouteParameter {
|
|
19
|
+
name: string;
|
|
20
|
+
in: "query" | "path" | "header" | "cookie";
|
|
21
|
+
description?: string;
|
|
22
|
+
required?: boolean;
|
|
23
|
+
schema?: Record<string, any>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface RouteDetail {
|
|
27
|
+
tags?: string[];
|
|
28
|
+
summary?: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
deprecated?: boolean;
|
|
31
|
+
hide?: boolean;
|
|
32
|
+
parameters?: RouteParameter[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface RouteConfig {
|
|
36
|
+
path: string;
|
|
37
|
+
method: "GET" | "POST" | "PUT" | "DELETE";
|
|
38
|
+
handler: RouteHandler;
|
|
39
|
+
ttl: number;
|
|
40
|
+
before: BeforeMiddleware[];
|
|
41
|
+
detail?: RouteDetail;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export declare const routeRegistry: Map<string, RouteConfig>;
|
|
45
|
+
export declare const keepAliveSites: Set<string>;
|
|
46
|
+
|
|
47
|
+
export declare function startServer(
|
|
48
|
+
port: number,
|
|
49
|
+
hostname?: string,
|
|
50
|
+
openapiOpts?: {
|
|
51
|
+
title?: string;
|
|
52
|
+
version?: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
path?: string;
|
|
55
|
+
}
|
|
56
|
+
): Promise<Elysia>;
|
|
57
|
+
|
|
58
58
|
export declare function stopServer(): void;
|
package/piggy/server/index.ts
CHANGED
|
@@ -1,190 +1,190 @@
|
|
|
1
|
-
// piggy/server/index.ts
|
|
2
|
-
import { Elysia } from "elysia";
|
|
3
|
-
import { openapi } from "@elysiajs/openapi";
|
|
4
|
-
import * as cache from "../cache/memory";
|
|
5
|
-
import logger from "../logger";
|
|
6
|
-
|
|
7
|
-
export type BeforeMiddleware = (ctx: {
|
|
8
|
-
params: Record<string, string>;
|
|
9
|
-
query: Record<string, string>;
|
|
10
|
-
body: any;
|
|
11
|
-
headers: Record<string, string>;
|
|
12
|
-
set: any;
|
|
13
|
-
}) => void | Promise<void>;
|
|
14
|
-
|
|
15
|
-
export type RouteHandler = (
|
|
16
|
-
params: Record<string, string>,
|
|
17
|
-
query: Record<string, string>,
|
|
18
|
-
body: any
|
|
19
|
-
) => Promise<any>;
|
|
20
|
-
|
|
21
|
-
export interface RouteParameter {
|
|
22
|
-
name: string;
|
|
23
|
-
in: "query" | "path" | "header" | "cookie";
|
|
24
|
-
description?: string;
|
|
25
|
-
required?: boolean;
|
|
26
|
-
schema?: Record<string, any>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface RouteDetail {
|
|
30
|
-
tags?: string[];
|
|
31
|
-
summary?: string;
|
|
32
|
-
description?: string;
|
|
33
|
-
deprecated?: boolean;
|
|
34
|
-
hide?: boolean;
|
|
35
|
-
parameters?: RouteParameter[];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface RouteConfig {
|
|
39
|
-
path: string;
|
|
40
|
-
method: "GET" | "POST" | "PUT" | "DELETE";
|
|
41
|
-
handler: RouteHandler;
|
|
42
|
-
ttl: number;
|
|
43
|
-
before: BeforeMiddleware[];
|
|
44
|
-
detail?: RouteDetail;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export const routeRegistry = new Map<string, RouteConfig>();
|
|
48
|
-
export const keepAliveSites = new Set<string>();
|
|
49
|
-
|
|
50
|
-
// ── Error mapper ──────────────────────────────────────────────────────────────
|
|
51
|
-
|
|
52
|
-
function mapError(err: Error, site: string) {
|
|
53
|
-
const msg = err.message.toLowerCase();
|
|
54
|
-
if (msg.includes("selector") || msg.includes("not found") || msg.includes("null"))
|
|
55
|
-
return { status: 404, error: "Not found", site, details: err.message };
|
|
56
|
-
if (msg.includes("timeout") || msg.includes("timed out"))
|
|
57
|
-
return { status: 504, error: "Timeout", site, details: err.message };
|
|
58
|
-
if (msg.includes("socket") || msg.includes("closed") || msg.includes("browser"))
|
|
59
|
-
return { status: 503, error: "Browser unavailable", site, details: err.message };
|
|
60
|
-
return { status: 500, error: "Internal server error", site, details: err.message };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// ── Server factory ────────────────────────────────────────────────────────────
|
|
64
|
-
|
|
65
|
-
let _app: Elysia | null = null;
|
|
66
|
-
|
|
67
|
-
export async function startServer(
|
|
68
|
-
port: number,
|
|
69
|
-
hostname = "0.0.0.0",
|
|
70
|
-
openapiOpts?: {
|
|
71
|
-
title?: string;
|
|
72
|
-
version?: string;
|
|
73
|
-
description?: string;
|
|
74
|
-
path?: string;
|
|
75
|
-
}
|
|
76
|
-
): Promise<Elysia> {
|
|
77
|
-
_app = new Elysia();
|
|
78
|
-
|
|
79
|
-
// ── OpenAPI ───────────────────────────────────────────────────────────────
|
|
80
|
-
_app.use(
|
|
81
|
-
openapi({
|
|
82
|
-
path: openapiOpts?.path ?? "/openapi",
|
|
83
|
-
documentation: {
|
|
84
|
-
info: {
|
|
85
|
-
title: openapiOpts?.title ?? "Piggy API",
|
|
86
|
-
version: openapiOpts?.version ?? "1.0.0",
|
|
87
|
-
description: openapiOpts?.description ?? "Auto-generated docs for all registered piggy routes",
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
})
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
// ── Health ────────────────────────────────────────────────────────────────
|
|
94
|
-
_app.get("/health", () => ({
|
|
95
|
-
status: "ok",
|
|
96
|
-
routes: routeRegistry.size,
|
|
97
|
-
cacheEntries: cache.size(),
|
|
98
|
-
uptime: process.uptime(),
|
|
99
|
-
}), {
|
|
100
|
-
detail: { tags: ["_piggy"], summary: "Health check" },
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// ── Cache ─────────────────────────────────────────────────────────────────
|
|
104
|
-
_app.delete("/cache", () => {
|
|
105
|
-
cache.clear();
|
|
106
|
-
return { cleared: true };
|
|
107
|
-
}, {
|
|
108
|
-
detail: { tags: ["_piggy"], summary: "Clear all cache entries" },
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
_app.get("/cache/keys", () => ({ keys: cache.keys() }), {
|
|
112
|
-
detail: { tags: ["_piggy"], summary: "List all cache keys" },
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// ── Registered site routes ────────────────────────────────────────────────
|
|
116
|
-
for (const [registryKey, config] of routeRegistry.entries()) {
|
|
117
|
-
const colonIdx = registryKey.indexOf(":");
|
|
118
|
-
const siteName = registryKey.substring(0, colonIdx);
|
|
119
|
-
const fullPath = `/${siteName}${config.path}`;
|
|
120
|
-
const method = config.method.toLowerCase() as "get" | "post" | "put" | "delete";
|
|
121
|
-
|
|
122
|
-
logger.info(`[server] mounting ${config.method} ${fullPath} (ttl=${config.ttl}ms)`);
|
|
123
|
-
|
|
124
|
-
const routeHandler = async ({ params, query, body, headers, set }: any) => {
|
|
125
|
-
for (const mw of config.before) {
|
|
126
|
-
try {
|
|
127
|
-
await mw({ params, query, body, headers, set });
|
|
128
|
-
} catch (e: any) {
|
|
129
|
-
set.status = set.status ?? 400;
|
|
130
|
-
return { error: e.message };
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const cacheKey = `${siteName}:${fullPath}:${JSON.stringify({ params, query })}`;
|
|
135
|
-
const hit = cache.get(cacheKey);
|
|
136
|
-
if (hit !== null) {
|
|
137
|
-
set.headers["x-cache"] = "HIT";
|
|
138
|
-
set.headers["x-cache-key"] = cacheKey;
|
|
139
|
-
return hit;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
let result: any;
|
|
143
|
-
try {
|
|
144
|
-
result = await config.handler(params, query, body);
|
|
145
|
-
} catch (e: any) {
|
|
146
|
-
const mapped = mapError(e, siteName);
|
|
147
|
-
set.status = mapped.status;
|
|
148
|
-
return mapped;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (config.ttl > 0) {
|
|
152
|
-
cache.set(cacheKey, result, config.ttl);
|
|
153
|
-
set.headers["x-cache"] = "MISS";
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return result;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const routeDetail = {
|
|
160
|
-
tags: config.detail?.tags ?? [siteName],
|
|
161
|
-
summary: config.detail?.summary ?? `${config.method} ${fullPath}`,
|
|
162
|
-
description: config.detail?.description,
|
|
163
|
-
deprecated: config.detail?.deprecated ?? false,
|
|
164
|
-
hide: config.detail?.hide ?? false,
|
|
165
|
-
parameters: config.detail?.parameters ?? [],
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
if (method === "get") _app.get(fullPath, routeHandler, { detail: routeDetail });
|
|
169
|
-
else if (method === "post") _app.post(fullPath, routeHandler, { detail: routeDetail });
|
|
170
|
-
else if (method === "put") _app.put(fullPath, routeHandler, { detail: routeDetail });
|
|
171
|
-
else if (method === "delete") _app.delete(fullPath, routeHandler, { detail: routeDetail });
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
_app.listen({ port, hostname });
|
|
175
|
-
|
|
176
|
-
logger.success(`🚀 Piggy API server → http://${hostname === "0.0.0.0" ? "localhost" : hostname}:${port}`);
|
|
177
|
-
logger.success(`📖 OpenAPI docs → http://${hostname === "0.0.0.0" ? "localhost" : hostname}:${port}/openapi`);
|
|
178
|
-
logger.info(` Routes mounted: ${routeRegistry.size}`);
|
|
179
|
-
routeRegistry.forEach((cfg, key) => {
|
|
180
|
-
const siteName = key.substring(0, key.indexOf(":"));
|
|
181
|
-
logger.info(` ${cfg.method} /${siteName}${cfg.path} (ttl=${cfg.ttl}ms)`);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
return _app;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export function stopServer() {
|
|
188
|
-
_app?.stop();
|
|
189
|
-
_app = null;
|
|
1
|
+
// piggy/server/index.ts
|
|
2
|
+
import { Elysia } from "elysia";
|
|
3
|
+
import { openapi } from "@elysiajs/openapi";
|
|
4
|
+
import * as cache from "../cache/memory";
|
|
5
|
+
import logger from "../logger";
|
|
6
|
+
|
|
7
|
+
export type BeforeMiddleware = (ctx: {
|
|
8
|
+
params: Record<string, string>;
|
|
9
|
+
query: Record<string, string>;
|
|
10
|
+
body: any;
|
|
11
|
+
headers: Record<string, string>;
|
|
12
|
+
set: any;
|
|
13
|
+
}) => void | Promise<void>;
|
|
14
|
+
|
|
15
|
+
export type RouteHandler = (
|
|
16
|
+
params: Record<string, string>,
|
|
17
|
+
query: Record<string, string>,
|
|
18
|
+
body: any
|
|
19
|
+
) => Promise<any>;
|
|
20
|
+
|
|
21
|
+
export interface RouteParameter {
|
|
22
|
+
name: string;
|
|
23
|
+
in: "query" | "path" | "header" | "cookie";
|
|
24
|
+
description?: string;
|
|
25
|
+
required?: boolean;
|
|
26
|
+
schema?: Record<string, any>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface RouteDetail {
|
|
30
|
+
tags?: string[];
|
|
31
|
+
summary?: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
deprecated?: boolean;
|
|
34
|
+
hide?: boolean;
|
|
35
|
+
parameters?: RouteParameter[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface RouteConfig {
|
|
39
|
+
path: string;
|
|
40
|
+
method: "GET" | "POST" | "PUT" | "DELETE";
|
|
41
|
+
handler: RouteHandler;
|
|
42
|
+
ttl: number;
|
|
43
|
+
before: BeforeMiddleware[];
|
|
44
|
+
detail?: RouteDetail;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const routeRegistry = new Map<string, RouteConfig>();
|
|
48
|
+
export const keepAliveSites = new Set<string>();
|
|
49
|
+
|
|
50
|
+
// ── Error mapper ──────────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
function mapError(err: Error, site: string) {
|
|
53
|
+
const msg = err.message.toLowerCase();
|
|
54
|
+
if (msg.includes("selector") || msg.includes("not found") || msg.includes("null"))
|
|
55
|
+
return { status: 404, error: "Not found", site, details: err.message };
|
|
56
|
+
if (msg.includes("timeout") || msg.includes("timed out"))
|
|
57
|
+
return { status: 504, error: "Timeout", site, details: err.message };
|
|
58
|
+
if (msg.includes("socket") || msg.includes("closed") || msg.includes("browser"))
|
|
59
|
+
return { status: 503, error: "Browser unavailable", site, details: err.message };
|
|
60
|
+
return { status: 500, error: "Internal server error", site, details: err.message };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── Server factory ────────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
let _app: Elysia | null = null;
|
|
66
|
+
|
|
67
|
+
export async function startServer(
|
|
68
|
+
port: number,
|
|
69
|
+
hostname = "0.0.0.0",
|
|
70
|
+
openapiOpts?: {
|
|
71
|
+
title?: string;
|
|
72
|
+
version?: string;
|
|
73
|
+
description?: string;
|
|
74
|
+
path?: string;
|
|
75
|
+
}
|
|
76
|
+
): Promise<Elysia> {
|
|
77
|
+
_app = new Elysia();
|
|
78
|
+
|
|
79
|
+
// ── OpenAPI ───────────────────────────────────────────────────────────────
|
|
80
|
+
_app.use(
|
|
81
|
+
openapi({
|
|
82
|
+
path: openapiOpts?.path ?? "/openapi",
|
|
83
|
+
documentation: {
|
|
84
|
+
info: {
|
|
85
|
+
title: openapiOpts?.title ?? "Piggy API",
|
|
86
|
+
version: openapiOpts?.version ?? "1.0.0",
|
|
87
|
+
description: openapiOpts?.description ?? "Auto-generated docs for all registered piggy routes",
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// ── Health ────────────────────────────────────────────────────────────────
|
|
94
|
+
_app.get("/health", () => ({
|
|
95
|
+
status: "ok",
|
|
96
|
+
routes: routeRegistry.size,
|
|
97
|
+
cacheEntries: cache.size(),
|
|
98
|
+
uptime: process.uptime(),
|
|
99
|
+
}), {
|
|
100
|
+
detail: { tags: ["_piggy"], summary: "Health check" },
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ── Cache ─────────────────────────────────────────────────────────────────
|
|
104
|
+
_app.delete("/cache", () => {
|
|
105
|
+
cache.clear();
|
|
106
|
+
return { cleared: true };
|
|
107
|
+
}, {
|
|
108
|
+
detail: { tags: ["_piggy"], summary: "Clear all cache entries" },
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
_app.get("/cache/keys", () => ({ keys: cache.keys() }), {
|
|
112
|
+
detail: { tags: ["_piggy"], summary: "List all cache keys" },
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// ── Registered site routes ────────────────────────────────────────────────
|
|
116
|
+
for (const [registryKey, config] of routeRegistry.entries()) {
|
|
117
|
+
const colonIdx = registryKey.indexOf(":");
|
|
118
|
+
const siteName = registryKey.substring(0, colonIdx);
|
|
119
|
+
const fullPath = `/${siteName}${config.path}`;
|
|
120
|
+
const method = config.method.toLowerCase() as "get" | "post" | "put" | "delete";
|
|
121
|
+
|
|
122
|
+
logger.info(`[server] mounting ${config.method} ${fullPath} (ttl=${config.ttl}ms)`);
|
|
123
|
+
|
|
124
|
+
const routeHandler = async ({ params, query, body, headers, set }: any) => {
|
|
125
|
+
for (const mw of config.before) {
|
|
126
|
+
try {
|
|
127
|
+
await mw({ params, query, body, headers, set });
|
|
128
|
+
} catch (e: any) {
|
|
129
|
+
set.status = set.status ?? 400;
|
|
130
|
+
return { error: e.message };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const cacheKey = `${siteName}:${fullPath}:${JSON.stringify({ params, query })}`;
|
|
135
|
+
const hit = cache.get(cacheKey);
|
|
136
|
+
if (hit !== null) {
|
|
137
|
+
set.headers["x-cache"] = "HIT";
|
|
138
|
+
set.headers["x-cache-key"] = cacheKey;
|
|
139
|
+
return hit;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let result: any;
|
|
143
|
+
try {
|
|
144
|
+
result = await config.handler(params, query, body);
|
|
145
|
+
} catch (e: any) {
|
|
146
|
+
const mapped = mapError(e, siteName);
|
|
147
|
+
set.status = mapped.status;
|
|
148
|
+
return mapped;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (config.ttl > 0) {
|
|
152
|
+
cache.set(cacheKey, result, config.ttl);
|
|
153
|
+
set.headers["x-cache"] = "MISS";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return result;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const routeDetail = {
|
|
160
|
+
tags: config.detail?.tags ?? [siteName],
|
|
161
|
+
summary: config.detail?.summary ?? `${config.method} ${fullPath}`,
|
|
162
|
+
description: config.detail?.description,
|
|
163
|
+
deprecated: config.detail?.deprecated ?? false,
|
|
164
|
+
hide: config.detail?.hide ?? false,
|
|
165
|
+
parameters: config.detail?.parameters ?? [],
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
if (method === "get") _app.get(fullPath, routeHandler, { detail: routeDetail });
|
|
169
|
+
else if (method === "post") _app.post(fullPath, routeHandler, { detail: routeDetail });
|
|
170
|
+
else if (method === "put") _app.put(fullPath, routeHandler, { detail: routeDetail });
|
|
171
|
+
else if (method === "delete") _app.delete(fullPath, routeHandler, { detail: routeDetail });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
_app.listen({ port, hostname });
|
|
175
|
+
|
|
176
|
+
logger.success(`🚀 Piggy API server → http://${hostname === "0.0.0.0" ? "localhost" : hostname}:${port}`);
|
|
177
|
+
logger.success(`📖 OpenAPI docs → http://${hostname === "0.0.0.0" ? "localhost" : hostname}:${port}/openapi`);
|
|
178
|
+
logger.info(` Routes mounted: ${routeRegistry.size}`);
|
|
179
|
+
routeRegistry.forEach((cfg, key) => {
|
|
180
|
+
const siteName = key.substring(0, key.indexOf(":"));
|
|
181
|
+
logger.info(` ${cfg.method} /${siteName}${cfg.path} (ttl=${cfg.ttl}ms)`);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
return _app;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function stopServer() {
|
|
188
|
+
_app?.stop();
|
|
189
|
+
_app = null;
|
|
190
190
|
}
|
package/piggy/store/index.d.ts
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
// piggy/store/index.d.ts
|
|
2
|
-
export type FieldType = "string" | "number" | "boolean" | "object" | "array";
|
|
3
|
-
|
|
4
|
-
export interface FieldSchema {
|
|
5
|
-
type: FieldType;
|
|
6
|
-
required?: boolean;
|
|
7
|
-
default?: any;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface StoreSchema {
|
|
11
|
-
name: string;
|
|
12
|
-
destination: string;
|
|
13
|
-
fields: Record<string, FieldSchema>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface PiggyStoreConfig {
|
|
17
|
-
stores: StoreSchema[];
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export declare function loadStoreConfig(configPath?: string): PiggyStoreConfig;
|
|
21
|
-
export declare function getSchema(storeName: string): StoreSchema | null;
|
|
22
|
-
export declare function shapeRecord(data: Record<string, any>, schema: StoreSchema): Record<string, any>;
|
|
23
|
-
export declare function storeRecord(
|
|
24
|
-
storeName: string,
|
|
25
|
-
data: Record<string, any> | Record<string, any>[]
|
|
1
|
+
// piggy/store/index.d.ts
|
|
2
|
+
export type FieldType = "string" | "number" | "boolean" | "object" | "array";
|
|
3
|
+
|
|
4
|
+
export interface FieldSchema {
|
|
5
|
+
type: FieldType;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
default?: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface StoreSchema {
|
|
11
|
+
name: string;
|
|
12
|
+
destination: string;
|
|
13
|
+
fields: Record<string, FieldSchema>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PiggyStoreConfig {
|
|
17
|
+
stores: StoreSchema[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export declare function loadStoreConfig(configPath?: string): PiggyStoreConfig;
|
|
21
|
+
export declare function getSchema(storeName: string): StoreSchema | null;
|
|
22
|
+
export declare function shapeRecord(data: Record<string, any>, schema: StoreSchema): Record<string, any>;
|
|
23
|
+
export declare function storeRecord(
|
|
24
|
+
storeName: string,
|
|
25
|
+
data: Record<string, any> | Record<string, any>[]
|
|
26
26
|
): Promise<{ stored: number; skipped: number }>;
|