@kozojs/core 0.2.7 → 0.3.0
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/lib/chunk-W44TTZNJ.js +205 -0
- package/lib/index.js +63 -1081
- package/lib/middleware/index.js +16 -189
- package/package.json +2 -2
- package/lib/index.d.ts +0 -658
- package/lib/index.js.map +0 -1
- package/lib/middleware/index.d.ts +0 -160
- package/lib/middleware/index.js.map +0 -1
- package/lib/wasm/radix.wasm +0 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// src/middleware/logger.ts
|
|
2
|
+
function logger(options = {}) {
|
|
3
|
+
const { prefix = "\u{1F310}", colorize = true } = options;
|
|
4
|
+
return async (c, next) => {
|
|
5
|
+
const start = Date.now();
|
|
6
|
+
const method = c.req.method;
|
|
7
|
+
const path = new URL(c.req.url).pathname;
|
|
8
|
+
await next();
|
|
9
|
+
const duration = Date.now() - start;
|
|
10
|
+
const status = c.res.status;
|
|
11
|
+
const statusColor = status >= 500 ? "\u{1F534}" : status >= 400 ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
12
|
+
const log = `${prefix} ${method.padEnd(6)} ${path} ${statusColor} ${status} ${duration}ms`;
|
|
13
|
+
console.log(log);
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/middleware/cors.ts
|
|
18
|
+
import { cors as honoCors } from "hono/cors";
|
|
19
|
+
function cors(options = {}) {
|
|
20
|
+
return honoCors({
|
|
21
|
+
origin: options.origin || "*",
|
|
22
|
+
allowMethods: options.allowMethods || ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
|
|
23
|
+
allowHeaders: options.allowHeaders || ["Content-Type", "Authorization"],
|
|
24
|
+
exposeHeaders: options.exposeHeaders || [],
|
|
25
|
+
maxAge: options.maxAge || 86400,
|
|
26
|
+
credentials: options.credentials || false
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/middleware/rate-limit.ts
|
|
31
|
+
var store = /* @__PURE__ */ new Map();
|
|
32
|
+
function rateLimit(options) {
|
|
33
|
+
const {
|
|
34
|
+
max = 100,
|
|
35
|
+
window = 60,
|
|
36
|
+
keyGenerator = (c) => c.req.header("x-forwarded-for") ?? c.req.header("x-real-ip") ?? "anonymous",
|
|
37
|
+
message = "Too many requests"
|
|
38
|
+
} = options;
|
|
39
|
+
return async (c, next) => {
|
|
40
|
+
const key = keyGenerator(c);
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
const windowMs = window * 1e3;
|
|
43
|
+
let record = store.get(key);
|
|
44
|
+
if (!record || now > record.resetAt) {
|
|
45
|
+
record = { count: 0, resetAt: now + windowMs };
|
|
46
|
+
}
|
|
47
|
+
record.count++;
|
|
48
|
+
store.set(key, record);
|
|
49
|
+
c.header("X-RateLimit-Limit", String(max));
|
|
50
|
+
c.header("X-RateLimit-Remaining", String(Math.max(0, max - record.count)));
|
|
51
|
+
c.header("X-RateLimit-Reset", String(Math.ceil(record.resetAt / 1e3)));
|
|
52
|
+
if (record.count > max) {
|
|
53
|
+
return c.json({ error: message }, 429);
|
|
54
|
+
}
|
|
55
|
+
await next();
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function clearRateLimitStore() {
|
|
59
|
+
store.clear();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/middleware/error-handler.ts
|
|
63
|
+
var HttpError = class extends Error {
|
|
64
|
+
constructor(statusCode, message, details) {
|
|
65
|
+
super(message);
|
|
66
|
+
this.statusCode = statusCode;
|
|
67
|
+
this.details = details;
|
|
68
|
+
this.name = "HttpError";
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var BadRequestError = class extends HttpError {
|
|
72
|
+
constructor(message = "Bad Request", details) {
|
|
73
|
+
super(400, message, details);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
var UnauthorizedError = class extends HttpError {
|
|
77
|
+
constructor(message = "Unauthorized") {
|
|
78
|
+
super(401, message);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var ForbiddenError = class extends HttpError {
|
|
82
|
+
constructor(message = "Forbidden") {
|
|
83
|
+
super(403, message);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var NotFoundError = class extends HttpError {
|
|
87
|
+
constructor(message = "Not Found") {
|
|
88
|
+
super(404, message);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
var ConflictError = class extends HttpError {
|
|
92
|
+
constructor(message = "Conflict", details) {
|
|
93
|
+
super(409, message, details);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
var InternalServerError = class extends HttpError {
|
|
97
|
+
constructor(message = "Internal Server Error") {
|
|
98
|
+
super(500, message);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
function errorHandler() {
|
|
102
|
+
return async (c, next) => {
|
|
103
|
+
try {
|
|
104
|
+
await next();
|
|
105
|
+
} catch (err) {
|
|
106
|
+
if (err instanceof HttpError) {
|
|
107
|
+
return c.json({
|
|
108
|
+
error: err.message,
|
|
109
|
+
status: err.statusCode,
|
|
110
|
+
...err.details ? { details: err.details } : {}
|
|
111
|
+
}, err.statusCode);
|
|
112
|
+
}
|
|
113
|
+
console.error("Unhandled error:", err);
|
|
114
|
+
return c.json({
|
|
115
|
+
error: "Internal Server Error",
|
|
116
|
+
status: 500
|
|
117
|
+
}, 500);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/middleware/fileSystemRouting.ts
|
|
123
|
+
import { readFile } from "fs/promises";
|
|
124
|
+
import { resolve } from "path";
|
|
125
|
+
import { pathToFileURL } from "url";
|
|
126
|
+
async function readManifest(manifestPath, onMissing) {
|
|
127
|
+
try {
|
|
128
|
+
const raw = await readFile(manifestPath, "utf-8");
|
|
129
|
+
return JSON.parse(raw);
|
|
130
|
+
} catch (err) {
|
|
131
|
+
onMissing(err instanceof Error ? err : new Error(String(err)));
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function importHandler(handlerPath) {
|
|
136
|
+
try {
|
|
137
|
+
const url = handlerPath.startsWith("file://") ? handlerPath : pathToFileURL(handlerPath).href;
|
|
138
|
+
const mod = await import(url);
|
|
139
|
+
if (typeof mod.default !== "function") {
|
|
140
|
+
console.warn(
|
|
141
|
+
`[kozo:fsr] Skipping ${handlerPath}: no default export function`
|
|
142
|
+
);
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
return mod.default;
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.warn(
|
|
148
|
+
`[kozo:fsr] Failed to import handler ${handlerPath}:`,
|
|
149
|
+
err.message
|
|
150
|
+
);
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function applyFileSystemRouting(app, options = {}) {
|
|
155
|
+
const {
|
|
156
|
+
manifestPath = resolve(process.cwd(), "routes-manifest.json"),
|
|
157
|
+
verbose = false,
|
|
158
|
+
onMissingManifest = () => {
|
|
159
|
+
},
|
|
160
|
+
logger: logger2 = console.log
|
|
161
|
+
} = options;
|
|
162
|
+
const manifest = await readManifest(manifestPath, onMissingManifest);
|
|
163
|
+
if (!manifest) return;
|
|
164
|
+
const log = logger2;
|
|
165
|
+
if (verbose) {
|
|
166
|
+
log(
|
|
167
|
+
`
|
|
168
|
+
\u{1F4CB} [kozo:fsr] Loading ${manifest.routes.length} route(s) from manifest
|
|
169
|
+
`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
for (const route of manifest.routes) {
|
|
173
|
+
const handler = await importHandler(route.handler);
|
|
174
|
+
if (!handler) continue;
|
|
175
|
+
app[route.method](route.path, handler);
|
|
176
|
+
if (verbose) {
|
|
177
|
+
log(
|
|
178
|
+
` ${route.method.toUpperCase().padEnd(6)} ${route.path} \u2192 ${route.handler}`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (verbose) {
|
|
183
|
+
log("");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function createFileSystemRouting(options = {}) {
|
|
187
|
+
return (app) => applyFileSystemRouting(app, options);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export {
|
|
191
|
+
logger,
|
|
192
|
+
cors,
|
|
193
|
+
rateLimit,
|
|
194
|
+
clearRateLimitStore,
|
|
195
|
+
HttpError,
|
|
196
|
+
BadRequestError,
|
|
197
|
+
UnauthorizedError,
|
|
198
|
+
ForbiddenError,
|
|
199
|
+
NotFoundError,
|
|
200
|
+
ConflictError,
|
|
201
|
+
InternalServerError,
|
|
202
|
+
errorHandler,
|
|
203
|
+
applyFileSystemRouting,
|
|
204
|
+
createFileSystemRouting
|
|
205
|
+
};
|