@syncular/server-hono 0.0.6-96 → 0.2.1

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.
Files changed (104) hide show
  1. package/README.md +5 -23
  2. package/dist/admin-page.d.ts +14 -0
  3. package/dist/admin-page.js +217 -0
  4. package/dist/admin.d.ts +39 -0
  5. package/dist/admin.js +142 -0
  6. package/dist/index.d.ts +16 -14
  7. package/dist/index.js +134 -23
  8. package/package.json +19 -53
  9. package/src/admin-page.ts +217 -0
  10. package/src/admin.ts +193 -0
  11. package/src/index.ts +175 -31
  12. package/dist/api-key-auth.d.ts +0 -49
  13. package/dist/api-key-auth.d.ts.map +0 -1
  14. package/dist/api-key-auth.js +0 -110
  15. package/dist/api-key-auth.js.map +0 -1
  16. package/dist/blobs.d.ts +0 -69
  17. package/dist/blobs.d.ts.map +0 -1
  18. package/dist/blobs.js +0 -383
  19. package/dist/blobs.js.map +0 -1
  20. package/dist/console/gateway.d.ts +0 -33
  21. package/dist/console/gateway.d.ts.map +0 -1
  22. package/dist/console/gateway.js +0 -2534
  23. package/dist/console/gateway.js.map +0 -1
  24. package/dist/console/index.d.ts +0 -11
  25. package/dist/console/index.d.ts.map +0 -1
  26. package/dist/console/index.js +0 -11
  27. package/dist/console/index.js.map +0 -1
  28. package/dist/console/routes.d.ts +0 -33
  29. package/dist/console/routes.d.ts.map +0 -1
  30. package/dist/console/routes.js +0 -3029
  31. package/dist/console/routes.js.map +0 -1
  32. package/dist/console/schemas.d.ts +0 -490
  33. package/dist/console/schemas.d.ts.map +0 -1
  34. package/dist/console/schemas.js +0 -303
  35. package/dist/console/schemas.js.map +0 -1
  36. package/dist/console/types.d.ts +0 -175
  37. package/dist/console/types.d.ts.map +0 -1
  38. package/dist/console/types.js +0 -2
  39. package/dist/console/types.js.map +0 -1
  40. package/dist/console/ui.d.ts +0 -38
  41. package/dist/console/ui.d.ts.map +0 -1
  42. package/dist/console/ui.js +0 -43
  43. package/dist/console/ui.js.map +0 -1
  44. package/dist/create-server.d.ts +0 -68
  45. package/dist/create-server.d.ts.map +0 -1
  46. package/dist/create-server.js +0 -110
  47. package/dist/create-server.js.map +0 -1
  48. package/dist/index.d.ts.map +0 -1
  49. package/dist/index.js.map +0 -1
  50. package/dist/openapi.d.ts +0 -45
  51. package/dist/openapi.d.ts.map +0 -1
  52. package/dist/openapi.js +0 -59
  53. package/dist/openapi.js.map +0 -1
  54. package/dist/proxy/connection-manager.d.ts +0 -78
  55. package/dist/proxy/connection-manager.d.ts.map +0 -1
  56. package/dist/proxy/connection-manager.js +0 -251
  57. package/dist/proxy/connection-manager.js.map +0 -1
  58. package/dist/proxy/index.d.ts +0 -8
  59. package/dist/proxy/index.d.ts.map +0 -1
  60. package/dist/proxy/index.js +0 -8
  61. package/dist/proxy/index.js.map +0 -1
  62. package/dist/proxy/routes.d.ts +0 -74
  63. package/dist/proxy/routes.d.ts.map +0 -1
  64. package/dist/proxy/routes.js +0 -147
  65. package/dist/proxy/routes.js.map +0 -1
  66. package/dist/rate-limit.d.ts +0 -101
  67. package/dist/rate-limit.d.ts.map +0 -1
  68. package/dist/rate-limit.js +0 -186
  69. package/dist/rate-limit.js.map +0 -1
  70. package/dist/routes.d.ts +0 -161
  71. package/dist/routes.d.ts.map +0 -1
  72. package/dist/routes.js +0 -1493
  73. package/dist/routes.js.map +0 -1
  74. package/dist/ws.d.ts +0 -235
  75. package/dist/ws.d.ts.map +0 -1
  76. package/dist/ws.js +0 -614
  77. package/dist/ws.js.map +0 -1
  78. package/src/__tests__/blob-routes.test.ts +0 -424
  79. package/src/__tests__/console-gateway-live-routes.test.ts +0 -297
  80. package/src/__tests__/console-gateway-routes.test.ts +0 -1364
  81. package/src/__tests__/console-routes.test.ts +0 -1626
  82. package/src/__tests__/console-ui.test.ts +0 -114
  83. package/src/__tests__/create-server.test.ts +0 -485
  84. package/src/__tests__/pull-chunk-storage.test.ts +0 -576
  85. package/src/__tests__/rate-limit.test.ts +0 -78
  86. package/src/__tests__/realtime-bridge.test.ts +0 -135
  87. package/src/__tests__/sync-rate-limit-routing.test.ts +0 -185
  88. package/src/__tests__/ws-connection-manager.test.ts +0 -176
  89. package/src/api-key-auth.ts +0 -179
  90. package/src/blobs.ts +0 -534
  91. package/src/console/gateway.ts +0 -3603
  92. package/src/console/index.ts +0 -11
  93. package/src/console/routes.ts +0 -3936
  94. package/src/console/schemas.ts +0 -434
  95. package/src/console/types.ts +0 -185
  96. package/src/console/ui.ts +0 -100
  97. package/src/create-server.ts +0 -207
  98. package/src/openapi.ts +0 -74
  99. package/src/proxy/connection-manager.ts +0 -340
  100. package/src/proxy/index.ts +0 -8
  101. package/src/proxy/routes.ts +0 -223
  102. package/src/rate-limit.ts +0 -321
  103. package/src/routes.ts +0 -2059
  104. package/src/ws.ts +0 -802
