@parsrun/server 0.1.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/dist/health.js ADDED
@@ -0,0 +1,112 @@
1
+ // src/health.ts
2
+ import { Hono } from "hono";
3
+ var startTime = Date.now();
4
+ async function checkDatabase(db) {
5
+ const start = Date.now();
6
+ try {
7
+ if (db.ping) {
8
+ await db.ping();
9
+ }
10
+ return {
11
+ status: "healthy",
12
+ latency: Date.now() - start
13
+ };
14
+ } catch (err) {
15
+ return {
16
+ status: "unhealthy",
17
+ message: err instanceof Error ? err.message : "Database connection failed",
18
+ latency: Date.now() - start
19
+ };
20
+ }
21
+ }
22
+ function aggregateStatus(checks) {
23
+ const statuses = Object.values(checks).map((c) => c.status);
24
+ if (statuses.some((s) => s === "unhealthy")) {
25
+ return "unhealthy";
26
+ }
27
+ if (statuses.some((s) => s === "degraded")) {
28
+ return "degraded";
29
+ }
30
+ return "healthy";
31
+ }
32
+ function createHealthRouter(options = {}) {
33
+ const router = new Hono();
34
+ const { checks = {}, detailed = true } = options;
35
+ router.get("/", async (c) => {
36
+ const db = c.get("db");
37
+ const results = {};
38
+ results["database"] = await checkDatabase(db);
39
+ const customCheckResults = await Promise.all(
40
+ Object.entries(checks).map(async ([name, check]) => {
41
+ try {
42
+ const result = await check();
43
+ return [name, result];
44
+ } catch (err) {
45
+ return [
46
+ name,
47
+ {
48
+ status: "unhealthy",
49
+ message: err instanceof Error ? err.message : "Check failed"
50
+ }
51
+ ];
52
+ }
53
+ })
54
+ );
55
+ for (const [name, result] of customCheckResults) {
56
+ results[name] = result;
57
+ }
58
+ const overallStatus = aggregateStatus(results);
59
+ const response = {
60
+ status: overallStatus,
61
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
62
+ uptime: Math.floor((Date.now() - startTime) / 1e3),
63
+ checks: detailed ? results : {}
64
+ };
65
+ const statusCode = overallStatus === "healthy" ? 200 : overallStatus === "degraded" ? 200 : 503;
66
+ return c.json(response, statusCode);
67
+ });
68
+ router.get("/live", (c) => {
69
+ return c.json({
70
+ status: "ok",
71
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
72
+ });
73
+ });
74
+ router.get("/ready", async (c) => {
75
+ const db = c.get("db");
76
+ const dbHealth = await checkDatabase(db);
77
+ if (dbHealth.status === "unhealthy") {
78
+ return c.json(
79
+ {
80
+ status: "not_ready",
81
+ reason: "database",
82
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
83
+ },
84
+ 503
85
+ );
86
+ }
87
+ return c.json({
88
+ status: "ready",
89
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
90
+ });
91
+ });
92
+ router.get("/startup", (c) => {
93
+ return c.json({
94
+ status: "started",
95
+ uptime: Math.floor((Date.now() - startTime) / 1e3),
96
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
97
+ });
98
+ });
99
+ return router;
100
+ }
101
+ async function healthHandler(c) {
102
+ return c.json({
103
+ status: "ok",
104
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
105
+ uptime: Math.floor((Date.now() - startTime) / 1e3)
106
+ });
107
+ }
108
+ export {
109
+ createHealthRouter,
110
+ healthHandler
111
+ };
112
+ //# sourceMappingURL=health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/health.ts"],"sourcesContent":["/**\n * @parsrun/server - Health Check Endpoints\n * Kubernetes-compatible health and readiness probes\n */\n\nimport { Hono } from \"hono\";\nimport type { HonoApp, ServerContextVariables } from \"./context.js\";\n\n/**\n * Health check status\n */\nexport type HealthStatus = \"healthy\" | \"degraded\" | \"unhealthy\";\n\n/**\n * Health check result\n */\nexport interface HealthCheckResult {\n status: HealthStatus;\n message?: string;\n latency?: number;\n}\n\n/**\n * Health check function\n */\nexport type HealthCheck = () => Promise<HealthCheckResult> | HealthCheckResult;\n\n/**\n * Health response\n */\nexport interface HealthResponse {\n status: HealthStatus;\n timestamp: string;\n uptime: number;\n checks: Record<string, HealthCheckResult>;\n}\n\n/**\n * Health check options\n */\nexport interface HealthCheckOptions {\n /** Custom health checks */\n checks?: Record<string, HealthCheck>;\n /** Include detailed info (disable in production) */\n detailed?: boolean;\n}\n\n// Track server start time\nconst startTime = Date.now();\n\n/**\n * Database health check\n */\nasync function checkDatabase(db: { ping?: () => Promise<boolean> }): Promise<HealthCheckResult> {\n const start = Date.now();\n\n try {\n if (db.ping) {\n await db.ping();\n }\n return {\n status: \"healthy\",\n latency: Date.now() - start,\n };\n } catch (err) {\n return {\n status: \"unhealthy\",\n message: err instanceof Error ? err.message : \"Database connection failed\",\n latency: Date.now() - start,\n };\n }\n}\n\n/**\n * Determine overall status from individual checks\n */\nfunction aggregateStatus(checks: Record<string, HealthCheckResult>): HealthStatus {\n const statuses = Object.values(checks).map((c) => c.status);\n\n if (statuses.some((s) => s === \"unhealthy\")) {\n return \"unhealthy\";\n }\n\n if (statuses.some((s) => s === \"degraded\")) {\n return \"degraded\";\n }\n\n return \"healthy\";\n}\n\n/**\n * Create health check router\n *\n * @example\n * ```typescript\n * const health = createHealthRouter({\n * checks: {\n * redis: async () => {\n * await redis.ping();\n * return { status: 'healthy' };\n * },\n * external: async () => {\n * const res = await fetch('https://api.example.com/health');\n * return { status: res.ok ? 'healthy' : 'unhealthy' };\n * },\n * },\n * });\n *\n * app.route('/health', health);\n * // GET /health - Full health check\n * // GET /health/live - Liveness probe\n * // GET /health/ready - Readiness probe\n * ```\n */\nexport function createHealthRouter(options: HealthCheckOptions = {}): HonoApp {\n const router = new Hono<{ Variables: ServerContextVariables }>();\n const { checks = {}, detailed = true } = options;\n\n /**\n * GET /health - Full health check\n * Returns detailed status of all checks\n */\n router.get(\"/\", async (c) => {\n const db = c.get(\"db\");\n const results: Record<string, HealthCheckResult> = {};\n\n // Run database check\n results[\"database\"] = await checkDatabase(db);\n\n // Run custom checks in parallel\n const customCheckResults = await Promise.all(\n Object.entries(checks).map(async ([name, check]) => {\n try {\n const result = await check();\n return [name, result] as const;\n } catch (err) {\n return [\n name,\n {\n status: \"unhealthy\" as const,\n message: err instanceof Error ? err.message : \"Check failed\",\n },\n ] as const;\n }\n })\n );\n\n for (const [name, result] of customCheckResults) {\n results[name] = result;\n }\n\n const overallStatus = aggregateStatus(results);\n const response: HealthResponse = {\n status: overallStatus,\n timestamp: new Date().toISOString(),\n uptime: Math.floor((Date.now() - startTime) / 1000),\n checks: detailed ? results : {},\n };\n\n const statusCode = overallStatus === \"healthy\" ? 200 : overallStatus === \"degraded\" ? 200 : 503;\n\n return c.json(response, statusCode as 200);\n });\n\n /**\n * GET /health/live - Liveness probe\n * Kubernetes liveness probe - returns 200 if server is running\n */\n router.get(\"/live\", (c) => {\n return c.json({\n status: \"ok\",\n timestamp: new Date().toISOString(),\n });\n });\n\n /**\n * GET /health/ready - Readiness probe\n * Kubernetes readiness probe - returns 200 if server can handle traffic\n */\n router.get(\"/ready\", async (c) => {\n const db = c.get(\"db\");\n\n // Check database connection\n const dbHealth = await checkDatabase(db);\n\n if (dbHealth.status === \"unhealthy\") {\n return c.json(\n {\n status: \"not_ready\",\n reason: \"database\",\n timestamp: new Date().toISOString(),\n },\n 503\n );\n }\n\n return c.json({\n status: \"ready\",\n timestamp: new Date().toISOString(),\n });\n });\n\n /**\n * GET /health/startup - Startup probe\n * For slow-starting containers\n */\n router.get(\"/startup\", (c) => {\n return c.json({\n status: \"started\",\n uptime: Math.floor((Date.now() - startTime) / 1000),\n timestamp: new Date().toISOString(),\n });\n });\n\n return router;\n}\n\n/**\n * Simple health endpoint handler\n *\n * @example\n * ```typescript\n * app.get('/health', healthHandler);\n * ```\n */\nexport async function healthHandler(c: import(\"hono\").Context<{ Variables: ServerContextVariables }>) {\n return c.json({\n status: \"ok\",\n timestamp: new Date().toISOString(),\n uptime: Math.floor((Date.now() - startTime) / 1000),\n });\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,15 @@
1
+ export { CreateServerOptions, createModuleRouter, createRouter, createServer, createVersionedRouter } from './app.js';
2
+ export { ApiResponse, ContextTenant, ContextUser, CorsConfig, DatabaseAdapter, HonoApp, HonoContext, HonoNext, Middleware, ModuleManifest, PermissionCheck, PermissionDefinition, RoleDefinition, RouteHandler, ServerConfig, ServerContextVariables, error, generateRequestId, success } from './context.js';
3
+ export { ModuleLoader, ModuleLoaderOptions, createModuleLoader, defineModule } from './module-loader.js';
4
+ export { RLSConfig, RLSError, RLSManager, createRLSManager, generateDisableRLS, generateRLSPolicy, rlsMiddleware } from './rls.js';
5
+ export { InMemoryRBAC, PermissionChecker, RBACService, StandardRoles, createInMemoryRBAC, createPermission, createRBACService, crudPermissions, parsePermission, requireAnyPermission, requireAnyRole, requireAuth, requirePermission, requireRole, requireTenantMember } from './rbac.js';
6
+ export { ApiError, AuthMiddlewareOptions, BadRequestError, ConflictError, CsrfOptions, ErrorHandlerOptions, ForbiddenError, InternalError, JwtPayload, JwtVerifier, MemoryRateLimitStorage, NotFoundError, QuotaCheckResult, QuotaEnforcementOptions, QuotaExceededError, QuotaManagerLike, RateLimitError, RateLimitOptions, RateLimitStorage, RequestLoggerOptions, ServiceUnavailableError, UnauthorizedError, UsageServiceLike, UsageTrackingOptions, ValidationError, auth, cors, createAuthMiddleware, createQuotaEnforcement, createRateLimiter, createUsageTracking, csrf, doubleSubmitCookie, errorHandler, multiQuotaEnforcement, notFoundHandler, optionalAuth, quotaEnforcement, rateLimit, requestLogger, usageTracking } from './middleware/index.js';
7
+ export { Infer, ValidateOptions, ValidationTarget, validate, validateBody, validateHeaders, validateParams, validateQuery } from './validation/index.js';
8
+ export { CursorPaginatedResponse, CursorPaginationMeta, CursorPaginationParams, PaginatedResponse, PaginationMeta, PaginationOptions, PaginationParams, accepted, createPaginationMeta, created, cursorPaginate, download, json, jsonError, jsonWithMeta, noContent, paginate, parseCursorPagination, parsePagination, redirect, setPaginationHeaders, sse, stream } from './utils/index.js';
9
+ export { HealthCheck, HealthCheckOptions, HealthCheckResult, HealthResponse, HealthStatus, createHealthRouter, healthHandler } from './health.js';
10
+ export { DateRangeQuery, dateRangeQuery as DateRangeQuerySchema, PaginationQuery, paginationQuery as PaginationQuerySchema, SearchQuery, searchQuery as SearchQuerySchema, Type, UuidParam, uuidParam as UuidParamSchema, type } from '@parsrun/types';
11
+ import 'hono';
12
+ import '@parsrun/core';
13
+ import 'hono/utils/types';
14
+ import '@parsrun/core/transports';
15
+ import 'hono/utils/http-status';