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/adapters/index.js +1 -1
- package/dist/chunk-GWLL6W5O.js +26 -0
- package/dist/cli/index.js +7 -148
- package/dist/index.js +2 -457
- package/package.json +1 -1
- package/dist/chunk-NVNSYLVY.js +0 -2075
package/dist/index.js
CHANGED
|
@@ -1,457 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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};
|