@@ -1,8 +0,0 @@
1
- /**
2
- * @syncular/server-hono - Proxy Exports
3
- *
4
- * WebSocket endpoint for admin database proxy.
5
- */
6
- export * from './connection-manager.js';
7
- export * from './routes.js';
8
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/proxy/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC"}
@@ -1,74 +0,0 @@
1
- /**
2
- * @syncular/server-hono - Proxy Routes
3
- *
4
- * WebSocket endpoint for database proxy.
5
- */
6
- import type { ProxyHandshake, ProxyMessage, ProxyResponse } from '@syncular/core';
7
- import type { ProxyHandlerCollection, ServerSyncDialect, SyncCoreDb } from '@syncular/server';
8
- import type { Context } from 'hono';
9
- import { Hono } from 'hono';
10
- import type { UpgradeWebSocket, WSContext } from 'hono/ws';
11
- import type { Kysely } from 'kysely';
12
- /**
13
- * WeakMap for storing proxy connection manager per Hono instance.
14
- */
15
- interface ProxyConnectionManagerHandle {
16
- canAccept(): boolean;
17
- getConnectionCount(): number;
18
- register(ws: WSContext, handshake: ProxyHandshake): unknown;
19
- handleMessage(ws: WSContext, message: ProxyMessage): Promise<ProxyResponse>;
20
- unregister(ws: WSContext): Promise<void>;
21
- }
22
- interface ProxyAuthResult {
23
- /** Actor ID for oplog tracking */
24
- actorId: string;
25
- }
26
- interface CreateProxyRoutesConfig<DB extends SyncCoreDb = SyncCoreDb> {
27
- /** Database connection */
28
- db: Kysely<DB>;
29
- /** Server sync dialect */
30
- dialect: ServerSyncDialect;
31
- /** Proxy table handlers for oplog generation */
32
- handlers: ProxyHandlerCollection;
33
- /** Authenticate the request and return actor info */
34
- authenticate: (c: Context) => Promise<ProxyAuthResult | null>;
35
- /** WebSocket upgrade function from Hono */
36
- upgradeWebSocket: UpgradeWebSocket;
37
- /** Maximum concurrent connections (default: 100) */
38
- maxConnections?: number;
39
- /** Idle connection timeout in ms (default: 30000) */
40
- idleTimeoutMs?: number;
41
- }
42
- /**
43
- * Create Hono routes for the proxy WebSocket endpoint.
44
- *
45
- * @example
46
- * ```typescript
47
- * import { Hono } from 'hono';
48
- * import { createBunWebSocket } from 'hono/bun';
49
- * import { createProxyRoutes } from '@syncular/server-hono/proxy';
50
- *
51
- * const { upgradeWebSocket, websocket } = createBunWebSocket();
52
- *
53
- * const app = new Hono();
54
- *
55
- * app.route('/proxy', createProxyRoutes({
56
- * db,
57
- * handlers: proxyHandlers,
58
- * authenticate: async (c) => {
59
- * // Verify admin auth
60
- * return { actorId: 'admin:123' };
61
- * },
62
- * upgradeWebSocket,
63
- * }));
64
- *
65
- * export default { fetch: app.fetch, websocket };
66
- * ```
67
- */
68
- export declare function createProxyRoutes<DB extends SyncCoreDb>(config: CreateProxyRoutesConfig<DB>): Hono;
69
- /**
70
- * Get the ProxyConnectionManager from a proxy routes instance.
71
- */
72
- export declare function getProxyConnectionManager(routes: Hono): ProxyConnectionManagerHandle | undefined;
73
- export {};
74
- //# sourceMappingURL=routes.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/proxy/routes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,cAAc,EAEd,YAAY,EACZ,aAAa,EACd,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EACV,sBAAsB,EACtB,iBAAiB,EACjB,UAAU,EACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGrC;;GAEG;AACH,UAAU,4BAA4B;IACpC,SAAS,IAAI,OAAO,CAAC;IACrB,kBAAkB,IAAI,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC;IAC5D,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5E,UAAU,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAOD,UAAU,eAAe;IACvB,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,uBAAuB,CAAC,EAAE,SAAS,UAAU,GAAG,UAAU;IAClE,0BAA0B;IAC1B,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACf,0BAA0B;IAC1B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,gDAAgD;IAChD,QAAQ,EAAE,sBAAsB,CAAC;IACjC,qDAAqD;IACrD,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IAC9D,2CAA2C;IAC3C,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,UAAU,EACrD,MAAM,EAAE,uBAAuB,CAAC,EAAE,CAAC,GAClC,IAAI,CA2HN;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,IAAI,GACX,4BAA4B,GAAG,SAAS,CAE1C"}
@@ -1,147 +0,0 @@
1
- /**
2
- * @syncular/server-hono - Proxy Routes
3
- *
4
- * WebSocket endpoint for database proxy.
5
- */
6
- import { logSyncEvent } from '@syncular/core';
7
- import { Hono } from 'hono';
8
- import { ProxyConnectionManager } from './connection-manager.js';
9
- const proxyConnectionManagerMap = new WeakMap();
10
- /**
11
- * Create Hono routes for the proxy WebSocket endpoint.
12
- *
13
- * @example
14
- * ```typescript
15
- * import { Hono } from 'hono';
16
- * import { createBunWebSocket } from 'hono/bun';
17
- * import { createProxyRoutes } from '@syncular/server-hono/proxy';
18
- *
19
- * const { upgradeWebSocket, websocket } = createBunWebSocket();
20
- *
21
- * const app = new Hono();
22
- *
23
- * app.route('/proxy', createProxyRoutes({
24
- * db,
25
- * handlers: proxyHandlers,
26
- * authenticate: async (c) => {
27
- * // Verify admin auth
28
- * return { actorId: 'admin:123' };
29
- * },
30
- * upgradeWebSocket,
31
- * }));
32
- *
33
- * export default { fetch: app.fetch, websocket };
34
- * ```
35
- */
36
- export function createProxyRoutes(config) {
37
- const app = new Hono();
38
- const manager = new ProxyConnectionManager({
39
- db: config.db,
40
- dialect: config.dialect,
41
- handlers: config.handlers,
42
- maxConnections: config.maxConnections,
43
- idleTimeoutMs: config.idleTimeoutMs,
44
- });
45
- // Store manager for external access if needed
46
- proxyConnectionManagerMap.set(app, manager);
47
- // WebSocket upgrade endpoint - using regular route since WebSocket doesn't fit OpenAPI well
48
- app.get('/', async (c) => {
49
- // Authenticate before upgrade
50
- const auth = await config.authenticate(c);
51
- if (!auth) {
52
- return c.json({ error: 'UNAUTHENTICATED' }, 401);
53
- }
54
- // Check connection limit
55
- if (!manager.canAccept()) {
56
- logSyncEvent({
57
- event: 'proxy.rejected',
58
- userId: auth.actorId,
59
- reason: 'max_connections',
60
- });
61
- return c.json({ error: 'PROXY_CONNECTION_LIMIT' }, 429);
62
- }
63
- logSyncEvent({
64
- event: 'proxy.connect',
65
- userId: auth.actorId,
66
- });
67
- return config.upgradeWebSocket(c, {
68
- onOpen(_evt, _ws) {
69
- // Connection opened, wait for handshake message
70
- },
71
- async onMessage(evt, ws) {
72
- try {
73
- const data = typeof evt.data === 'string'
74
- ? evt.data
75
- : new TextDecoder().decode(evt.data);
76
- const message = JSON.parse(data);
77
- // Handle handshake
78
- if (message.type === 'handshake') {
79
- const handshake = message;
80
- // Validate that the handshake actor matches authenticated actor
81
- if (handshake.actorId !== auth.actorId) {
82
- const ack = {
83
- type: 'handshake_ack',
84
- ok: false,
85
- error: 'Actor ID mismatch',
86
- };
87
- ws.send(JSON.stringify(ack));
88
- ws.close(4001, 'Unauthorized');
89
- return;
90
- }
91
- manager.register(ws, handshake);
92
- const ack = {
93
- type: 'handshake_ack',
94
- ok: true,
95
- };
96
- ws.send(JSON.stringify(ack));
97
- return;
98
- }
99
- // Handle proxy messages
100
- const proxyMessage = message;
101
- const response = await manager.handleMessage(ws, proxyMessage);
102
- ws.send(JSON.stringify(response));
103
- }
104
- catch (err) {
105
- // Send error response if we can parse the message ID
106
- try {
107
- const parsed = JSON.parse(typeof evt.data === 'string'
108
- ? evt.data
109
- : new TextDecoder().decode(evt.data));
110
- if (parsed.id) {
111
- ws.send(JSON.stringify({
112
- id: parsed.id,
113
- type: 'error',
114
- error: err instanceof Error ? err.message : 'Unknown error',
115
- }));
116
- }
117
- }
118
- catch {
119
- // Ignore parse errors
120
- }
121
- }
122
- },
123
- async onClose(_evt, ws) {
124
- await manager.unregister(ws);
125
- logSyncEvent({
126
- event: 'proxy.disconnect',
127
- userId: auth.actorId,
128
- });
129
- },
130
- async onError(_evt, ws) {
131
- await manager.unregister(ws);
132
- logSyncEvent({
133
- event: 'proxy.error',
134
- userId: auth.actorId,
135
- });
136
- },
137
- });
138
- });
139
- return app;
140
- }
141
- /**
142
- * Get the ProxyConnectionManager from a proxy routes instance.
143
- */
144
- export function getProxyConnectionManager(routes) {
145
- return proxyConnectionManagerMap.get(routes);
146
- }
147
- //# sourceMappingURL=routes.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/proxy/routes.ts"],"names":[],"mappings":"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"}
@@ -1,101 +0,0 @@
1
- /**
2
- * @syncular/server-hono - Rate limiting middleware for sync endpoints
3
- *
4
- * Provides per-user rate limiting to prevent DoS attacks and excessive
5
- * server load from misbehaving clients.
6
- */
7
- import type { Context, MiddlewareHandler } from 'hono';
8
- /**
9
- * Rate limit configuration
10
- */
11
- export interface RateLimitConfig {
12
- /**
13
- * Maximum requests per window (default: 60)
14
- */
15
- maxRequests: number;
16
- /**
17
- * Time window in milliseconds (default: 60000 = 1 minute)
18
- */
19
- windowMs: number;
20
- /**
21
- * Function to extract the rate limit key from a request.
22
- * Typically returns userId, deviceId, or IP address.
23
- * Return null to skip rate limiting for this request.
24
- */
25
- keyGenerator: (c: Context) => string | null | Promise<string | null>;
26
- /**
27
- * Whether to include rate limit headers in responses (default: true)
28
- */
29
- includeHeaders?: boolean;
30
- /**
31
- * Custom handler for rate-limited requests (optional)
32
- * If not provided, returns a 429 JSON response
33
- */
34
- onRateLimited?: (c: Context, retryAfterMs: number) => Response | Promise<Response>;
35
- /**
36
- * Whether to skip rate limiting in test environments (default: false)
37
- */
38
- skipInTest?: boolean;
39
- }
40
- /**
41
- * Reset the global store (for testing)
42
- */
43
- export declare function resetRateLimitStore(): void;
44
- /**
45
- * Create a rate limiting middleware for Hono.
46
- *
47
- * @example
48
- * ```typescript
49
- * import { createRateLimiter } from '@syncular/server-hono';
50
- *
51
- * const rateLimiter = createRateLimiter({
52
- * maxRequests: 60,
53
- * windowMs: 60_000,
54
- * keyGenerator: async (c) => {
55
- * const auth = await authenticate(c);
56
- * return auth?.userId ?? null;
57
- * },
58
- * });
59
- *
60
- * app.use('/sync/*', rateLimiter);
61
- * ```
62
- */
63
- export declare function createRateLimiter(config: Partial<RateLimitConfig> & Pick<RateLimitConfig, 'keyGenerator'>): MiddlewareHandler;
64
- /**
65
- * Create a rate limiter that uses userId from auth context.
66
- *
67
- * This is a convenience function for the common case of rate limiting
68
- * by authenticated user.
69
- *
70
- * @example
71
- * ```typescript
72
- * const syncRoutes = createSyncRoutes({
73
- * db,
74
- * handlers: [tasksHandler],
75
- * authenticate,
76
- * sync: {
77
- * rateLimit: {
78
- * pull: { maxRequests: 120, windowMs: 60_000 },
79
- * push: { maxRequests: 60, windowMs: 60_000 },
80
- * },
81
- * },
82
- * });
83
- * ```
84
- */
85
- export interface SyncRateLimitConfig {
86
- /**
87
- * Rate limit config for pull requests.
88
- * Set to false to disable rate limiting for pulls.
89
- */
90
- pull?: Omit<RateLimitConfig, 'keyGenerator'> | false;
91
- /**
92
- * Rate limit config for push requests.
93
- * Set to false to disable rate limiting for pushes.
94
- */
95
- push?: Omit<RateLimitConfig, 'keyGenerator'> | false;
96
- }
97
- /**
98
- * Default sync rate limit configuration
99
- */
100
- export declare const DEFAULT_SYNC_RATE_LIMITS: SyncRateLimitConfig;
101
- //# sourceMappingURL=rate-limit.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAErE;;OAEG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;OAGG;IACH,aAAa,CAAC,EAAE,CACd,CAAC,EAAE,OAAO,EACV,YAAY,EAAE,MAAM,KACjB,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAElC;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAwHD;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC,eAAe,EAAE,cAAc,CAAC,GACvE,iBAAiB,CAqEnB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,cAAc,CAAC,GAAG,KAAK,CAAC;IAErD;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,cAAc,CAAC,GAAG,KAAK,CAAC;CACtD;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,mBAWtC,CAAC"}
@@ -1,186 +0,0 @@
1
- /**
2
- * @syncular/server-hono - Rate limiting middleware for sync endpoints
3
- *
4
- * Provides per-user rate limiting to prevent DoS attacks and excessive
5
- * server load from misbehaving clients.
6
- */
7
- import { logSyncEvent } from '@syncular/core';
8
- /**
9
- * Default rate limit configuration
10
- */
11
- const DEFAULT_RATE_LIMIT_CONFIG = {
12
- maxRequests: 60,
13
- windowMs: 60_000,
14
- includeHeaders: true,
15
- skipInTest: false,
16
- };
17
- /**
18
- * In-memory rate limiter store
19
- *
20
- * Note: This is suitable for single-instance deployments.
21
- * For distributed deployments, use Redis or similar.
22
- */
23
- class RateLimitStore {
24
- windowMs;
25
- entries = new Map();
26
- cleanupInterval = null;
27
- constructor(windowMs) {
28
- this.windowMs = windowMs;
29
- // Clean up expired entries periodically
30
- this.cleanupInterval = setInterval(() => this.cleanup(), Math.max(windowMs, 60_000));
31
- }
32
- /**
33
- * Check and increment the rate limit counter for a key.
34
- *
35
- * @param key - Rate limit key (e.g., userId)
36
- * @param maxRequests - Maximum requests allowed
37
- * @returns Rate limit check result
38
- */
39
- check(key, maxRequests) {
40
- const now = Date.now();
41
- let entry = this.entries.get(key);
42
- // Check if window has expired
43
- if (!entry || now - entry.windowStart >= this.windowMs) {
44
- entry = { count: 0, windowStart: now };
45
- this.entries.set(key, entry);
46
- }
47
- const resetAt = entry.windowStart + this.windowMs;
48
- const allowed = entry.count < maxRequests;
49
- if (allowed) {
50
- entry.count++;
51
- }
52
- return {
53
- allowed,
54
- current: entry.count,
55
- remaining: Math.max(0, maxRequests - entry.count),
56
- resetAt,
57
- };
58
- }
59
- /**
60
- * Clean up expired entries
61
- */
62
- cleanup() {
63
- const now = Date.now();
64
- for (const [key, entry] of this.entries) {
65
- if (now - entry.windowStart >= this.windowMs) {
66
- this.entries.delete(key);
67
- }
68
- }
69
- }
70
- /**
71
- * Stop the cleanup interval
72
- */
73
- stop() {
74
- if (this.cleanupInterval) {
75
- clearInterval(this.cleanupInterval);
76
- this.cleanupInterval = null;
77
- }
78
- }
79
- /**
80
- * Clear all entries (for testing)
81
- */
82
- clear() {
83
- this.entries.clear();
84
- }
85
- /**
86
- * Get current entry count (for monitoring)
87
- */
88
- get size() {
89
- return this.entries.size;
90
- }
91
- }
92
- // Track created stores so tests can reset state deterministically.
93
- const activeStores = new Set();
94
- /**
95
- * Reset the global store (for testing)
96
- */
97
- export function resetRateLimitStore() {
98
- for (const store of activeStores) {
99
- store.stop();
100
- }
101
- activeStores.clear();
102
- }
103
- /**
104
- * Create a rate limiting middleware for Hono.
105
- *
106
- * @example
107
- * ```typescript
108
- * import { createRateLimiter } from '@syncular/server-hono';
109
- *
110
- * const rateLimiter = createRateLimiter({
111
- * maxRequests: 60,
112
- * windowMs: 60_000,
113
- * keyGenerator: async (c) => {
114
- * const auth = await authenticate(c);
115
- * return auth?.userId ?? null;
116
- * },
117
- * });
118
- *
119
- * app.use('/sync/*', rateLimiter);
120
- * ```
121
- */
122
- export function createRateLimiter(config) {
123
- const { maxRequests = DEFAULT_RATE_LIMIT_CONFIG.maxRequests, windowMs = DEFAULT_RATE_LIMIT_CONFIG.windowMs, keyGenerator, includeHeaders = DEFAULT_RATE_LIMIT_CONFIG.includeHeaders, onRateLimited, skipInTest = DEFAULT_RATE_LIMIT_CONFIG.skipInTest, } = config;
124
- const store = new RateLimitStore(windowMs);
125
- activeStores.add(store);
126
- return async (c, next) => {
127
- // Skip in test environment if configured
128
- if (skipInTest && process.env.NODE_ENV === 'test') {
129
- return next();
130
- }
131
- // Get the rate limit key
132
- const key = await keyGenerator(c);
133
- if (key === null) {
134
- // Skip rate limiting for this request
135
- return next();
136
- }
137
- const result = store.check(key, maxRequests);
138
- // Add rate limit headers if configured
139
- if (includeHeaders) {
140
- c.header('X-RateLimit-Limit', String(maxRequests));
141
- c.header('X-RateLimit-Remaining', String(result.remaining));
142
- c.header('X-RateLimit-Reset', String(Math.ceil(result.resetAt / 1000)));
143
- }
144
- if (!result.allowed) {
145
- const retryAfterMs = result.resetAt - Date.now();
146
- const retryAfterSec = Math.ceil(retryAfterMs / 1000);
147
- // Log rate limit event
148
- logSyncEvent({
149
- event: 'sync.rate_limit',
150
- key,
151
- current: result.current,
152
- maxRequests,
153
- retryAfterMs,
154
- });
155
- // Add Retry-After header
156
- c.header('Retry-After', String(retryAfterSec));
157
- // Use custom handler or default response
158
- if (onRateLimited) {
159
- return onRateLimited(c, retryAfterMs);
160
- }
161
- return c.json({
162
- error: 'RATE_LIMITED',
163
- message: 'Too many requests. Please try again later.',
164
- retryAfterMs,
165
- retryAfterSec,
166
- }, 429);
167
- }
168
- return next();
169
- };
170
- }
171
- /**
172
- * Default sync rate limit configuration
173
- */
174
- export const DEFAULT_SYNC_RATE_LIMITS = {
175
- pull: {
176
- maxRequests: 120, // 2 requests per second average
177
- windowMs: 60_000,
178
- includeHeaders: true,
179
- },
180
- push: {
181
- maxRequests: 60, // 1 request per second average
182
- windowMs: 60_000,
183
- includeHeaders: true,
184
- },
185
- };
186
- //# sourceMappingURL=rate-limit.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"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"}