lacis 0.2.0 → 0.2.2

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/dist/index.js CHANGED
@@ -1,457 +1,2 @@
1
- import { router, loadRoutes, primaryLog, getAdapter } from './chunk-NVNSYLVY.js';
2
- export { addMiddleware, addPathMiddleware, collectMiddleware, createCorsMiddleware, createSSEClient, findRoute, getPathMiddlewares, getRouterStats, getRoutesDir, hasMiddlewares, initSSE, isRouteError, loadMiddlewares, loadRoutes, registerCorsConfig, registerMiddlewareConfig, registerRoutes, resetMiddlewares, resetRouter, router, runMiddlewares, send, sendEvent, sendJson, setVerboseLogging, sseClose, sseComment, sseEventError, sseId, sseRetry } from './chunk-NVNSYLVY.js';
3
- import cluster from 'cluster';
4
-
5
- // src/config/serverConfig.ts
6
- var defaultConfig = {
7
- port: 3e3,
8
- isDev: process.env.NODE_ENV === "development",
9
- timeout: 3e4,
10
- cluster: {
11
- enabled: true,
12
- workers: void 0
13
- },
14
- platform: "node"
15
- };
16
- function getConfig(customConfig = {}) {
17
- return {
18
- ...defaultConfig,
19
- ...customConfig
20
- };
21
- }
22
-
23
- // src/core/defineHandler.ts
24
- async function runValidation(schema, value) {
25
- const result = await schema["~standard"].validate(value);
26
- if (result.issues) return { success: false, issues: result.issues };
27
- return { success: true, data: result.value };
28
- }
29
- function formatIssues(issues) {
30
- return Array.from(issues).map((issue) => ({
31
- message: issue.message,
32
- path: issue.path ? Array.from(issue.path).map((p) => typeof p === "object" && "key" in p ? p.key : p) : void 0
33
- }));
34
- }
35
- function defineHandler(config) {
36
- const wrapped = async (req, res) => {
37
- if (config.params) {
38
- const result = await runValidation(config.params, req.params ?? {});
39
- if (!result.success) {
40
- res.status(400).json({ error: "Validation failed", issues: formatIssues(result.issues) });
41
- return;
42
- }
43
- req.params = result.data;
44
- }
45
- if (config.query) {
46
- const result = await runValidation(config.query, req.query ?? {});
47
- if (!result.success) {
48
- res.status(400).json({ error: "Validation failed", issues: formatIssues(result.issues) });
49
- return;
50
- }
51
- req.query = result.data;
52
- }
53
- if (config.body) {
54
- let rawBody;
55
- try {
56
- rawBody = await req.json();
57
- } catch {
58
- res.status(400).json({ error: "Invalid JSON body" });
59
- return;
60
- }
61
- const result = await runValidation(config.body, rawBody);
62
- if (!result.success) {
63
- res.status(400).json({ error: "Validation failed", issues: formatIssues(result.issues) });
64
- return;
65
- }
66
- req.body = result.data;
67
- }
68
- await config.handler(req, res);
69
- };
70
- wrapped._defineHandler = config;
71
- return wrapped;
72
- }
73
-
74
- // src/core/openapi.ts
75
- async function schemaToJsonSchema(schema) {
76
- const vendor = schema?.["~standard"]?.vendor;
77
- if (!vendor) return null;
78
- try {
79
- if (vendor === "zod") {
80
- const zod = await import('zod');
81
- if (typeof zod.toJSONSchema === "function") return zod.toJSONSchema(schema);
82
- const mod = await import('zod-to-json-schema');
83
- return (mod.zodToJsonSchema ?? mod.default)(schema, { target: "openApi3" });
84
- }
85
- if (vendor === "valibot") {
86
- const mod = await import('@valibot/to-json-schema');
87
- return (mod.toJsonSchema ?? mod.default)(schema);
88
- }
89
- if (vendor === "arktype") {
90
- return schema.toJsonSchema();
91
- }
92
- } catch {
93
- primaryLog(`[openapi] no converter found for "${vendor}" \u2014 install the matching json-schema package`);
94
- }
95
- return null;
96
- }
97
- function toOpenApiPath(path) {
98
- return path.replace(/:(\w+)\??/g, "{$1}");
99
- }
100
- async function buildOperation(method, config) {
101
- const op = {
102
- responses: { "200": { description: "Success" } }
103
- };
104
- if (config.meta?.summary) op.summary = config.meta.summary;
105
- if (config.meta?.description) op.description = config.meta.description;
106
- if (config.meta?.tags) op.tags = config.meta.tags;
107
- if (config.meta?.deprecated) op.deprecated = config.meta.deprecated;
108
- const parameters = [];
109
- if (config.params) {
110
- const jsonSchema = await schemaToJsonSchema(config.params);
111
- if (jsonSchema?.properties) {
112
- for (const [name, propSchema] of Object.entries(jsonSchema.properties)) {
113
- parameters.push({ name, in: "path", required: true, schema: propSchema });
114
- }
115
- }
116
- }
117
- if (config.query) {
118
- const jsonSchema = await schemaToJsonSchema(config.query);
119
- if (jsonSchema?.properties) {
120
- const required = jsonSchema.required ?? [];
121
- for (const [name, propSchema] of Object.entries(jsonSchema.properties)) {
122
- parameters.push({ name, in: "query", required: required.includes(name), schema: propSchema });
123
- }
124
- }
125
- }
126
- if (parameters.length > 0) op.parameters = parameters;
127
- if (config.body) {
128
- const jsonSchema = await schemaToJsonSchema(config.body);
129
- if (jsonSchema) {
130
- op.requestBody = {
131
- required: true,
132
- content: { "application/json": { schema: jsonSchema } }
133
- };
134
- }
135
- }
136
- return op;
137
- }
138
- async function buildOpenApiDoc(config) {
139
- const routes = router.getRoutes();
140
- const paths = {};
141
- for (const { method, path, handler } of routes) {
142
- const defineConfig = handler._defineHandler;
143
- const openApiPath = toOpenApiPath(path);
144
- if (!paths[openApiPath]) paths[openApiPath] = {};
145
- paths[openApiPath][method.toLowerCase()] = defineConfig ? await buildOperation(method, defineConfig) : { responses: { "200": { description: "Success" } } };
146
- }
147
- return { openapi: "3.1.0", info: config.info, paths };
148
- }
149
- var serverInstance = null;
150
- var isShuttingDown = false;
151
- var shutdownListenersRegistered = false;
152
- async function createServer(routesDir, config = defaultConfig) {
153
- const { platform = "node" } = config;
154
- const verbose = config.isDev && cluster.isPrimary && !process.env.ZENO_BUN_WORKER;
155
- try {
156
- await loadRoutes(routesDir);
157
- if (config.openapi) {
158
- const doc = await buildOpenApiDoc(config.openapi);
159
- const openapiPath = config.openapi.path ?? "/openapi.json";
160
- router.addRoute("GET", openapiPath, (_req, res) => res.json(doc));
161
- if (verbose) primaryLog(`OpenAPI doc available at ${openapiPath}`);
162
- }
163
- if (verbose) {
164
- primaryLog("\u{1F680} Serveur d\xE9marr\xE9");
165
- primaryLog(`\u{1F4C2} Routes charg\xE9es depuis: ${routesDir}`);
166
- }
167
- if (config.isDev) {
168
- if (verbose) {
169
- primaryLog("\u{1F525} Mode de d\xE9veloppement activ\xE9");
170
- }
171
- }
172
- const adapter = getAdapter(platform);
173
- const handler = adapter.createHandler(routesDir);
174
- let server;
175
- switch (platform) {
176
- case "node":
177
- server = await handler(config);
178
- serverInstance = server;
179
- break;
180
- case "bun":
181
- server = handler(config);
182
- break;
183
- case "vercel":
184
- case "netlify":
185
- server = handler;
186
- break;
187
- default:
188
- throw new Error(`Plateforme "${platform}" non support\xE9e`);
189
- }
190
- setupGracefulShutdown();
191
- return server;
192
- } catch (error) {
193
- if (verbose) {
194
- primaryLog("\u274C Erreur lors de la cr\xE9ation du serveur:", error);
195
- }
196
- throw error;
197
- }
198
- }
199
- function setupGracefulShutdown() {
200
- if (!cluster.isPrimary) return;
201
- if (shutdownListenersRegistered) return;
202
- shutdownListenersRegistered = true;
203
- const shutdown = async (signal) => {
204
- if (isShuttingDown) return;
205
- isShuttingDown = true;
206
- primaryLog(`
207
- Signal ${signal} re\xE7u, arr\xEAt en cours...`);
208
- if (serverInstance && typeof serverInstance.close === "function") {
209
- await new Promise((resolve) => {
210
- serverInstance.close(() => resolve());
211
- setTimeout(() => {
212
- primaryLog("Fermeture forc\xE9e apr\xE8s d\xE9lai");
213
- resolve();
214
- }, 3e3);
215
- });
216
- }
217
- process.exit(0);
218
- };
219
- process.on("SIGINT", () => shutdown("SIGINT"));
220
- process.on("SIGTERM", () => shutdown("SIGTERM"));
221
- process.on("SIGHUP", () => shutdown("SIGHUP"));
222
- }
223
-
224
- // src/core/errors.ts
225
- var HTTP_STATUS = {
226
- OK: 200,
227
- CREATED: 201,
228
- NO_CONTENT: 204,
229
- BAD_REQUEST: 400,
230
- UNAUTHORIZED: 401,
231
- FORBIDDEN: 403,
232
- NOT_FOUND: 404,
233
- METHOD_NOT_ALLOWED: 405,
234
- CONFLICT: 409,
235
- UNPROCESSABLE_ENTITY: 422,
236
- TOO_MANY_REQUESTS: 429,
237
- INTERNAL_SERVER_ERROR: 500,
238
- SERVICE_UNAVAILABLE: 503,
239
- GATEWAY_TIMEOUT: 504
240
- };
241
- var DEFAULT_ERROR_MESSAGES = {
242
- 400: "Bad Request",
243
- 401: "Unauthorized",
244
- 403: "Forbidden",
245
- 404: "Not Found",
246
- 405: "Method Not Allowed",
247
- 409: "Conflict",
248
- 422: "Unprocessable Entity",
249
- 429: "Too Many Requests",
250
- 500: "Internal Server Error",
251
- 503: "Service Unavailable",
252
- 504: "Gateway Timeout"
253
- };
254
- function createHttpError(options) {
255
- const code = options.code || HTTP_STATUS.INTERNAL_SERVER_ERROR;
256
- const message = options.message || DEFAULT_ERROR_MESSAGES[code] || "Unknown Error";
257
- const name = options.name || `HttpError${code}`;
258
- const stackCapture = new Error(message);
259
- Error.captureStackTrace?.(stackCapture, createHttpError);
260
- return {
261
- name,
262
- code,
263
- message,
264
- details: options.details,
265
- expose: options.expose !== void 0 ? options.expose : code < 500,
266
- log: options.log !== void 0 ? options.log : code >= 500,
267
- stack: stackCapture.stack
268
- };
269
- }
270
- function errorToJSON(error) {
271
- const result = {
272
- error: error.message,
273
- code: error.code
274
- };
275
- if (error.expose && error.details) {
276
- return { ...result, details: error.details };
277
- }
278
- return result;
279
- }
280
- function sendError(error, res) {
281
- if (!res.headersSent) {
282
- res.statusCode = error.code;
283
- res.setHeader("Content-Type", "application/json");
284
- res.end(JSON.stringify(errorToJSON(error)));
285
- }
286
- if (error.log) {
287
- logError(error);
288
- }
289
- }
290
- function logError(error) {
291
- const logData = {
292
- name: error.name,
293
- message: error.message,
294
- code: error.code,
295
- details: error.details,
296
- stack: error.stack
297
- };
298
- if (error.code >= 500) {
299
- primaryLog("\u274C ERROR:", JSON.stringify(logData, null, 2));
300
- } else {
301
- primaryLog("\u26A0\uFE0F WARNING:", JSON.stringify(logData, null, 2));
302
- }
303
- }
304
- function createBadRequestError(message, details) {
305
- return createHttpError({
306
- name: "BadRequestError",
307
- code: HTTP_STATUS.BAD_REQUEST,
308
- message,
309
- details
310
- });
311
- }
312
- function createUnauthorizedError(message, details) {
313
- return createHttpError({
314
- name: "UnauthorizedError",
315
- code: HTTP_STATUS.UNAUTHORIZED,
316
- message,
317
- details
318
- });
319
- }
320
- function createForbiddenError(message, details) {
321
- return createHttpError({
322
- name: "ForbiddenError",
323
- code: HTTP_STATUS.FORBIDDEN,
324
- message,
325
- details
326
- });
327
- }
328
- function createNotFoundError(message, details) {
329
- return createHttpError({
330
- name: "NotFoundError",
331
- code: HTTP_STATUS.NOT_FOUND,
332
- message,
333
- details
334
- });
335
- }
336
- function createMethodNotAllowedError(message, details) {
337
- return createHttpError({
338
- name: "MethodNotAllowedError",
339
- code: HTTP_STATUS.METHOD_NOT_ALLOWED,
340
- message,
341
- details
342
- });
343
- }
344
- function createConflictError(message, details) {
345
- return createHttpError({
346
- name: "ConflictError",
347
- code: HTTP_STATUS.CONFLICT,
348
- message,
349
- details
350
- });
351
- }
352
- function createValidationError(message, details) {
353
- return createHttpError({
354
- name: "ValidationError",
355
- code: HTTP_STATUS.UNPROCESSABLE_ENTITY,
356
- message,
357
- details,
358
- expose: true
359
- });
360
- }
361
- function createRateLimitError(message, details) {
362
- return createHttpError({
363
- name: "RateLimitError",
364
- code: HTTP_STATUS.TOO_MANY_REQUESTS,
365
- message,
366
- details
367
- });
368
- }
369
- function createInternalServerError(message, details) {
370
- return createHttpError({
371
- name: "InternalServerError",
372
- code: HTTP_STATUS.INTERNAL_SERVER_ERROR,
373
- message,
374
- details
375
- });
376
- }
377
- function createServiceUnavailableError(message, details) {
378
- return createHttpError({
379
- name: "ServiceUnavailableError",
380
- code: HTTP_STATUS.SERVICE_UNAVAILABLE,
381
- message,
382
- details
383
- });
384
- }
385
- function createGatewayTimeoutError(message, details) {
386
- return createHttpError({
387
- name: "GatewayTimeoutError",
388
- code: HTTP_STATUS.GATEWAY_TIMEOUT,
389
- message,
390
- details
391
- });
392
- }
393
- function normalizeError(error) {
394
- if (error && typeof error === "object" && typeof error.code === "number" && "message" in error) {
395
- return error;
396
- }
397
- const message = error?.message || "Unknown error occurred";
398
- const details = error?.stack ? { stack: error.stack } : void 0;
399
- const errorCode = error?.code || error?.statusCode;
400
- if (errorCode === "ECONNREFUSED" || errorCode === "ENOTFOUND") {
401
- return createServiceUnavailableError(
402
- `Service connection failed: ${message}`,
403
- details
404
- );
405
- }
406
- if (errorCode === "ETIMEDOUT") {
407
- return createGatewayTimeoutError(`Request timed out: ${message}`, details);
408
- }
409
- if (typeof errorCode === "number" && errorCode >= 400 && errorCode < 600) {
410
- return createHttpError({
411
- code: errorCode,
412
- message,
413
- details
414
- });
415
- }
416
- return createInternalServerError(message, details);
417
- }
418
- function isHttpError(error) {
419
- return !!(error && typeof error === "object" && typeof error.code === "number" && "message" in error && "name" in error);
420
- }
421
-
422
- // src/core/rateLimit.ts
423
- function createRateLimit(options = {}) {
424
- const windowMs = options.windowMs ?? 6e4;
425
- const max = options.max ?? 100;
426
- const message = options.message ?? "Too Many Requests";
427
- const keyGenerator = options.keyGenerator ?? ((req) => {
428
- const forwarded = req.headers["x-forwarded-for"];
429
- return (typeof forwarded === "string" ? forwarded.split(",")[0].trim() : req.socket?.remoteAddress) ?? "unknown";
430
- });
431
- const store = /* @__PURE__ */ new Map();
432
- return (req, res) => {
433
- const key = keyGenerator(req);
434
- const now = Date.now();
435
- let entry = store.get(key);
436
- if (!entry || now >= entry.resetAt) {
437
- entry = { count: 0, resetAt: now + windowMs };
438
- store.set(key, entry);
439
- }
440
- entry.count++;
441
- const remaining = Math.max(0, max - entry.count);
442
- const resetSecs = Math.ceil(entry.resetAt / 1e3);
443
- res.setHeader("X-RateLimit-Limit", String(max));
444
- res.setHeader("X-RateLimit-Remaining", String(remaining));
445
- res.setHeader("X-RateLimit-Reset", String(resetSecs));
446
- if (entry.count > max) {
447
- res.setHeader(
448
- "Retry-After",
449
- String(Math.ceil((entry.resetAt - now) / 1e3))
450
- );
451
- sendError(createRateLimitError(message), res);
452
- return false;
453
- }
454
- };
455
- }
456
-
457
- export { HTTP_STATUS, buildOpenApiDoc, createBadRequestError, createConflictError, createForbiddenError, createGatewayTimeoutError, createHttpError, createInternalServerError, createMethodNotAllowedError, createNotFoundError, createRateLimit, createRateLimitError, createServer, createServiceUnavailableError, createUnauthorizedError, createValidationError, defaultConfig, defineHandler, getConfig, isHttpError, logError, normalizeError, sendError };
1
+ import {k as k$1,n,j,J}from'./chunk-GWLL6W5O.js';export{a as addMiddleware,b as addPathMiddleware,c as collectMiddleware,t as createCorsMiddleware,v as createSSEClient,o as findRoute,g as getPathMiddlewares,q as getRouterStats,p as getRoutesDir,d as hasMiddlewares,w as initSSE,l as isRouteError,f as loadMiddlewares,n as loadRoutes,u as registerCorsConfig,i as registerMiddlewareConfig,m as registerRoutes,h as resetMiddlewares,s as resetRouter,k as router,e as runMiddlewares,x as send,z as sendEvent,y as sendJson,r as setVerboseLogging,D as sseClose,A as sseComment,E as sseEventError,B as sseId,C as sseRetry}from'./chunk-GWLL6W5O.js';import O from'cluster';var g={port:3e3,isDev:process.env.NODE_ENV==="development",timeout:3e4,cluster:{enabled:true,workers:void 0},platform:"node"};function le(e={}){return {...g,...e}}async function S(e,r){let t=await e["~standard"].validate(r);return t.issues?{success:false,issues:t.issues}:{success:true,data:t.value}}function E(e){return Array.from(e).map(r=>({message:r.message,path:r.path?Array.from(r.path).map(t=>typeof t=="object"&&"key"in t?t.key:t):void 0}))}function ye(e){let r=async(t,n)=>{if(e.params){let a=await S(e.params,t.params??{});if(!a.success){n.status(400).json({error:"Validation failed",issues:E(a.issues)});return}t.params=a.data;}if(e.query){let a=await S(e.query,t.query??{});if(!a.success){n.status(400).json({error:"Validation failed",issues:E(a.issues)});return}t.query=a.data;}if(e.body){let a;try{a=await t.json();}catch{n.status(400).json({error:"Invalid JSON body"});return}let s=await S(e.body,a);if(!s.success){n.status(400).json({error:"Validation failed",issues:E(s.issues)});return}t.body=s.data;}await e.handler(t,n);};return r._defineHandler=e,r}async function R(e){let r=e?.["~standard"]?.vendor;if(!r)return null;try{if(r==="zod"){let t=await import('zod');if(typeof t.toJSONSchema=="function")return t.toJSONSchema(e);let n=await import('zod-to-json-schema');return (n.zodToJsonSchema??n.default)(e,{target:"openApi3"})}if(r==="valibot"){let t=await import('@valibot/to-json-schema');return (t.toJsonSchema??t.default)(e)}if(r==="arktype")return e.toJsonSchema()}catch{j(`[openapi] no converter found for "${r}" \u2014 install the matching json-schema package`);}return null}function k(e){return e.replace(/:(\w+)\??/g,"{$1}")}async function N(e,r){let t={responses:{200:{description:"Success"}}};r.meta?.summary&&(t.summary=r.meta.summary),r.meta?.description&&(t.description=r.meta.description),r.meta?.tags&&(t.tags=r.meta.tags),r.meta?.deprecated&&(t.deprecated=r.meta.deprecated);let n=[];if(r.params){let a=await R(r.params);if(a?.properties)for(let[s,o]of Object.entries(a.properties))n.push({name:s,in:"path",required:true,schema:o});}if(r.query){let a=await R(r.query);if(a?.properties){let s=a.required??[];for(let[o,i]of Object.entries(a.properties))n.push({name:o,in:"query",required:s.includes(o),schema:i});}}if(n.length>0&&(t.parameters=n),r.body){let a=await R(r.body);a&&(t.requestBody={required:true,content:{"application/json":{schema:a}}});}return t}async function h(e){let r=k$1.getRoutes(),t={};for(let{method:n,path:a,handler:s}of r){let o=s._defineHandler,i=k(a);t[i]||(t[i]={}),t[i][n.toLowerCase()]=o?await N(n,o):{responses:{200:{description:"Success"}}};}return {openapi:"3.1.0",info:e.info,paths:t}}var y=null,v=false,C=false;async function we(e,r=g){let{platform:t="node"}=r,n$1=r.isDev&&O.isPrimary&&!process.env.LACIS_BUN_WORKER;try{if(await n(e),r.openapi){let i=await h(r.openapi),m=r.openapi.path??"/openapi.json";k$1.addRoute("GET",m,(l,c)=>c.json(i)),n$1&&j(`OpenAPI doc available at ${m}`);}n$1&&(j("\u{1F680} Serveur d\xE9marr\xE9"),j(`\u{1F4C2} Routes charg\xE9es depuis: ${e}`)),r.isDev&&n$1&&j("\u{1F525} Mode de d\xE9veloppement activ\xE9");let s=J(t).createHandler(e),o;switch(t){case "node":o=await s(r),y=o;break;case "bun":o=s(r);break;case "vercel":case "netlify":o=s;break;default:throw new Error(`Plateforme "${t}" non support\xE9e`)}return I(),o}catch(a){throw n$1&&j("\u274C Erreur lors de la cr\xE9ation du serveur:",a),a}}function I(){if(!O.isPrimary||C)return;C=true;let e=async r=>{v||(v=true,j(`
2
+ Signal ${r} re\xE7u, arr\xEAt en cours...`),y&&typeof y.close=="function"&&await new Promise(t=>{y.close(()=>t()),setTimeout(()=>{j("Fermeture forc\xE9e apr\xE8s d\xE9lai"),t();},3e3);}),process.exit(0));};process.on("SIGINT",()=>e("SIGINT")),process.on("SIGTERM",()=>e("SIGTERM")),process.on("SIGHUP",()=>e("SIGHUP"));}var p={OK:200,CREATED:201,NO_CONTENT:204,BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_ALLOWED:405,CONFLICT:409,UNPROCESSABLE_ENTITY:422,TOO_MANY_REQUESTS:429,INTERNAL_SERVER_ERROR:500,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504},P={400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",409:"Conflict",422:"Unprocessable Entity",429:"Too Many Requests",500:"Internal Server Error",503:"Service Unavailable",504:"Gateway Timeout"};function u(e){let r=e.code||p.INTERNAL_SERVER_ERROR,t=e.message||P[r]||"Unknown Error",n=e.name||`HttpError${r}`,a=new Error(t);return Error.captureStackTrace?.(a,u),{name:n,code:r,message:t,details:e.details,expose:e.expose!==void 0?e.expose:r<500,log:e.log!==void 0?e.log:r>=500,stack:a.stack}}function q(e){let r={error:e.message,code:e.code};return e.expose&&e.details?{...r,details:e.details}:r}function w(e,r){r.headersSent||(r.statusCode=e.code,r.setHeader("Content-Type","application/json"),r.end(JSON.stringify(q(e)))),e.log&&D(e);}function D(e){let r={name:e.name,message:e.message,code:e.code,details:e.details,stack:e.stack};e.code>=500?j("\u274C ERROR:",JSON.stringify(r,null,2)):j("\u26A0\uFE0F WARNING:",JSON.stringify(r,null,2));}function Ae(e,r){return u({name:"BadRequestError",code:p.BAD_REQUEST,message:e,details:r})}function ke(e,r){return u({name:"UnauthorizedError",code:p.UNAUTHORIZED,message:e,details:r})}function Ne(e,r){return u({name:"ForbiddenError",code:p.FORBIDDEN,message:e,details:r})}function Ie(e,r){return u({name:"NotFoundError",code:p.NOT_FOUND,message:e,details:r})}function Pe(e,r){return u({name:"MethodNotAllowedError",code:p.METHOD_NOT_ALLOWED,message:e,details:r})}function qe(e,r){return u({name:"ConflictError",code:p.CONFLICT,message:e,details:r})}function De(e,r){return u({name:"ValidationError",code:p.UNPROCESSABLE_ENTITY,message:e,details:r,expose:true})}function x(e,r){return u({name:"RateLimitError",code:p.TOO_MANY_REQUESTS,message:e,details:r})}function L(e,r){return u({name:"InternalServerError",code:p.INTERNAL_SERVER_ERROR,message:e,details:r})}function M(e,r){return u({name:"ServiceUnavailableError",code:p.SERVICE_UNAVAILABLE,message:e,details:r})}function U(e,r){return u({name:"GatewayTimeoutError",code:p.GATEWAY_TIMEOUT,message:e,details:r})}function Le(e){if(e&&typeof e=="object"&&typeof e.code=="number"&&"message"in e)return e;let r=e?.message||"Unknown error occurred",t=e?.stack?{stack:e.stack}:void 0,n=e?.code||e?.statusCode;return n==="ECONNREFUSED"||n==="ENOTFOUND"?M(`Service connection failed: ${r}`,t):n==="ETIMEDOUT"?U(`Request timed out: ${r}`,t):typeof n=="number"&&n>=400&&n<600?u({code:n,message:r,details:t}):L(r,t)}function Me(e){return !!(e&&typeof e=="object"&&typeof e.code=="number"&&"message"in e&&"name"in e)}function Be(e={}){let r=e.windowMs??6e4,t=e.max??100,n=e.message??"Too Many Requests",a=e.keyGenerator??(o=>{let i=o.headers["x-forwarded-for"];return (typeof i=="string"?i.split(",")[0].trim():o.socket?.remoteAddress)??"unknown"}),s=new Map;return (o,i)=>{let m=a(o),l=Date.now(),c=s.get(m);(!c||l>=c.resetAt)&&(c={count:0,resetAt:l+r},s.set(m,c)),c.count++;let H=Math.max(0,t-c.count),A=Math.ceil(c.resetAt/1e3);if(i.setHeader("X-RateLimit-Limit",String(t)),i.setHeader("X-RateLimit-Remaining",String(H)),i.setHeader("X-RateLimit-Reset",String(A)),c.count>t)return i.setHeader("Retry-After",String(Math.ceil((c.resetAt-l)/1e3))),w(x(n),i),false}}export{p as HTTP_STATUS,h as buildOpenApiDoc,Ae as createBadRequestError,qe as createConflictError,Ne as createForbiddenError,U as createGatewayTimeoutError,u as createHttpError,L as createInternalServerError,Pe as createMethodNotAllowedError,Ie as createNotFoundError,Be as createRateLimit,x as createRateLimitError,we as createServer,M as createServiceUnavailableError,ke as createUnauthorizedError,De as createValidationError,g as defaultConfig,ye as defineHandler,le as getConfig,Me as isHttpError,D as logError,Le as normalizeError,w as sendError};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lacis",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Zero-dependency TypeScript web framework",
5
5
  "type": "module",
6
6
  "module": "index.ts",