@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/index.js ADDED
@@ -0,0 +1,2094 @@
1
+ // src/app.ts
2
+ import { Hono } from "hono";
3
+ import { createLogger } from "@parsrun/core";
4
+
5
+ // src/context.ts
6
+ function success(data, meta) {
7
+ return {
8
+ success: true,
9
+ data,
10
+ meta: meta ?? void 0
11
+ };
12
+ }
13
+ function error(code, message, details) {
14
+ return {
15
+ success: false,
16
+ error: { code, message, details: details ?? void 0 }
17
+ };
18
+ }
19
+ function generateRequestId() {
20
+ return crypto.randomUUID();
21
+ }
22
+
23
+ // src/app.ts
24
+ function createServer(options) {
25
+ const app = new Hono({
26
+ strict: options.strict ?? false
27
+ });
28
+ const logger = options.logger ?? createLogger({ name: "pars-server" });
29
+ app.use("*", async (c, next) => {
30
+ c.set("db", options.database);
31
+ c.set("config", options);
32
+ c.set("logger", logger);
33
+ c.set("enabledModules", /* @__PURE__ */ new Set());
34
+ c.set("cookiePrefix", options.cookiePrefix);
35
+ c.set("custom", options.custom ?? {});
36
+ if (options.requestId !== false) {
37
+ const requestId = c.req.header("x-request-id") ?? generateRequestId();
38
+ c.set("requestId", requestId);
39
+ c.header("x-request-id", requestId);
40
+ }
41
+ await next();
42
+ });
43
+ return app;
44
+ }
45
+ function createRouter() {
46
+ return new Hono();
47
+ }
48
+ function createVersionedRouter(version) {
49
+ const router = new Hono();
50
+ const versionedRouter = new Hono();
51
+ versionedRouter.route(`/${version}`, router);
52
+ return versionedRouter;
53
+ }
54
+ function createModuleRouter(moduleName, options) {
55
+ const moduleRouter = new Hono();
56
+ if (options.middleware) {
57
+ for (const mw of options.middleware) {
58
+ moduleRouter.use("*", mw);
59
+ }
60
+ }
61
+ options.routes(moduleRouter);
62
+ const wrappedRouter = new Hono();
63
+ wrappedRouter.route(`/${moduleName}`, moduleRouter);
64
+ return wrappedRouter;
65
+ }
66
+
67
+ // src/module-loader.ts
68
+ import { Hono as Hono2 } from "hono";
69
+ import { cors } from "hono/cors";
70
+ import { createLogger as createLogger2 } from "@parsrun/core";
71
+ var ModuleLoader = class {
72
+ app;
73
+ db;
74
+ enabledModules = /* @__PURE__ */ new Set();
75
+ moduleRegistry = /* @__PURE__ */ new Map();
76
+ logger;
77
+ config;
78
+ cookiePrefix;
79
+ initialized = false;
80
+ constructor(options) {
81
+ this.config = options.config;
82
+ this.db = options.config.database;
83
+ this.cookiePrefix = options.cookiePrefix;
84
+ this.logger = options.logger ?? createLogger2({ name: "ModuleLoader" });
85
+ this.app = new Hono2();
86
+ this.setupMiddleware();
87
+ this.setupCoreRoutes();
88
+ }
89
+ /**
90
+ * Initialize the module loader
91
+ * Checks database connection
92
+ */
93
+ async initialize() {
94
+ if (this.initialized) {
95
+ this.logger.warn("ModuleLoader already initialized");
96
+ return;
97
+ }
98
+ this.logger.info("Initializing ModuleLoader...");
99
+ try {
100
+ if (this.db.ping) {
101
+ const ok = await this.db.ping();
102
+ if (!ok) throw new Error("Database ping returned false");
103
+ } else {
104
+ await this.db.execute("SELECT 1");
105
+ }
106
+ this.logger.info("Database connection: OK");
107
+ } catch (error2) {
108
+ this.logger.error("Database connection failed", error2);
109
+ throw new Error("Database connection failed");
110
+ }
111
+ this.initialized = true;
112
+ this.logger.info("ModuleLoader initialized successfully");
113
+ }
114
+ /**
115
+ * Setup core middleware
116
+ */
117
+ setupMiddleware() {
118
+ this.app.use("*", async (c, next) => {
119
+ const requestId = generateRequestId();
120
+ const requestLogger2 = this.logger.child({ requestId });
121
+ c.set("db", this.db);
122
+ c.set("config", this.config);
123
+ c.set("enabledModules", this.enabledModules);
124
+ c.set("logger", requestLogger2);
125
+ c.set("requestId", requestId);
126
+ c.set("cookiePrefix", this.cookiePrefix);
127
+ c.set("custom", this.config.custom ?? {});
128
+ c.set("user", void 0);
129
+ c.set("tenant", void 0);
130
+ const start = Date.now();
131
+ await next();
132
+ const duration = Date.now() - start;
133
+ requestLogger2.debug("Request completed", {
134
+ method: c.req.method,
135
+ path: c.req.path,
136
+ status: c.res.status,
137
+ durationMs: duration
138
+ });
139
+ });
140
+ if (this.config.cors) {
141
+ this.app.use("*", cors(this.normalizeCorsConfig(this.config.cors)));
142
+ }
143
+ }
144
+ /**
145
+ * Normalize CORS config for Hono
146
+ */
147
+ normalizeCorsConfig(config) {
148
+ const result = {
149
+ origin: typeof config.origin === "function" ? (origin) => config.origin(origin) ? origin : null : config.origin
150
+ };
151
+ if (config.credentials !== void 0) {
152
+ result.credentials = config.credentials;
153
+ }
154
+ if (config.methods !== void 0) {
155
+ result.allowMethods = config.methods;
156
+ }
157
+ if (config.allowedHeaders !== void 0) {
158
+ result.allowHeaders = config.allowedHeaders;
159
+ }
160
+ if (config.exposedHeaders !== void 0) {
161
+ result.exposeHeaders = config.exposedHeaders;
162
+ }
163
+ if (config.maxAge !== void 0) {
164
+ result.maxAge = config.maxAge;
165
+ }
166
+ return result;
167
+ }
168
+ /**
169
+ * Setup core routes
170
+ */
171
+ setupCoreRoutes() {
172
+ const basePath = this.config.basePath ?? "/api/v1";
173
+ this.app.get("/health", (c) => {
174
+ return c.json({
175
+ status: "ok",
176
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
177
+ });
178
+ });
179
+ this.app.get("/health/details", async (c) => {
180
+ let dbStatus = "unknown";
181
+ try {
182
+ if (this.db.ping) {
183
+ dbStatus = await this.db.ping() ? "ok" : "error";
184
+ } else {
185
+ await this.db.execute("SELECT 1");
186
+ dbStatus = "ok";
187
+ }
188
+ } catch {
189
+ dbStatus = "error";
190
+ }
191
+ return c.json({
192
+ status: dbStatus === "ok" ? "ok" : "degraded",
193
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
194
+ services: {
195
+ database: dbStatus
196
+ },
197
+ modules: {
198
+ enabled: Array.from(this.enabledModules),
199
+ registered: Array.from(this.moduleRegistry.keys())
200
+ }
201
+ });
202
+ });
203
+ this.app.get(basePath, (c) => {
204
+ const endpoints = {
205
+ health: "/health",
206
+ features: `${basePath}/features`
207
+ };
208
+ for (const moduleName of this.enabledModules) {
209
+ endpoints[moduleName] = `${basePath}/${moduleName}`;
210
+ }
211
+ return c.json({
212
+ name: "Pars API",
213
+ version: "1.0.0",
214
+ endpoints
215
+ });
216
+ });
217
+ this.app.get(`${basePath}/features`, (c) => {
218
+ const features = Array.from(this.enabledModules).map((name) => {
219
+ const module = this.moduleRegistry.get(name);
220
+ return {
221
+ name,
222
+ version: module?.version ?? "1.0.0",
223
+ description: module?.description ?? "",
224
+ permissions: module?.permissions ?? {}
225
+ };
226
+ });
227
+ return c.json({
228
+ enabled: Array.from(this.enabledModules),
229
+ features
230
+ });
231
+ });
232
+ }
233
+ /**
234
+ * Register a module
235
+ */
236
+ registerModule(manifest) {
237
+ if (this.moduleRegistry.has(manifest.name)) {
238
+ this.logger.warn(`Module already registered: ${manifest.name}`);
239
+ return;
240
+ }
241
+ this.moduleRegistry.set(manifest.name, manifest);
242
+ this.logger.info(`Registered module: ${manifest.name}`);
243
+ }
244
+ /**
245
+ * Enable a registered module
246
+ */
247
+ async enableModule(moduleName) {
248
+ const module = this.moduleRegistry.get(moduleName);
249
+ if (!module) {
250
+ this.logger.error(`Module not found: ${moduleName}`);
251
+ return false;
252
+ }
253
+ if (this.enabledModules.has(moduleName)) {
254
+ this.logger.warn(`Module already enabled: ${moduleName}`);
255
+ return true;
256
+ }
257
+ if (module.dependencies) {
258
+ for (const dep of module.dependencies) {
259
+ if (!this.enabledModules.has(dep)) {
260
+ this.logger.error(
261
+ `Module ${moduleName} requires ${dep} to be enabled first`
262
+ );
263
+ return false;
264
+ }
265
+ }
266
+ }
267
+ try {
268
+ this.logger.info(`Enabling module: ${moduleName}`);
269
+ if (module.onEnable) {
270
+ await module.onEnable();
271
+ }
272
+ module.registerRoutes(this.app);
273
+ this.enabledModules.add(moduleName);
274
+ this.logger.info(`Enabled module: ${moduleName}`);
275
+ return true;
276
+ } catch (error2) {
277
+ this.logger.error(`Failed to enable module ${moduleName}`, error2);
278
+ return false;
279
+ }
280
+ }
281
+ /**
282
+ * Disable an enabled module
283
+ */
284
+ async disableModule(moduleName) {
285
+ if (!this.enabledModules.has(moduleName)) {
286
+ this.logger.warn(`Module not enabled: ${moduleName}`);
287
+ return true;
288
+ }
289
+ const module = this.moduleRegistry.get(moduleName);
290
+ if (!module) {
291
+ this.logger.error(`Module not found: ${moduleName}`);
292
+ return false;
293
+ }
294
+ for (const [name, m] of this.moduleRegistry) {
295
+ if (this.enabledModules.has(name) && m.dependencies?.includes(moduleName)) {
296
+ this.logger.error(
297
+ `Cannot disable ${moduleName}: ${name} depends on it`
298
+ );
299
+ return false;
300
+ }
301
+ }
302
+ try {
303
+ if (module.onDisable) {
304
+ await module.onDisable();
305
+ }
306
+ this.enabledModules.delete(moduleName);
307
+ this.logger.info(`Disabled module: ${moduleName}`);
308
+ return true;
309
+ } catch (error2) {
310
+ this.logger.error(`Failed to disable module ${moduleName}`, error2);
311
+ return false;
312
+ }
313
+ }
314
+ /**
315
+ * Get the Hono app instance
316
+ */
317
+ getApp() {
318
+ return this.app;
319
+ }
320
+ /**
321
+ * Get enabled modules
322
+ */
323
+ getEnabledModules() {
324
+ return Array.from(this.enabledModules);
325
+ }
326
+ /**
327
+ * Get registered modules
328
+ */
329
+ getRegisteredModules() {
330
+ return Array.from(this.moduleRegistry.keys());
331
+ }
332
+ /**
333
+ * Check if module is enabled
334
+ */
335
+ isModuleEnabled(moduleName) {
336
+ return this.enabledModules.has(moduleName);
337
+ }
338
+ /**
339
+ * Check if module is registered
340
+ */
341
+ isModuleRegistered(moduleName) {
342
+ return this.moduleRegistry.has(moduleName);
343
+ }
344
+ /**
345
+ * Get database adapter
346
+ */
347
+ getDatabase() {
348
+ return this.db;
349
+ }
350
+ /**
351
+ * Get logger
352
+ */
353
+ getLogger() {
354
+ return this.logger;
355
+ }
356
+ };
357
+ function createModuleLoader(options) {
358
+ return new ModuleLoader(options);
359
+ }
360
+ function defineModule(manifest) {
361
+ return manifest;
362
+ }
363
+
364
+ // src/rls.ts
365
+ var DEFAULT_RLS_CONFIG = {
366
+ tenantIdColumn: "tenant_id",
367
+ sessionVariable: "app.current_tenant_id",
368
+ enabled: true
369
+ };
370
+ var RLSManager = class {
371
+ constructor(db, config = {}) {
372
+ this.db = db;
373
+ this.config = { ...DEFAULT_RLS_CONFIG, ...config };
374
+ }
375
+ config;
376
+ /**
377
+ * Set current tenant ID in database session
378
+ * This enables RLS policies to filter by tenant
379
+ */
380
+ async setTenantId(tenantId) {
381
+ if (!this.config.enabled) return;
382
+ try {
383
+ const escapedTenantId = tenantId.replace(/'/g, "''");
384
+ await this.db.execute(`SET ${this.config.sessionVariable} = '${escapedTenantId}'`);
385
+ } catch (error2) {
386
+ console.error("Failed to set tenant ID for RLS:", error2);
387
+ throw new RLSError("Failed to set tenant context", "RLS_SET_FAILED", error2);
388
+ }
389
+ }
390
+ /**
391
+ * Clear current tenant ID from database session
392
+ */
393
+ async clearTenantId() {
394
+ if (!this.config.enabled) return;
395
+ try {
396
+ await this.db.execute(`RESET ${this.config.sessionVariable}`);
397
+ } catch (error2) {
398
+ console.error("Failed to clear tenant ID for RLS:", error2);
399
+ throw new RLSError("Failed to clear tenant context", "RLS_CLEAR_FAILED", error2);
400
+ }
401
+ }
402
+ /**
403
+ * Execute a function with tenant context
404
+ * Automatically sets and clears tenant ID
405
+ */
406
+ async withTenant(tenantId, operation) {
407
+ await this.setTenantId(tenantId);
408
+ try {
409
+ return await operation();
410
+ } finally {
411
+ await this.clearTenantId();
412
+ }
413
+ }
414
+ /**
415
+ * Check if RLS is enabled
416
+ */
417
+ isEnabled() {
418
+ return this.config.enabled;
419
+ }
420
+ /**
421
+ * Get current configuration
422
+ */
423
+ getConfig() {
424
+ return { ...this.config };
425
+ }
426
+ };
427
+ var RLSError = class extends Error {
428
+ constructor(message, code, cause) {
429
+ super(message);
430
+ this.code = code;
431
+ this.cause = cause;
432
+ this.name = "RLSError";
433
+ }
434
+ };
435
+ function createRLSManager(db, config) {
436
+ return new RLSManager(db, config);
437
+ }
438
+ function rlsMiddleware(config) {
439
+ return async (c, next) => {
440
+ const user = c.get("user");
441
+ const db = c.get("db");
442
+ if (!user?.tenantId || !db) {
443
+ return next();
444
+ }
445
+ const rls = new RLSManager(db, config);
446
+ try {
447
+ await rls.setTenantId(user.tenantId);
448
+ await next();
449
+ } finally {
450
+ await rls.clearTenantId();
451
+ }
452
+ };
453
+ }
454
+ function generateRLSPolicy(tableName, options = {}) {
455
+ const {
456
+ tenantIdColumn = "tenant_id",
457
+ sessionVariable = "app.current_tenant_id",
458
+ policyName = `${tableName}_tenant_isolation`,
459
+ castType = "uuid"
460
+ } = options;
461
+ return `
462
+ -- Enable RLS on table
463
+ ALTER TABLE ${tableName} ENABLE ROW LEVEL SECURITY;
464
+
465
+ -- Force RLS for table owner too
466
+ ALTER TABLE ${tableName} FORCE ROW LEVEL SECURITY;
467
+
468
+ -- Create tenant isolation policy
469
+ DROP POLICY IF EXISTS ${policyName} ON ${tableName};
470
+ CREATE POLICY ${policyName} ON ${tableName}
471
+ FOR ALL
472
+ USING (${tenantIdColumn} = current_setting('${sessionVariable}', true)::${castType});
473
+ `.trim();
474
+ }
475
+ function generateDisableRLS(tableName) {
476
+ return `
477
+ ALTER TABLE ${tableName} DISABLE ROW LEVEL SECURITY;
478
+ ALTER TABLE ${tableName} NO FORCE ROW LEVEL SECURITY;
479
+ `.trim();
480
+ }
481
+
482
+ // src/rbac.ts
483
+ import { ForbiddenError, UnauthorizedError } from "@parsrun/core";
484
+ var InMemoryRBAC = class {
485
+ userPermissions = /* @__PURE__ */ new Map();
486
+ userRoles = /* @__PURE__ */ new Map();
487
+ rolePermissions = /* @__PURE__ */ new Map();
488
+ tenantMembers = /* @__PURE__ */ new Map();
489
+ /**
490
+ * Grant permission to user
491
+ */
492
+ grantPermission(userId, permission) {
493
+ if (!this.userPermissions.has(userId)) {
494
+ this.userPermissions.set(userId, /* @__PURE__ */ new Set());
495
+ }
496
+ this.userPermissions.get(userId).add(permission);
497
+ }
498
+ /**
499
+ * Revoke permission from user
500
+ */
501
+ revokePermission(userId, permission) {
502
+ this.userPermissions.get(userId)?.delete(permission);
503
+ }
504
+ /**
505
+ * Assign role to user
506
+ */
507
+ assignRole(userId, role) {
508
+ if (!this.userRoles.has(userId)) {
509
+ this.userRoles.set(userId, /* @__PURE__ */ new Set());
510
+ }
511
+ this.userRoles.get(userId).add(role);
512
+ }
513
+ /**
514
+ * Remove role from user
515
+ */
516
+ removeRole(userId, role) {
517
+ this.userRoles.get(userId)?.delete(role);
518
+ }
519
+ /**
520
+ * Define role with permissions
521
+ */
522
+ defineRole(roleName, permissions) {
523
+ this.rolePermissions.set(roleName, new Set(permissions));
524
+ }
525
+ /**
526
+ * Add user to tenant
527
+ */
528
+ addTenantMember(tenantId, userId) {
529
+ if (!this.tenantMembers.has(tenantId)) {
530
+ this.tenantMembers.set(tenantId, /* @__PURE__ */ new Set());
531
+ }
532
+ this.tenantMembers.get(tenantId).add(userId);
533
+ }
534
+ /**
535
+ * Remove user from tenant
536
+ */
537
+ removeTenantMember(tenantId, userId) {
538
+ this.tenantMembers.get(tenantId)?.delete(userId);
539
+ }
540
+ // PermissionChecker implementation
541
+ async getUserPermissions(userId, _tenantId) {
542
+ const permissions = /* @__PURE__ */ new Set();
543
+ const direct = this.userPermissions.get(userId);
544
+ if (direct) {
545
+ direct.forEach((p) => permissions.add(p));
546
+ }
547
+ const roles = this.userRoles.get(userId);
548
+ if (roles) {
549
+ roles.forEach((role) => {
550
+ const rolePerms = this.rolePermissions.get(role);
551
+ if (rolePerms) {
552
+ rolePerms.forEach((p) => permissions.add(p));
553
+ }
554
+ });
555
+ }
556
+ return Array.from(permissions);
557
+ }
558
+ async getUserRoles(userId, _tenantId) {
559
+ return Array.from(this.userRoles.get(userId) ?? []);
560
+ }
561
+ async hasPermission(userId, check, tenantId) {
562
+ const permissions = await this.getUserPermissions(userId, tenantId);
563
+ const permissionName = `${check.resource}:${check.action}`;
564
+ if (permissions.includes(permissionName)) {
565
+ return true;
566
+ }
567
+ for (const perm of permissions) {
568
+ if (perm === "*") return true;
569
+ if (perm === `${check.resource}:*`) return true;
570
+ if (perm === `*:${check.action}`) return true;
571
+ }
572
+ return false;
573
+ }
574
+ async isTenantMember(userId, tenantId) {
575
+ return this.tenantMembers.get(tenantId)?.has(userId) ?? false;
576
+ }
577
+ };
578
+ var RBACService = class {
579
+ constructor(checker) {
580
+ this.checker = checker;
581
+ }
582
+ /**
583
+ * Get user's permissions
584
+ */
585
+ async getUserPermissions(userId, tenantId) {
586
+ return this.checker.getUserPermissions(userId, tenantId);
587
+ }
588
+ /**
589
+ * Get user's roles
590
+ */
591
+ async getUserRoles(userId, tenantId) {
592
+ return this.checker.getUserRoles(userId, tenantId);
593
+ }
594
+ /**
595
+ * Check if user has specific permission
596
+ */
597
+ async hasPermission(userId, check, tenantId) {
598
+ return this.checker.hasPermission(userId, check, tenantId);
599
+ }
600
+ /**
601
+ * Check if user has any of the specified permissions
602
+ */
603
+ async hasAnyPermission(userId, checks, tenantId) {
604
+ for (const check of checks) {
605
+ if (await this.hasPermission(userId, check, tenantId)) {
606
+ return true;
607
+ }
608
+ }
609
+ return false;
610
+ }
611
+ /**
612
+ * Check if user has all specified permissions
613
+ */
614
+ async hasAllPermissions(userId, checks, tenantId) {
615
+ for (const check of checks) {
616
+ if (!await this.hasPermission(userId, check, tenantId)) {
617
+ return false;
618
+ }
619
+ }
620
+ return true;
621
+ }
622
+ /**
623
+ * Check if user is member of tenant
624
+ */
625
+ async isTenantMember(userId, tenantId) {
626
+ return this.checker.isTenantMember(userId, tenantId);
627
+ }
628
+ /**
629
+ * Check if user has specific role
630
+ */
631
+ async hasRole(userId, role, tenantId) {
632
+ const roles = await this.getUserRoles(userId, tenantId);
633
+ return roles.includes(role);
634
+ }
635
+ /**
636
+ * Check if user has any of the specified roles
637
+ */
638
+ async hasAnyRole(userId, roles, tenantId) {
639
+ const userRoles = await this.getUserRoles(userId, tenantId);
640
+ return roles.some((role) => userRoles.includes(role));
641
+ }
642
+ };
643
+ function createInMemoryRBAC() {
644
+ const checker = new InMemoryRBAC();
645
+ const rbac = new RBACService(checker);
646
+ return { rbac, checker };
647
+ }
648
+ function createRBACService(checker) {
649
+ return new RBACService(checker);
650
+ }
651
+ function requireAuth() {
652
+ return async (c, next) => {
653
+ const user = c.get("user");
654
+ if (!user) {
655
+ throw new UnauthorizedError("Authentication required");
656
+ }
657
+ return next();
658
+ };
659
+ }
660
+ function requirePermission(resource, action, options = {}) {
661
+ return async (c, next) => {
662
+ const user = c.get("user");
663
+ if (!user) {
664
+ throw new UnauthorizedError("Authentication required");
665
+ }
666
+ const check = {
667
+ resource,
668
+ action,
669
+ scope: options.scope ?? "tenant"
670
+ };
671
+ let hasPermission = false;
672
+ if (options.checker) {
673
+ hasPermission = await options.checker.hasPermission(user.id, check, user.tenantId);
674
+ } else {
675
+ hasPermission = checkUserPermission(user, check);
676
+ }
677
+ if (!hasPermission) {
678
+ throw new ForbiddenError(`Permission denied: ${resource}:${action}`);
679
+ }
680
+ return next();
681
+ };
682
+ }
683
+ function requireAnyPermission(permissions, options = {}) {
684
+ return async (c, next) => {
685
+ const user = c.get("user");
686
+ if (!user) {
687
+ throw new UnauthorizedError("Authentication required");
688
+ }
689
+ let hasAny = false;
690
+ for (const perm of permissions) {
691
+ const check = { resource: perm.resource, action: perm.action };
692
+ if (options.checker) {
693
+ if (await options.checker.hasPermission(user.id, check, user.tenantId)) {
694
+ hasAny = true;
695
+ break;
696
+ }
697
+ } else {
698
+ if (checkUserPermission(user, check)) {
699
+ hasAny = true;
700
+ break;
701
+ }
702
+ }
703
+ }
704
+ if (!hasAny) {
705
+ throw new ForbiddenError("Insufficient permissions");
706
+ }
707
+ return next();
708
+ };
709
+ }
710
+ function requireRole(role) {
711
+ return async (c, next) => {
712
+ const user = c.get("user");
713
+ if (!user) {
714
+ throw new UnauthorizedError("Authentication required");
715
+ }
716
+ if (user.role !== role) {
717
+ throw new ForbiddenError(`Role required: ${role}`);
718
+ }
719
+ return next();
720
+ };
721
+ }
722
+ function requireAnyRole(roles) {
723
+ return async (c, next) => {
724
+ const user = c.get("user");
725
+ if (!user) {
726
+ throw new UnauthorizedError("Authentication required");
727
+ }
728
+ if (!user.role || !roles.includes(user.role)) {
729
+ throw new ForbiddenError(`One of these roles required: ${roles.join(", ")}`);
730
+ }
731
+ return next();
732
+ };
733
+ }
734
+ function requireTenantMember(requiredRole) {
735
+ return async (c, next) => {
736
+ const user = c.get("user");
737
+ const tenant = c.get("tenant");
738
+ if (!user) {
739
+ throw new UnauthorizedError("Authentication required");
740
+ }
741
+ if (!tenant) {
742
+ throw new ForbiddenError("Tenant context required");
743
+ }
744
+ if (user.tenantId !== tenant.id) {
745
+ throw new ForbiddenError("Not a member of this tenant");
746
+ }
747
+ if (requiredRole && user.role !== requiredRole) {
748
+ throw new ForbiddenError(`Role required in tenant: ${requiredRole}`);
749
+ }
750
+ return next();
751
+ };
752
+ }
753
+ function checkUserPermission(user, check) {
754
+ const permissionName = `${check.resource}:${check.action}`;
755
+ for (const perm of user.permissions) {
756
+ if (perm === permissionName) return true;
757
+ if (perm === "*") return true;
758
+ if (perm === `${check.resource}:*`) return true;
759
+ if (perm === `*:${check.action}`) return true;
760
+ }
761
+ return false;
762
+ }
763
+ function parsePermission(permission) {
764
+ const [resource, action] = permission.split(":");
765
+ if (!resource || !action) {
766
+ throw new Error(`Invalid permission format: ${permission}`);
767
+ }
768
+ return { resource, action };
769
+ }
770
+ function createPermission(resource, action) {
771
+ return `${resource}:${action}`;
772
+ }
773
+ function crudPermissions(resource) {
774
+ return [
775
+ { name: `${resource}:create`, resource, action: "create" },
776
+ { name: `${resource}:read`, resource, action: "read" },
777
+ { name: `${resource}:update`, resource, action: "update" },
778
+ { name: `${resource}:delete`, resource, action: "delete" },
779
+ { name: `${resource}:list`, resource, action: "list" }
780
+ ];
781
+ }
782
+ var StandardRoles = {
783
+ OWNER: {
784
+ name: "owner",
785
+ displayName: "Owner",
786
+ description: "Full access to all resources",
787
+ permissions: ["*"],
788
+ isSystem: true
789
+ },
790
+ ADMIN: {
791
+ name: "admin",
792
+ displayName: "Administrator",
793
+ description: "Administrative access",
794
+ permissions: ["*:read", "*:create", "*:update", "*:list"],
795
+ isSystem: true
796
+ },
797
+ MEMBER: {
798
+ name: "member",
799
+ displayName: "Member",
800
+ description: "Standard member access",
801
+ permissions: ["*:read", "*:list"],
802
+ isSystem: true
803
+ },
804
+ VIEWER: {
805
+ name: "viewer",
806
+ displayName: "Viewer",
807
+ description: "Read-only access",
808
+ permissions: ["*:read", "*:list"],
809
+ isSystem: true
810
+ }
811
+ };
812
+
813
+ // src/middleware/error-handler.ts
814
+ var ApiError = class extends Error {
815
+ constructor(statusCode, code, message, details) {
816
+ super(message);
817
+ this.statusCode = statusCode;
818
+ this.code = code;
819
+ this.details = details;
820
+ this.name = "ApiError";
821
+ }
822
+ toResponse() {
823
+ return error(this.code, this.message, this.details);
824
+ }
825
+ };
826
+ var BadRequestError = class extends ApiError {
827
+ constructor(message = "Bad request", details) {
828
+ super(400, "BAD_REQUEST", message, details);
829
+ this.name = "BadRequestError";
830
+ }
831
+ };
832
+ var UnauthorizedError2 = class extends ApiError {
833
+ constructor(message = "Unauthorized", details) {
834
+ super(401, "UNAUTHORIZED", message, details);
835
+ this.name = "UnauthorizedError";
836
+ }
837
+ };
838
+ var ForbiddenError2 = class extends ApiError {
839
+ constructor(message = "Forbidden", details) {
840
+ super(403, "FORBIDDEN", message, details);
841
+ this.name = "ForbiddenError";
842
+ }
843
+ };
844
+ var NotFoundError = class extends ApiError {
845
+ constructor(message = "Not found", details) {
846
+ super(404, "NOT_FOUND", message, details);
847
+ this.name = "NotFoundError";
848
+ }
849
+ };
850
+ var ConflictError = class extends ApiError {
851
+ constructor(message = "Conflict", details) {
852
+ super(409, "CONFLICT", message, details);
853
+ this.name = "ConflictError";
854
+ }
855
+ };
856
+ var ValidationError = class extends ApiError {
857
+ constructor(message = "Validation failed", details) {
858
+ super(422, "VALIDATION_ERROR", message, details);
859
+ this.name = "ValidationError";
860
+ }
861
+ };
862
+ var RateLimitError = class extends ApiError {
863
+ constructor(message = "Too many requests", retryAfter) {
864
+ super(429, "RATE_LIMIT_EXCEEDED", message, { retryAfter });
865
+ this.retryAfter = retryAfter;
866
+ this.name = "RateLimitError";
867
+ }
868
+ };
869
+ var InternalError = class extends ApiError {
870
+ constructor(message = "Internal server error", details) {
871
+ super(500, "INTERNAL_ERROR", message, details);
872
+ this.name = "InternalError";
873
+ }
874
+ };
875
+ var ServiceUnavailableError = class extends ApiError {
876
+ constructor(message = "Service unavailable", details) {
877
+ super(503, "SERVICE_UNAVAILABLE", message, details);
878
+ this.name = "ServiceUnavailableError";
879
+ }
880
+ };
881
+ function errorHandler(options = {}) {
882
+ const {
883
+ includeStack = false,
884
+ onError,
885
+ errorTransport,
886
+ captureAllErrors = false,
887
+ shouldCapture
888
+ } = options;
889
+ return async (c, next) => {
890
+ try {
891
+ await next();
892
+ } catch (err) {
893
+ const error2 = err instanceof Error ? err : new Error(String(err));
894
+ const statusCode = error2 instanceof ApiError ? error2.statusCode : 500;
895
+ if (onError) {
896
+ onError(error2, c);
897
+ } else {
898
+ const logger = c.get("logger");
899
+ if (logger) {
900
+ logger.error("Request error", {
901
+ requestId: c.get("requestId"),
902
+ error: error2.message,
903
+ stack: error2.stack
904
+ });
905
+ }
906
+ }
907
+ if (errorTransport) {
908
+ const shouldCaptureError = shouldCapture ? shouldCapture(error2, statusCode) : captureAllErrors || statusCode >= 500;
909
+ if (shouldCaptureError) {
910
+ const user = c.get("user");
911
+ const tenant = c.get("tenant");
912
+ const errorContext = {
913
+ requestId: c.get("requestId"),
914
+ tags: {
915
+ path: c.req.path,
916
+ method: c.req.method,
917
+ statusCode: String(statusCode)
918
+ }
919
+ };
920
+ if (user?.id) {
921
+ errorContext.userId = user.id;
922
+ }
923
+ if (tenant?.id) {
924
+ errorContext.tenantId = tenant.id;
925
+ }
926
+ const extra = {
927
+ query: c.req.query()
928
+ };
929
+ if (error2 instanceof ApiError) {
930
+ extra["errorCode"] = error2.code;
931
+ }
932
+ errorContext.extra = extra;
933
+ Promise.resolve(
934
+ errorTransport.captureException(error2, errorContext)
935
+ ).catch(() => {
936
+ });
937
+ }
938
+ }
939
+ if (error2 instanceof ApiError) {
940
+ return c.json(error2.toResponse(), error2.statusCode);
941
+ }
942
+ const details = {};
943
+ if (includeStack && error2.stack) {
944
+ details["stack"] = error2.stack;
945
+ }
946
+ return c.json(
947
+ error("INTERNAL_ERROR", "An unexpected error occurred", details),
948
+ 500
949
+ );
950
+ }
951
+ };
952
+ }
953
+ function notFoundHandler(c) {
954
+ return c.json(
955
+ error("NOT_FOUND", `Route ${c.req.method} ${c.req.path} not found`),
956
+ 404
957
+ );
958
+ }
959
+
960
+ // src/middleware/auth.ts
961
+ function extractToken(c, header, prefix, cookie) {
962
+ const authHeader = c.req.header(header);
963
+ if (authHeader) {
964
+ if (prefix && authHeader.startsWith(`${prefix} `)) {
965
+ return authHeader.slice(prefix.length + 1);
966
+ }
967
+ return authHeader;
968
+ }
969
+ if (cookie) {
970
+ const cookieHeader = c.req.header("cookie");
971
+ if (cookieHeader) {
972
+ const cookies = cookieHeader.split(";").map((c2) => c2.trim());
973
+ for (const c2 of cookies) {
974
+ const [key, ...valueParts] = c2.split("=");
975
+ if (key === cookie) {
976
+ return valueParts.join("=");
977
+ }
978
+ }
979
+ }
980
+ }
981
+ return null;
982
+ }
983
+ function auth(options) {
984
+ const {
985
+ verify,
986
+ header = "authorization",
987
+ prefix = "Bearer",
988
+ cookie,
989
+ skip,
990
+ message = "Authentication required"
991
+ } = options;
992
+ return async (c, next) => {
993
+ if (skip?.(c)) {
994
+ return next();
995
+ }
996
+ const token = extractToken(c, header, prefix, cookie);
997
+ if (!token) {
998
+ throw new UnauthorizedError2(message);
999
+ }
1000
+ const payload = await verify(token);
1001
+ if (!payload) {
1002
+ throw new UnauthorizedError2("Invalid or expired token");
1003
+ }
1004
+ const user = {
1005
+ id: payload.sub,
1006
+ email: payload.email,
1007
+ tenantId: payload.tenantId,
1008
+ role: payload.role,
1009
+ permissions: payload.permissions ?? []
1010
+ };
1011
+ c.set("user", user);
1012
+ await next();
1013
+ };
1014
+ }
1015
+ function optionalAuth(options) {
1016
+ const { verify, header = "authorization", prefix = "Bearer", cookie, skip } = options;
1017
+ return async (c, next) => {
1018
+ if (skip?.(c)) {
1019
+ return next();
1020
+ }
1021
+ const token = extractToken(c, header, prefix, cookie);
1022
+ if (token) {
1023
+ try {
1024
+ const payload = await verify(token);
1025
+ if (payload) {
1026
+ const user = {
1027
+ id: payload.sub,
1028
+ email: payload.email,
1029
+ tenantId: payload.tenantId,
1030
+ role: payload.role,
1031
+ permissions: payload.permissions ?? []
1032
+ };
1033
+ c.set("user", user);
1034
+ }
1035
+ } catch {
1036
+ }
1037
+ }
1038
+ await next();
1039
+ };
1040
+ }
1041
+ function createAuthMiddleware(baseOptions) {
1042
+ return {
1043
+ auth: (options) => auth({ ...baseOptions, ...options }),
1044
+ optionalAuth: (options) => optionalAuth({ ...baseOptions, ...options })
1045
+ };
1046
+ }
1047
+
1048
+ // src/middleware/cors.ts
1049
+ var defaultCorsConfig = {
1050
+ origin: "*",
1051
+ credentials: false,
1052
+ methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
1053
+ allowedHeaders: ["Content-Type", "Authorization", "X-Request-ID", "X-CSRF-Token"],
1054
+ exposedHeaders: ["X-Request-ID", "X-Total-Count"],
1055
+ maxAge: 86400
1056
+ // 24 hours
1057
+ };
1058
+ function isOriginAllowed(origin, config) {
1059
+ if (config.origin === "*") return true;
1060
+ if (typeof config.origin === "string") {
1061
+ return origin === config.origin;
1062
+ }
1063
+ if (Array.isArray(config.origin)) {
1064
+ return config.origin.includes(origin);
1065
+ }
1066
+ if (typeof config.origin === "function") {
1067
+ return config.origin(origin);
1068
+ }
1069
+ return false;
1070
+ }
1071
+ function cors2(config) {
1072
+ const corsConfig2 = { ...defaultCorsConfig, ...config };
1073
+ return async (c, next) => {
1074
+ const origin = c.req.header("origin") ?? "";
1075
+ if (c.req.method === "OPTIONS") {
1076
+ const response = new Response(null, { status: 204 });
1077
+ if (isOriginAllowed(origin, corsConfig2)) {
1078
+ response.headers.set("Access-Control-Allow-Origin", origin || "*");
1079
+ }
1080
+ if (corsConfig2.credentials) {
1081
+ response.headers.set("Access-Control-Allow-Credentials", "true");
1082
+ }
1083
+ if (corsConfig2.methods) {
1084
+ response.headers.set(
1085
+ "Access-Control-Allow-Methods",
1086
+ corsConfig2.methods.join(", ")
1087
+ );
1088
+ }
1089
+ if (corsConfig2.allowedHeaders) {
1090
+ response.headers.set(
1091
+ "Access-Control-Allow-Headers",
1092
+ corsConfig2.allowedHeaders.join(", ")
1093
+ );
1094
+ }
1095
+ if (corsConfig2.maxAge) {
1096
+ response.headers.set("Access-Control-Max-Age", String(corsConfig2.maxAge));
1097
+ }
1098
+ return response;
1099
+ }
1100
+ await next();
1101
+ if (isOriginAllowed(origin, corsConfig2)) {
1102
+ c.header("Access-Control-Allow-Origin", origin || "*");
1103
+ }
1104
+ if (corsConfig2.credentials) {
1105
+ c.header("Access-Control-Allow-Credentials", "true");
1106
+ }
1107
+ if (corsConfig2.exposedHeaders) {
1108
+ c.header("Access-Control-Expose-Headers", corsConfig2.exposedHeaders.join(", "));
1109
+ }
1110
+ };
1111
+ }
1112
+
1113
+ // src/middleware/csrf.ts
1114
+ function generateRandomToken() {
1115
+ const bytes = new Uint8Array(32);
1116
+ crypto.getRandomValues(bytes);
1117
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1118
+ }
1119
+ function getCookie(c, name) {
1120
+ const cookieHeader = c.req.header("cookie");
1121
+ if (!cookieHeader) return void 0;
1122
+ const cookies = cookieHeader.split(";").map((c2) => c2.trim());
1123
+ for (const cookie of cookies) {
1124
+ const [key, ...valueParts] = cookie.split("=");
1125
+ if (key === name) {
1126
+ return valueParts.join("=");
1127
+ }
1128
+ }
1129
+ return void 0;
1130
+ }
1131
+ function csrf(options = {}) {
1132
+ const {
1133
+ cookieName = "_csrf",
1134
+ headerName = "x-csrf-token",
1135
+ methods = ["POST", "PUT", "PATCH", "DELETE"],
1136
+ excludePaths = [],
1137
+ skip,
1138
+ generateToken = generateRandomToken,
1139
+ cookie = {}
1140
+ } = options;
1141
+ const cookieOptions = {
1142
+ secure: cookie.secure ?? true,
1143
+ httpOnly: cookie.httpOnly ?? true,
1144
+ sameSite: cookie.sameSite ?? "lax",
1145
+ path: cookie.path ?? "/",
1146
+ maxAge: cookie.maxAge ?? 86400
1147
+ // 24 hours
1148
+ };
1149
+ return async (c, next) => {
1150
+ if (skip?.(c)) {
1151
+ return next();
1152
+ }
1153
+ const path = c.req.path;
1154
+ if (excludePaths.some((p) => path.startsWith(p))) {
1155
+ return next();
1156
+ }
1157
+ let token = getCookie(c, cookieName);
1158
+ if (!token) {
1159
+ token = generateToken();
1160
+ const cookieValue = [
1161
+ `${cookieName}=${token}`,
1162
+ `Path=${cookieOptions.path}`,
1163
+ `Max-Age=${cookieOptions.maxAge}`,
1164
+ cookieOptions.sameSite && `SameSite=${cookieOptions.sameSite}`,
1165
+ cookieOptions.secure && "Secure",
1166
+ cookieOptions.httpOnly && "HttpOnly"
1167
+ ].filter(Boolean).join("; ");
1168
+ c.header("Set-Cookie", cookieValue);
1169
+ }
1170
+ c.set("csrfToken", token);
1171
+ if (methods.includes(c.req.method)) {
1172
+ const headerToken = c.req.header(headerName);
1173
+ const bodyToken = await getBodyToken(c);
1174
+ const providedToken = headerToken ?? bodyToken;
1175
+ if (!providedToken || providedToken !== token) {
1176
+ throw new ForbiddenError2("Invalid CSRF token");
1177
+ }
1178
+ }
1179
+ await next();
1180
+ };
1181
+ }
1182
+ async function getBodyToken(c) {
1183
+ try {
1184
+ const contentType = c.req.header("content-type") ?? "";
1185
+ if (contentType.includes("application/json")) {
1186
+ const body = await c.req.json();
1187
+ return body["_csrf"] ?? body["csrfToken"] ?? body["csrf_token"];
1188
+ }
1189
+ if (contentType.includes("application/x-www-form-urlencoded")) {
1190
+ const body = await c.req.parseBody();
1191
+ return body["_csrf"];
1192
+ }
1193
+ } catch {
1194
+ }
1195
+ return void 0;
1196
+ }
1197
+ function doubleSubmitCookie(options = {}) {
1198
+ return csrf({
1199
+ ...options,
1200
+ cookie: {
1201
+ ...options.cookie,
1202
+ httpOnly: false
1203
+ // Allow JS to read the cookie
1204
+ }
1205
+ });
1206
+ }
1207
+
1208
+ // src/middleware/rate-limit.ts
1209
+ var MemoryRateLimitStorage = class {
1210
+ store = /* @__PURE__ */ new Map();
1211
+ async get(key) {
1212
+ const entry = this.store.get(key);
1213
+ if (!entry || entry.expires < Date.now()) {
1214
+ return 0;
1215
+ }
1216
+ return entry.count;
1217
+ }
1218
+ async increment(key, windowMs) {
1219
+ const now = Date.now();
1220
+ const entry = this.store.get(key);
1221
+ if (!entry || entry.expires < now) {
1222
+ this.store.set(key, { count: 1, expires: now + windowMs });
1223
+ return 1;
1224
+ }
1225
+ entry.count++;
1226
+ return entry.count;
1227
+ }
1228
+ async reset(key) {
1229
+ this.store.delete(key);
1230
+ }
1231
+ /** Clean up expired entries */
1232
+ cleanup() {
1233
+ const now = Date.now();
1234
+ for (const [key, entry] of this.store) {
1235
+ if (entry.expires < now) {
1236
+ this.store.delete(key);
1237
+ }
1238
+ }
1239
+ }
1240
+ };
1241
+ var defaultStorage = null;
1242
+ function getDefaultStorage() {
1243
+ if (!defaultStorage) {
1244
+ defaultStorage = new MemoryRateLimitStorage();
1245
+ }
1246
+ return defaultStorage;
1247
+ }
1248
+ function rateLimit(options = {}) {
1249
+ const {
1250
+ windowMs = 60 * 1e3,
1251
+ // 1 minute
1252
+ max = 100,
1253
+ keyGenerator = defaultKeyGenerator,
1254
+ skip,
1255
+ storage = getDefaultStorage(),
1256
+ message = "Too many requests, please try again later",
1257
+ headers = true,
1258
+ onLimitReached
1259
+ } = options;
1260
+ return async (c, next) => {
1261
+ if (skip?.(c)) {
1262
+ return next();
1263
+ }
1264
+ const key = `ratelimit:${keyGenerator(c)}`;
1265
+ const current = await storage.increment(key, windowMs);
1266
+ if (headers) {
1267
+ c.header("X-RateLimit-Limit", String(max));
1268
+ c.header("X-RateLimit-Remaining", String(Math.max(0, max - current)));
1269
+ c.header("X-RateLimit-Reset", String(Math.ceil((Date.now() + windowMs) / 1e3)));
1270
+ }
1271
+ if (current > max) {
1272
+ if (onLimitReached) {
1273
+ onLimitReached(c, key);
1274
+ }
1275
+ const retryAfter = Math.ceil(windowMs / 1e3);
1276
+ c.header("Retry-After", String(retryAfter));
1277
+ throw new RateLimitError(message, retryAfter);
1278
+ }
1279
+ await next();
1280
+ };
1281
+ }
1282
+ function defaultKeyGenerator(c) {
1283
+ return c.req.header("x-forwarded-for")?.split(",")[0]?.trim() ?? c.req.header("x-real-ip") ?? c.req.header("cf-connecting-ip") ?? "unknown";
1284
+ }
1285
+ function createRateLimiter(options = {}) {
1286
+ const storage = options.storage ?? getDefaultStorage();
1287
+ return {
1288
+ middleware: rateLimit({ ...options, storage }),
1289
+ storage,
1290
+ reset: (key) => storage.reset(`ratelimit:${key}`),
1291
+ get: (key) => storage.get(`ratelimit:${key}`)
1292
+ };
1293
+ }
1294
+
1295
+ // src/middleware/request-logger.ts
1296
+ function formatBytes(bytes) {
1297
+ if (bytes < 1024) return `${bytes}B`;
1298
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
1299
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
1300
+ }
1301
+ function requestLogger(options = {}) {
1302
+ const {
1303
+ skip,
1304
+ format = "json",
1305
+ includeBody = false,
1306
+ maxBodyLength = 1e3
1307
+ } = options;
1308
+ return async (c, next) => {
1309
+ if (skip?.(c)) {
1310
+ return next();
1311
+ }
1312
+ const start = Date.now();
1313
+ const logger = c.get("logger");
1314
+ const requestId = c.get("requestId");
1315
+ const method = c.req.method;
1316
+ const path = c.req.path;
1317
+ const query = c.req.query();
1318
+ const userAgent = c.req.header("user-agent");
1319
+ const ip = c.req.header("x-forwarded-for") ?? c.req.header("x-real-ip") ?? "unknown";
1320
+ if (format === "json") {
1321
+ logger?.debug("Request started", {
1322
+ requestId,
1323
+ method,
1324
+ path,
1325
+ query: Object.keys(query).length > 0 ? query : void 0,
1326
+ ip,
1327
+ userAgent
1328
+ });
1329
+ }
1330
+ let requestBody;
1331
+ if (includeBody && ["POST", "PUT", "PATCH"].includes(method)) {
1332
+ try {
1333
+ const contentType = c.req.header("content-type") ?? "";
1334
+ if (contentType.includes("application/json")) {
1335
+ const body = await c.req.text();
1336
+ requestBody = body.length > maxBodyLength ? body.substring(0, maxBodyLength) + "..." : body;
1337
+ }
1338
+ } catch {
1339
+ }
1340
+ }
1341
+ await next();
1342
+ const duration = Date.now() - start;
1343
+ const status2 = c.res.status;
1344
+ const contentLength = c.res.headers.get("content-length");
1345
+ const size = contentLength ? parseInt(contentLength, 10) : 0;
1346
+ if (format === "json") {
1347
+ const logData = {
1348
+ requestId,
1349
+ method,
1350
+ path,
1351
+ status: status2,
1352
+ duration: `${duration}ms`,
1353
+ size: formatBytes(size)
1354
+ };
1355
+ if (requestBody) {
1356
+ logData["requestBody"] = requestBody;
1357
+ }
1358
+ if (status2 >= 500) {
1359
+ logger?.error("Request completed", logData);
1360
+ } else if (status2 >= 400) {
1361
+ logger?.warn("Request completed", logData);
1362
+ } else {
1363
+ logger?.info("Request completed", logData);
1364
+ }
1365
+ } else if (format === "combined") {
1366
+ const log = `${ip} - - [${(/* @__PURE__ */ new Date()).toISOString()}] "${method} ${path}" ${status2} ${size} "-" "${userAgent}" ${duration}ms`;
1367
+ console.log(log);
1368
+ } else {
1369
+ const log = `${method} ${path} ${status2} ${duration}ms`;
1370
+ console.log(log);
1371
+ }
1372
+ };
1373
+ }
1374
+
1375
+ // src/middleware/usage-tracking.ts
1376
+ function usageTracking(options) {
1377
+ const {
1378
+ usageService,
1379
+ featureKey = "api_calls",
1380
+ quantity = 1,
1381
+ skip,
1382
+ trackOn = "response",
1383
+ successOnly = true,
1384
+ getCustomerId = (c) => c.get("user")?.id,
1385
+ getTenantId = (c) => c.get("tenant")?.id ?? c.get("user")?.tenantId,
1386
+ getSubscriptionId,
1387
+ includeMetadata = true,
1388
+ getIdempotencyKey
1389
+ } = options;
1390
+ return async (c, next) => {
1391
+ if (trackOn === "request") {
1392
+ await trackUsage(c);
1393
+ return next();
1394
+ }
1395
+ await next();
1396
+ if (skip?.(c)) return;
1397
+ if (successOnly && c.res.status >= 400) return;
1398
+ await trackUsage(c);
1399
+ };
1400
+ async function trackUsage(c) {
1401
+ const customerId = getCustomerId(c);
1402
+ const tenantId = getTenantId(c);
1403
+ if (!customerId || !tenantId) return;
1404
+ const resolvedFeatureKey = typeof featureKey === "function" ? featureKey(c) : featureKey;
1405
+ const resolvedQuantity = typeof quantity === "function" ? quantity(c) : quantity;
1406
+ const metadata = includeMetadata ? {
1407
+ path: c.req.path,
1408
+ method: c.req.method,
1409
+ statusCode: c.res.status,
1410
+ userAgent: c.req.header("user-agent")
1411
+ } : void 0;
1412
+ try {
1413
+ const trackOptions = {
1414
+ tenantId,
1415
+ customerId,
1416
+ featureKey: resolvedFeatureKey,
1417
+ quantity: resolvedQuantity
1418
+ };
1419
+ const subscriptionId = getSubscriptionId?.(c);
1420
+ if (subscriptionId !== void 0) {
1421
+ trackOptions.subscriptionId = subscriptionId;
1422
+ }
1423
+ if (metadata !== void 0) {
1424
+ trackOptions.metadata = metadata;
1425
+ }
1426
+ const idempotencyKey = getIdempotencyKey?.(c);
1427
+ if (idempotencyKey !== void 0) {
1428
+ trackOptions.idempotencyKey = idempotencyKey;
1429
+ }
1430
+ await usageService.trackUsage(trackOptions);
1431
+ } catch (error2) {
1432
+ const logger = c.get("logger");
1433
+ if (logger) {
1434
+ logger.error("Usage tracking failed", {
1435
+ error: error2 instanceof Error ? error2.message : String(error2),
1436
+ customerId,
1437
+ featureKey: resolvedFeatureKey
1438
+ });
1439
+ }
1440
+ }
1441
+ }
1442
+ }
1443
+ function createUsageTracking(baseOptions) {
1444
+ return (overrides) => {
1445
+ return usageTracking({ ...baseOptions, ...overrides });
1446
+ };
1447
+ }
1448
+
1449
+ // src/middleware/quota-enforcement.ts
1450
+ var QuotaExceededError = class extends Error {
1451
+ constructor(featureKey, limit, currentUsage, requestedQuantity = 1) {
1452
+ super(
1453
+ `Quota exceeded for "${featureKey}": ${currentUsage}/${limit ?? "unlimited"} used`
1454
+ );
1455
+ this.featureKey = featureKey;
1456
+ this.limit = limit;
1457
+ this.currentUsage = currentUsage;
1458
+ this.requestedQuantity = requestedQuantity;
1459
+ this.name = "QuotaExceededError";
1460
+ }
1461
+ statusCode = 429;
1462
+ code = "QUOTA_EXCEEDED";
1463
+ };
1464
+ function quotaEnforcement(options) {
1465
+ const {
1466
+ quotaManager,
1467
+ featureKey,
1468
+ quantity = 1,
1469
+ skip,
1470
+ getCustomerId = (c) => c.get("user")?.id,
1471
+ includeHeaders = true,
1472
+ onQuotaExceeded,
1473
+ softLimit = false,
1474
+ onQuotaWarning
1475
+ } = options;
1476
+ return async (c, next) => {
1477
+ if (skip?.(c)) {
1478
+ return next();
1479
+ }
1480
+ const customerId = getCustomerId(c);
1481
+ if (!customerId) {
1482
+ return next();
1483
+ }
1484
+ const resolvedFeatureKey = typeof featureKey === "function" ? featureKey(c) : featureKey;
1485
+ const resolvedQuantity = typeof quantity === "function" ? quantity(c) : quantity;
1486
+ try {
1487
+ const result = await quotaManager.checkQuota(
1488
+ customerId,
1489
+ resolvedFeatureKey,
1490
+ resolvedQuantity
1491
+ );
1492
+ if (includeHeaders) {
1493
+ c.header("X-Quota-Limit", String(result.limit ?? "unlimited"));
1494
+ c.header("X-Quota-Remaining", String(result.remaining ?? "unlimited"));
1495
+ c.header("X-Quota-Used", String(result.currentUsage));
1496
+ if (result.percentAfter !== null) {
1497
+ c.header("X-Quota-Percent", String(result.percentAfter));
1498
+ }
1499
+ }
1500
+ if (result.percentAfter !== null && result.percentAfter >= 80 && onQuotaWarning) {
1501
+ onQuotaWarning(c, result, resolvedFeatureKey);
1502
+ }
1503
+ if (!result.allowed && !softLimit) {
1504
+ if (onQuotaExceeded) {
1505
+ const response = onQuotaExceeded(c, result, resolvedFeatureKey);
1506
+ if (response) return response;
1507
+ }
1508
+ throw new QuotaExceededError(
1509
+ resolvedFeatureKey,
1510
+ result.limit,
1511
+ result.currentUsage,
1512
+ resolvedQuantity
1513
+ );
1514
+ }
1515
+ await next();
1516
+ } catch (error2) {
1517
+ if (error2 instanceof QuotaExceededError) {
1518
+ throw error2;
1519
+ }
1520
+ const logger = c.get("logger");
1521
+ if (logger) {
1522
+ logger.error("Quota check failed", {
1523
+ error: error2 instanceof Error ? error2.message : String(error2),
1524
+ customerId,
1525
+ featureKey: resolvedFeatureKey
1526
+ });
1527
+ }
1528
+ await next();
1529
+ }
1530
+ };
1531
+ }
1532
+ function createQuotaEnforcement(baseOptions) {
1533
+ return (featureKey) => {
1534
+ return quotaEnforcement({ ...baseOptions, featureKey });
1535
+ };
1536
+ }
1537
+ function multiQuotaEnforcement(options) {
1538
+ const { features } = options;
1539
+ return async (c, next) => {
1540
+ const customerId = (options.getCustomerId ?? ((ctx) => ctx.get("user")?.id))(c);
1541
+ if (!customerId || options.skip?.(c)) {
1542
+ return next();
1543
+ }
1544
+ for (const feature of features) {
1545
+ const resolvedQuantity = typeof feature.quantity === "function" ? feature.quantity(c) : feature.quantity ?? 1;
1546
+ const result = await options.quotaManager.checkQuota(
1547
+ customerId,
1548
+ feature.featureKey,
1549
+ resolvedQuantity
1550
+ );
1551
+ if (!result.allowed && !options.softLimit) {
1552
+ if (options.onQuotaExceeded) {
1553
+ const response = options.onQuotaExceeded(c, result, feature.featureKey);
1554
+ if (response) return response;
1555
+ }
1556
+ throw new QuotaExceededError(
1557
+ feature.featureKey,
1558
+ result.limit,
1559
+ result.currentUsage,
1560
+ resolvedQuantity
1561
+ );
1562
+ }
1563
+ }
1564
+ await next();
1565
+ };
1566
+ }
1567
+
1568
+ // src/validation/index.ts
1569
+ import {
1570
+ type,
1571
+ uuid,
1572
+ timestamp,
1573
+ email,
1574
+ url,
1575
+ nonEmptyString,
1576
+ positiveInt,
1577
+ nonNegativeInt,
1578
+ status,
1579
+ pagination,
1580
+ paginationMeta,
1581
+ cursorPagination,
1582
+ cursorPaginationMeta,
1583
+ uuidParam,
1584
+ paginationQuery,
1585
+ cursorPaginationQuery,
1586
+ searchQuery,
1587
+ dateRangeQuery,
1588
+ healthResponse,
1589
+ apiInfoResponse,
1590
+ corsConfig,
1591
+ serverRateLimitConfig,
1592
+ loggerConfig,
1593
+ serverConfig,
1594
+ authContext,
1595
+ requestContext,
1596
+ successResponse,
1597
+ errorResponse,
1598
+ paginatedResponse,
1599
+ cursorPaginatedResponse,
1600
+ parsError,
1601
+ validateWithSchema,
1602
+ safeValidate,
1603
+ isValid,
1604
+ formatErrors
1605
+ } from "@parsrun/types";
1606
+ import { type as type2, formatErrors as formatArkErrors } from "@parsrun/types";
1607
+ import {
1608
+ uuidParam as uuidParam2,
1609
+ paginationQuery as paginationQuery2,
1610
+ searchQuery as searchQuery2,
1611
+ dateRangeQuery as dateRangeQuery2
1612
+ } from "@parsrun/types";
1613
+ function formatValidationErrors(errors) {
1614
+ const formatted = formatArkErrors(errors);
1615
+ const result = {};
1616
+ for (const [key, value] of Object.entries(formatted)) {
1617
+ result[key] = [value];
1618
+ }
1619
+ return result;
1620
+ }
1621
+ function validateBody(schema, options = {}) {
1622
+ return async (c, next) => {
1623
+ let body;
1624
+ try {
1625
+ body = await c.req.json();
1626
+ } catch {
1627
+ throw new ValidationError("Invalid JSON body");
1628
+ }
1629
+ const result = schema(body);
1630
+ if (result instanceof type2.errors) {
1631
+ throw new ValidationError(
1632
+ options.messagePrefix ?? "Validation failed",
1633
+ { errors: formatValidationErrors(result) }
1634
+ );
1635
+ }
1636
+ c.set("validatedBody", result);
1637
+ await next();
1638
+ };
1639
+ }
1640
+ function validateQuery(schema, options = {}) {
1641
+ return async (c, next) => {
1642
+ const query = c.req.query();
1643
+ const result = schema(query);
1644
+ if (result instanceof type2.errors) {
1645
+ throw new ValidationError(
1646
+ options.messagePrefix ?? "Invalid query parameters",
1647
+ { errors: formatValidationErrors(result) }
1648
+ );
1649
+ }
1650
+ c.set("validatedQuery", result);
1651
+ await next();
1652
+ };
1653
+ }
1654
+ function validateParams(schema, options = {}) {
1655
+ return async (c, next) => {
1656
+ const params = c.req.param();
1657
+ const result = schema(params);
1658
+ if (result instanceof type2.errors) {
1659
+ throw new ValidationError(
1660
+ options.messagePrefix ?? "Invalid route parameters",
1661
+ { errors: formatValidationErrors(result) }
1662
+ );
1663
+ }
1664
+ c.set("validatedParams", result);
1665
+ await next();
1666
+ };
1667
+ }
1668
+ function validateHeaders(schema, options = {}) {
1669
+ return async (c, next) => {
1670
+ const headers = {};
1671
+ c.req.raw.headers.forEach((value, key) => {
1672
+ headers[key.toLowerCase()] = value;
1673
+ });
1674
+ const result = schema(headers);
1675
+ if (result instanceof type2.errors) {
1676
+ throw new ValidationError(
1677
+ options.messagePrefix ?? "Invalid headers",
1678
+ { errors: formatValidationErrors(result) }
1679
+ );
1680
+ }
1681
+ c.set("validatedHeaders", result);
1682
+ await next();
1683
+ };
1684
+ }
1685
+ function validate(schemas) {
1686
+ return async (c, next) => {
1687
+ if (schemas.params) {
1688
+ const params = c.req.param();
1689
+ const result = schemas.params(params);
1690
+ if (result instanceof type2.errors) {
1691
+ throw new ValidationError("Invalid route parameters", {
1692
+ errors: formatValidationErrors(result)
1693
+ });
1694
+ }
1695
+ c.set("validatedParams", result);
1696
+ }
1697
+ if (schemas.query) {
1698
+ const query = c.req.query();
1699
+ const result = schemas.query(query);
1700
+ if (result instanceof type2.errors) {
1701
+ throw new ValidationError("Invalid query parameters", {
1702
+ errors: formatValidationErrors(result)
1703
+ });
1704
+ }
1705
+ c.set("validatedQuery", result);
1706
+ }
1707
+ if (schemas.headers) {
1708
+ const headers = {};
1709
+ c.req.raw.headers.forEach((value, key) => {
1710
+ headers[key.toLowerCase()] = value;
1711
+ });
1712
+ const result = schemas.headers(headers);
1713
+ if (result instanceof type2.errors) {
1714
+ throw new ValidationError("Invalid headers", {
1715
+ errors: formatValidationErrors(result)
1716
+ });
1717
+ }
1718
+ c.set("validatedHeaders", result);
1719
+ }
1720
+ if (schemas.body) {
1721
+ let body;
1722
+ try {
1723
+ body = await c.req.json();
1724
+ } catch {
1725
+ throw new ValidationError("Invalid JSON body");
1726
+ }
1727
+ const result = schemas.body(body);
1728
+ if (result instanceof type2.errors) {
1729
+ throw new ValidationError("Validation failed", {
1730
+ errors: formatValidationErrors(result)
1731
+ });
1732
+ }
1733
+ c.set("validatedBody", result);
1734
+ }
1735
+ await next();
1736
+ };
1737
+ }
1738
+
1739
+ // src/utils/pagination.ts
1740
+ var defaultOptions = {
1741
+ defaultLimit: 20,
1742
+ maxLimit: 100,
1743
+ pageParam: "page",
1744
+ limitParam: "limit"
1745
+ };
1746
+ function parsePagination(c, options = {}) {
1747
+ const opts = { ...defaultOptions, ...options };
1748
+ const pageStr = c.req.query(opts.pageParam);
1749
+ const limitStr = c.req.query(opts.limitParam);
1750
+ let page = pageStr ? parseInt(pageStr, 10) : 1;
1751
+ let limit = limitStr ? parseInt(limitStr, 10) : opts.defaultLimit;
1752
+ if (isNaN(page) || page < 1) page = 1;
1753
+ if (isNaN(limit) || limit < 1) limit = opts.defaultLimit;
1754
+ if (limit > opts.maxLimit) limit = opts.maxLimit;
1755
+ const offset = (page - 1) * limit;
1756
+ return { page, limit, offset };
1757
+ }
1758
+ function createPaginationMeta(params) {
1759
+ const { page, limit, total } = params;
1760
+ const totalPages = Math.ceil(total / limit);
1761
+ return {
1762
+ page,
1763
+ limit,
1764
+ total,
1765
+ totalPages,
1766
+ hasNext: page < totalPages,
1767
+ hasPrev: page > 1
1768
+ };
1769
+ }
1770
+ function paginate(data, params) {
1771
+ return {
1772
+ data,
1773
+ pagination: createPaginationMeta(params)
1774
+ };
1775
+ }
1776
+ function parseCursorPagination(c, options = {}) {
1777
+ const opts = { ...defaultOptions, ...options };
1778
+ const cursor = c.req.query("cursor") ?? void 0;
1779
+ const limitStr = c.req.query(opts.limitParam);
1780
+ const direction = c.req.query("direction") === "backward" ? "backward" : "forward";
1781
+ let limit = limitStr ? parseInt(limitStr, 10) : opts.defaultLimit;
1782
+ if (isNaN(limit) || limit < 1) limit = opts.defaultLimit;
1783
+ if (limit > opts.maxLimit) limit = opts.maxLimit;
1784
+ return { cursor, limit, direction };
1785
+ }
1786
+ function cursorPaginate(data, params) {
1787
+ const { cursor, limit } = params;
1788
+ const hasMore = data.length > limit;
1789
+ const items = hasMore ? data.slice(0, limit) : data;
1790
+ const lastItem = items[items.length - 1];
1791
+ const firstItem = items[0];
1792
+ return {
1793
+ data: items,
1794
+ pagination: {
1795
+ cursor,
1796
+ nextCursor: hasMore && lastItem ? lastItem.id : void 0,
1797
+ prevCursor: cursor && firstItem ? firstItem.id : void 0,
1798
+ hasMore,
1799
+ limit
1800
+ }
1801
+ };
1802
+ }
1803
+ function setPaginationHeaders(c, meta) {
1804
+ c.header("X-Total-Count", String(meta.total));
1805
+ c.header("X-Total-Pages", String(meta.totalPages));
1806
+ c.header("X-Page", String(meta.page));
1807
+ c.header("X-Per-Page", String(meta.limit));
1808
+ c.header("X-Has-Next", String(meta.hasNext));
1809
+ c.header("X-Has-Prev", String(meta.hasPrev));
1810
+ }
1811
+
1812
+ // src/utils/response.ts
1813
+ function json(c, data, status2 = 200) {
1814
+ return c.json(success(data), status2);
1815
+ }
1816
+ function jsonWithMeta(c, data, meta, status2 = 200) {
1817
+ return c.json(success(data, meta), status2);
1818
+ }
1819
+ function jsonError(c, code, message, status2 = 400, details) {
1820
+ return c.json(error(code, message, details), status2);
1821
+ }
1822
+ function created(c, data, location) {
1823
+ if (location) {
1824
+ c.header("Location", location);
1825
+ }
1826
+ return c.json(success(data), 201);
1827
+ }
1828
+ function noContent(_c) {
1829
+ return new Response(null, { status: 204 });
1830
+ }
1831
+ function accepted(c, data) {
1832
+ if (data) {
1833
+ return c.json(success(data), 202);
1834
+ }
1835
+ return new Response(null, { status: 202 });
1836
+ }
1837
+ function redirect(c, url2, status2 = 302) {
1838
+ return c.redirect(url2, status2);
1839
+ }
1840
+ function stream(_c, callback, options = {}) {
1841
+ const encoder = new TextEncoder();
1842
+ const readableStream = new ReadableStream({
1843
+ async start(controller) {
1844
+ const write = async (chunk) => {
1845
+ controller.enqueue(encoder.encode(chunk));
1846
+ };
1847
+ try {
1848
+ await callback(write);
1849
+ } finally {
1850
+ controller.close();
1851
+ }
1852
+ }
1853
+ });
1854
+ return new Response(readableStream, {
1855
+ headers: {
1856
+ "Content-Type": options.contentType ?? "text/event-stream",
1857
+ "Cache-Control": "no-cache",
1858
+ Connection: "keep-alive",
1859
+ ...options.headers
1860
+ }
1861
+ });
1862
+ }
1863
+ function sse(c, callback) {
1864
+ return stream(c, async (write) => {
1865
+ const send = async (event) => {
1866
+ let message = "";
1867
+ if (event.id) {
1868
+ message += `id: ${event.id}
1869
+ `;
1870
+ }
1871
+ if (event.event) {
1872
+ message += `event: ${event.event}
1873
+ `;
1874
+ }
1875
+ if (event.retry) {
1876
+ message += `retry: ${event.retry}
1877
+ `;
1878
+ }
1879
+ const data = typeof event.data === "string" ? event.data : JSON.stringify(event.data);
1880
+ message += `data: ${data}
1881
+
1882
+ `;
1883
+ await write(message);
1884
+ };
1885
+ await callback(send);
1886
+ });
1887
+ }
1888
+ function download(c, data, filename, contentType = "application/octet-stream") {
1889
+ c.header("Content-Disposition", `attachment; filename="${filename}"`);
1890
+ c.header("Content-Type", contentType);
1891
+ if (typeof data === "string") {
1892
+ return c.body(data);
1893
+ }
1894
+ return new Response(data, {
1895
+ headers: c.res.headers
1896
+ });
1897
+ }
1898
+
1899
+ // src/health.ts
1900
+ import { Hono as Hono3 } from "hono";
1901
+ var startTime = Date.now();
1902
+ async function checkDatabase(db) {
1903
+ const start = Date.now();
1904
+ try {
1905
+ if (db.ping) {
1906
+ await db.ping();
1907
+ }
1908
+ return {
1909
+ status: "healthy",
1910
+ latency: Date.now() - start
1911
+ };
1912
+ } catch (err) {
1913
+ return {
1914
+ status: "unhealthy",
1915
+ message: err instanceof Error ? err.message : "Database connection failed",
1916
+ latency: Date.now() - start
1917
+ };
1918
+ }
1919
+ }
1920
+ function aggregateStatus(checks) {
1921
+ const statuses = Object.values(checks).map((c) => c.status);
1922
+ if (statuses.some((s) => s === "unhealthy")) {
1923
+ return "unhealthy";
1924
+ }
1925
+ if (statuses.some((s) => s === "degraded")) {
1926
+ return "degraded";
1927
+ }
1928
+ return "healthy";
1929
+ }
1930
+ function createHealthRouter(options = {}) {
1931
+ const router = new Hono3();
1932
+ const { checks = {}, detailed = true } = options;
1933
+ router.get("/", async (c) => {
1934
+ const db = c.get("db");
1935
+ const results = {};
1936
+ results["database"] = await checkDatabase(db);
1937
+ const customCheckResults = await Promise.all(
1938
+ Object.entries(checks).map(async ([name, check]) => {
1939
+ try {
1940
+ const result = await check();
1941
+ return [name, result];
1942
+ } catch (err) {
1943
+ return [
1944
+ name,
1945
+ {
1946
+ status: "unhealthy",
1947
+ message: err instanceof Error ? err.message : "Check failed"
1948
+ }
1949
+ ];
1950
+ }
1951
+ })
1952
+ );
1953
+ for (const [name, result] of customCheckResults) {
1954
+ results[name] = result;
1955
+ }
1956
+ const overallStatus = aggregateStatus(results);
1957
+ const response = {
1958
+ status: overallStatus,
1959
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1960
+ uptime: Math.floor((Date.now() - startTime) / 1e3),
1961
+ checks: detailed ? results : {}
1962
+ };
1963
+ const statusCode = overallStatus === "healthy" ? 200 : overallStatus === "degraded" ? 200 : 503;
1964
+ return c.json(response, statusCode);
1965
+ });
1966
+ router.get("/live", (c) => {
1967
+ return c.json({
1968
+ status: "ok",
1969
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1970
+ });
1971
+ });
1972
+ router.get("/ready", async (c) => {
1973
+ const db = c.get("db");
1974
+ const dbHealth = await checkDatabase(db);
1975
+ if (dbHealth.status === "unhealthy") {
1976
+ return c.json(
1977
+ {
1978
+ status: "not_ready",
1979
+ reason: "database",
1980
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1981
+ },
1982
+ 503
1983
+ );
1984
+ }
1985
+ return c.json({
1986
+ status: "ready",
1987
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1988
+ });
1989
+ });
1990
+ router.get("/startup", (c) => {
1991
+ return c.json({
1992
+ status: "started",
1993
+ uptime: Math.floor((Date.now() - startTime) / 1e3),
1994
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1995
+ });
1996
+ });
1997
+ return router;
1998
+ }
1999
+ async function healthHandler(c) {
2000
+ return c.json({
2001
+ status: "ok",
2002
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2003
+ uptime: Math.floor((Date.now() - startTime) / 1e3)
2004
+ });
2005
+ }
2006
+ export {
2007
+ ApiError,
2008
+ BadRequestError,
2009
+ ConflictError,
2010
+ dateRangeQuery2 as DateRangeQuerySchema,
2011
+ ForbiddenError2 as ForbiddenError,
2012
+ InMemoryRBAC,
2013
+ InternalError,
2014
+ MemoryRateLimitStorage,
2015
+ ModuleLoader,
2016
+ NotFoundError,
2017
+ paginationQuery2 as PaginationQuerySchema,
2018
+ QuotaExceededError,
2019
+ RBACService,
2020
+ RLSError,
2021
+ RLSManager,
2022
+ RateLimitError,
2023
+ searchQuery2 as SearchQuerySchema,
2024
+ ServiceUnavailableError,
2025
+ StandardRoles,
2026
+ UnauthorizedError2 as UnauthorizedError,
2027
+ uuidParam2 as UuidParamSchema,
2028
+ ValidationError,
2029
+ accepted,
2030
+ auth,
2031
+ cors2 as cors,
2032
+ createAuthMiddleware,
2033
+ createHealthRouter,
2034
+ createInMemoryRBAC,
2035
+ createModuleLoader,
2036
+ createModuleRouter,
2037
+ createPaginationMeta,
2038
+ createPermission,
2039
+ createQuotaEnforcement,
2040
+ createRBACService,
2041
+ createRLSManager,
2042
+ createRateLimiter,
2043
+ createRouter,
2044
+ createServer,
2045
+ createUsageTracking,
2046
+ createVersionedRouter,
2047
+ created,
2048
+ crudPermissions,
2049
+ csrf,
2050
+ cursorPaginate,
2051
+ defineModule,
2052
+ doubleSubmitCookie,
2053
+ download,
2054
+ error,
2055
+ errorHandler,
2056
+ generateDisableRLS,
2057
+ generateRLSPolicy,
2058
+ generateRequestId,
2059
+ healthHandler,
2060
+ json,
2061
+ jsonError,
2062
+ jsonWithMeta,
2063
+ multiQuotaEnforcement,
2064
+ noContent,
2065
+ notFoundHandler,
2066
+ optionalAuth,
2067
+ paginate,
2068
+ parseCursorPagination,
2069
+ parsePagination,
2070
+ parsePermission,
2071
+ quotaEnforcement,
2072
+ rateLimit,
2073
+ redirect,
2074
+ requestLogger,
2075
+ requireAnyPermission,
2076
+ requireAnyRole,
2077
+ requireAuth,
2078
+ requirePermission,
2079
+ requireRole,
2080
+ requireTenantMember,
2081
+ rlsMiddleware,
2082
+ setPaginationHeaders,
2083
+ sse,
2084
+ stream,
2085
+ success,
2086
+ type,
2087
+ usageTracking,
2088
+ validate,
2089
+ validateBody,
2090
+ validateHeaders,
2091
+ validateParams,
2092
+ validateQuery
2093
+ };
2094
+ //# sourceMappingURL=index.js.map