@jango-blockchained/hoox-shared 1.0.8 → 1.0.9

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 (61) hide show
  1. package/dist/analytics.d.ts +0 -1
  2. package/dist/analytics.d.ts.map +1 -1
  3. package/dist/colors.d.ts +0 -1
  4. package/dist/colors.d.ts.map +1 -1
  5. package/dist/colors.js +0 -1
  6. package/dist/cron-handler.d.ts +34 -0
  7. package/dist/cron-handler.d.ts.map +1 -0
  8. package/dist/cron-handler.js +48 -0
  9. package/dist/d1/index.js +49 -6
  10. package/dist/d1/repository.d.ts +5 -0
  11. package/dist/d1/repository.d.ts.map +1 -1
  12. package/dist/errors.d.ts +1 -0
  13. package/dist/errors.d.ts.map +1 -1
  14. package/dist/errors.js +48 -5
  15. package/dist/exchanges/base-exchange-client.d.ts +68 -0
  16. package/dist/exchanges/base-exchange-client.d.ts.map +1 -0
  17. package/dist/exchanges/index.d.ts +3 -0
  18. package/dist/exchanges/index.d.ts.map +1 -0
  19. package/dist/exchanges/index.js +120 -0
  20. package/dist/exchanges/types.d.ts +19 -0
  21. package/dist/exchanges/types.d.ts.map +1 -0
  22. package/dist/health.js +48 -5
  23. package/dist/index.d.ts +11 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +480 -15
  26. package/dist/kvUtils.js +48 -5
  27. package/dist/middleware/auth.d.ts +6 -3
  28. package/dist/middleware/auth.d.ts.map +1 -1
  29. package/dist/middleware/index.js +193 -18
  30. package/dist/middleware/logger.d.ts +2 -1
  31. package/dist/middleware/logger.d.ts.map +1 -1
  32. package/dist/middleware/rate-limit.d.ts +3 -1
  33. package/dist/middleware/rate-limit.d.ts.map +1 -1
  34. package/dist/path-utils.d.ts +147 -0
  35. package/dist/path-utils.d.ts.map +1 -0
  36. package/dist/path-utils.js +101 -0
  37. package/dist/queue-handler.d.ts +43 -0
  38. package/dist/queue-handler.d.ts.map +1 -0
  39. package/dist/queue-handler.js +46 -0
  40. package/dist/router.d.ts +1 -1
  41. package/dist/router.d.ts.map +1 -1
  42. package/dist/router.js +15 -8
  43. package/dist/service-bindings.d.ts +5 -28
  44. package/dist/service-bindings.d.ts.map +1 -1
  45. package/dist/service-bindings.js +3 -1
  46. package/dist/session.d.ts +9 -5
  47. package/dist/session.d.ts.map +1 -1
  48. package/dist/session.js +83 -3
  49. package/dist/sse.js +1 -1
  50. package/dist/stores/service-store.d.ts +50 -1
  51. package/dist/stores/service-store.d.ts.map +1 -1
  52. package/dist/stores/service-store.js +67 -3
  53. package/dist/test-utils.d.ts +132 -0
  54. package/dist/test-utils.d.ts.map +1 -0
  55. package/dist/test-utils.js +128 -0
  56. package/dist/types/router.d.ts +18 -8
  57. package/dist/types/router.d.ts.map +1 -1
  58. package/dist/types.d.ts +59 -5
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/wizard/engine.d.ts.map +1 -1
  61. package/package.json +11 -3
package/dist/kvUtils.js CHANGED
@@ -32,22 +32,65 @@ function toError(err, fallback = "Unknown error") {
32
32
  return fallback;
33
33
  }
34
34
  }
35
+ var SENSITIVE_FIELDS = new Set([
36
+ "password",
37
+ "token",
38
+ "secret",
39
+ "api_key",
40
+ "apiKey",
41
+ "authorization"
42
+ ]);
43
+ function hasSensitiveFields(obj) {
44
+ for (const key of Object.keys(obj)) {
45
+ if (SENSITIVE_FIELDS.has(key))
46
+ return true;
47
+ }
48
+ return false;
49
+ }
50
+ function hasErrorValues(obj) {
51
+ for (const v of Object.values(obj)) {
52
+ if (v instanceof Error)
53
+ return true;
54
+ }
55
+ return false;
56
+ }
35
57
  function sanitizeOutput(data) {
58
+ if (data === null || data === undefined)
59
+ return data;
60
+ const t = typeof data;
61
+ if (t === "string" || t === "number" || t === "boolean")
62
+ return data;
36
63
  if (data instanceof Error) {
37
64
  return { name: data.name, message: data.message };
38
65
  }
39
- if (data && typeof data === "object" && !Array.isArray(data)) {
66
+ if (Array.isArray(data)) {
67
+ return data.map(sanitizeOutput);
68
+ }
69
+ if (t === "object") {
70
+ const obj = data;
71
+ const keys = Object.keys(obj);
72
+ if (keys.length < 10) {
73
+ const sanitized2 = {};
74
+ for (const k of keys) {
75
+ if (k === "stack" || k === "cause")
76
+ continue;
77
+ const v = obj[k];
78
+ sanitized2[k] = v instanceof Error ? { name: v.name, message: v.message } : sanitizeOutput(v);
79
+ }
80
+ return sanitized2;
81
+ }
82
+ if (!hasSensitiveFields(obj) && !hasErrorValues(obj)) {
83
+ return obj;
84
+ }
40
85
  const sanitized = {};
41
- for (const [k, v] of Object.entries(data)) {
86
+ for (const k of keys) {
42
87
  if (k === "stack" || k === "cause")
43
88
  continue;
89
+ const v = obj[k];
44
90
  sanitized[k] = v instanceof Error ? { name: v.name, message: v.message } : sanitizeOutput(v);
45
91
  }
46
92
  return sanitized;
47
93
  }
48
- if (Array.isArray(data)) {
49
- return data.map(sanitizeOutput);
50
- }
51
94
  return data;
52
95
  }
53
96
  function createJsonResponse(data, status = 200) {
@@ -3,10 +3,10 @@
3
3
  * Provides both Bearer token auth and internal service-to-service auth
4
4
  */
5
5
  import type { Env } from "../types";
6
- import type { Handler } from "../types/router";
6
+ import type { MiddlewareHandler } from "../types/router";
7
7
  /**
8
8
  * Constant-time string comparison to prevent timing attacks.
9
- * Manually performs byte-wise XOR comparison to avoid early returns.
9
+ * Uses crypto.timingSafeEqual for edge-native constant-time comparison.
10
10
  */
11
11
  export declare function timingSafeEqual(a: string, b: string): boolean;
12
12
  /**
@@ -17,6 +17,9 @@ export declare function requireAuth(request: Request, env: Env): Promise<Respons
17
17
  /**
18
18
  * Environment with an internal auth key binding.
19
19
  * Workers that accept internal service-to-service requests should extend this.
20
+ * Note: Uses [key: string]: unknown for compatibility with dynamic key access.
21
+ * This is intentionally less strict than wrangler-generated Env to allow
22
+ * InternalAuthEnv to be satisfied by actual Env bindings at runtime.
20
23
  */
21
24
  export interface InternalAuthEnv {
22
25
  [key: string]: unknown;
@@ -42,7 +45,7 @@ export declare function requireInternalAuth(request: Request, env: InternalAuthE
42
45
  *
43
46
  * @param keyName - The env binding name for the internal key (default: 'INTERNAL_KEY_BINDING')
44
47
  */
45
- export declare function createInternalAuthMiddleware<TEnv extends InternalAuthEnv>(keyName?: string): Handler<TEnv>;
48
+ export declare function createInternalAuthMiddleware(keyName?: string): MiddlewareHandler<any>;
46
49
  /**
47
50
  * Check internal auth and return a result object (non-throwing pattern).
48
51
  * Useful when you need to check auth without immediately returning a response.
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAU7D;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,GACP,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAmB1B;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,eAAe,EACpB,OAAO,GAAE,MAA+B,GACvC,QAAQ,GAAG,IAAI,CAsBjB;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,SAAS,eAAe,EACvE,OAAO,GAAE,MAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CAKf;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,eAAe,EACpB,OAAO,GAAE,MAA+B,GACvC;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAYzC"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAa7D;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,GACP,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAkB1B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,eAAe,EACpB,OAAO,GAAE,MAA+B,GACvC,QAAQ,GAAG,IAAI,CAqBjB;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,GAAE,MAA+B,GACvC,iBAAiB,CAAC,GAAG,CAAC,CA0BxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,eAAe,EACpB,OAAO,GAAE,MAA+B,GACvC;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAYzC"}
@@ -32,22 +32,65 @@ function toError(err, fallback = "Unknown error") {
32
32
  return fallback;
33
33
  }
34
34
  }
35
+ var SENSITIVE_FIELDS = new Set([
36
+ "password",
37
+ "token",
38
+ "secret",
39
+ "api_key",
40
+ "apiKey",
41
+ "authorization"
42
+ ]);
43
+ function hasSensitiveFields(obj) {
44
+ for (const key of Object.keys(obj)) {
45
+ if (SENSITIVE_FIELDS.has(key))
46
+ return true;
47
+ }
48
+ return false;
49
+ }
50
+ function hasErrorValues(obj) {
51
+ for (const v of Object.values(obj)) {
52
+ if (v instanceof Error)
53
+ return true;
54
+ }
55
+ return false;
56
+ }
35
57
  function sanitizeOutput(data) {
58
+ if (data === null || data === undefined)
59
+ return data;
60
+ const t = typeof data;
61
+ if (t === "string" || t === "number" || t === "boolean")
62
+ return data;
36
63
  if (data instanceof Error) {
37
64
  return { name: data.name, message: data.message };
38
65
  }
39
- if (data && typeof data === "object" && !Array.isArray(data)) {
66
+ if (Array.isArray(data)) {
67
+ return data.map(sanitizeOutput);
68
+ }
69
+ if (t === "object") {
70
+ const obj = data;
71
+ const keys = Object.keys(obj);
72
+ if (keys.length < 10) {
73
+ const sanitized2 = {};
74
+ for (const k of keys) {
75
+ if (k === "stack" || k === "cause")
76
+ continue;
77
+ const v = obj[k];
78
+ sanitized2[k] = v instanceof Error ? { name: v.name, message: v.message } : sanitizeOutput(v);
79
+ }
80
+ return sanitized2;
81
+ }
82
+ if (!hasSensitiveFields(obj) && !hasErrorValues(obj)) {
83
+ return obj;
84
+ }
40
85
  const sanitized = {};
41
- for (const [k, v] of Object.entries(data)) {
86
+ for (const k of keys) {
42
87
  if (k === "stack" || k === "cause")
43
88
  continue;
89
+ const v = obj[k];
44
90
  sanitized[k] = v instanceof Error ? { name: v.name, message: v.message } : sanitizeOutput(v);
45
91
  }
46
92
  return sanitized;
47
93
  }
48
- if (Array.isArray(data)) {
49
- return data.map(sanitizeOutput);
50
- }
51
94
  return data;
52
95
  }
53
96
  function createJsonResponse(data, status = 200) {
@@ -114,10 +157,13 @@ function createLogger(ctx) {
114
157
  console.error(line);
115
158
  else if (level === "warn")
116
159
  console.warn(line);
160
+ else if (level === "debug")
161
+ console.debug(line);
117
162
  else
118
163
  console.info(line);
119
164
  }
120
165
  return {
166
+ debug: (msg, ctx2) => emit("debug", msg, ctx2),
121
167
  info: (msg, ctx2) => emit("info", msg, ctx2),
122
168
  warn: (msg, ctx2) => emit("warn", msg, ctx2),
123
169
  error: (msg, ctx2) => emit("error", msg, ctx2)
@@ -193,10 +239,19 @@ function requireInternalAuth(request, env, keyName = "INTERNAL_KEY_BINDING") {
193
239
  return null;
194
240
  }
195
241
  function createInternalAuthMiddleware(keyName = "INTERNAL_KEY_BINDING") {
196
- return (request, env) => {
197
- const result = requireInternalAuth(request, env, keyName);
198
- if (result)
199
- return result;
242
+ return async (request, env, _ctx) => {
243
+ const expectedKey = env[keyName];
244
+ if (!expectedKey) {
245
+ return new Response(JSON.stringify({
246
+ success: false,
247
+ error: `Internal auth key ${keyName} not configured`
248
+ }), { status: 401, headers: { "Content-Type": "application/json" } });
249
+ }
250
+ const providedKey = request.headers.get("X-Internal-Auth-Key");
251
+ if (!providedKey || !timingSafeEqual(providedKey, expectedKey)) {
252
+ return new Response(JSON.stringify({ success: false, error: "Unauthorized" }), { status: 401, headers: { "Content-Type": "application/json" } });
253
+ }
254
+ return;
200
255
  };
201
256
  }
202
257
  function checkInternalAuth(request, env, keyName = "INTERNAL_KEY_BINDING") {
@@ -211,8 +266,94 @@ function checkInternalAuth(request, env, keyName = "INTERNAL_KEY_BINDING") {
211
266
  return { authorized: true };
212
267
  }
213
268
  // src/middleware/rate-limit.ts
269
+ function createStorage(kv) {
270
+ if (!kv) {
271
+ const memory = new Map;
272
+ return {
273
+ async get(key) {
274
+ const entry = memory.get(key);
275
+ if (!entry)
276
+ return null;
277
+ if (Date.now() > entry.expiresAt) {
278
+ memory.delete(key);
279
+ return null;
280
+ }
281
+ return entry.value;
282
+ },
283
+ async put(key, value, opts) {
284
+ const ttl = opts?.expirationTtl ?? 0;
285
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
286
+ memory.set(key, { value, expiresAt });
287
+ },
288
+ async incr(key, opts) {
289
+ let entry = memory.get(key);
290
+ let newValue;
291
+ let attempts = 0;
292
+ const maxAttempts = 100;
293
+ while (true) {
294
+ const count = entry ? parseInt(entry.value, 10) : 0;
295
+ newValue = count + 1;
296
+ if (!entry) {
297
+ const ttl = opts?.expirationTtl ?? 0;
298
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
299
+ if (memory.get(key) === undefined) {
300
+ memory.set(key, { value: String(newValue), expiresAt });
301
+ return newValue;
302
+ }
303
+ entry = memory.get(key);
304
+ } else {
305
+ if (Date.now() > entry.expiresAt) {
306
+ memory.delete(key);
307
+ entry = undefined;
308
+ continue;
309
+ }
310
+ const currentEntry = memory.get(key);
311
+ if (currentEntry === entry) {
312
+ const ttl = opts?.expirationTtl ?? 0;
313
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
314
+ memory.set(key, { value: String(newValue), expiresAt });
315
+ return newValue;
316
+ }
317
+ entry = currentEntry;
318
+ }
319
+ attempts++;
320
+ if (attempts >= maxAttempts) {
321
+ const current = await this.get(key);
322
+ const count2 = current ? parseInt(current, 10) : 0;
323
+ newValue = count2 + 1;
324
+ const ttl = opts?.expirationTtl ?? 0;
325
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
326
+ memory.set(key, { value: String(newValue), expiresAt });
327
+ return newValue;
328
+ }
329
+ }
330
+ }
331
+ };
332
+ }
333
+ return {
334
+ async get(key) {
335
+ return kv.get(key);
336
+ },
337
+ async put(key, value, opts) {
338
+ await kv.put(key, value, opts);
339
+ },
340
+ async incr(key, opts) {
341
+ const maxRetries = 3;
342
+ for (let attempt = 0;attempt < maxRetries; attempt++) {
343
+ const current2 = await kv.get(key);
344
+ const count = current2 ? parseInt(current2, 10) : 0;
345
+ const next = count + 1;
346
+ await kv.put(key, String(next), opts);
347
+ return next;
348
+ }
349
+ const current = await kv.get(key);
350
+ return current ? parseInt(current, 10) + 1 : 1;
351
+ }
352
+ };
353
+ }
214
354
  function createRateLimiter(kv, config) {
215
355
  const prefix = config.keyPrefix ?? "rate-limit";
356
+ const storage = createStorage(kv);
216
357
  function getClientIp(request) {
217
358
  return request.headers.get("CF-Connecting-IP") ?? "unknown";
218
359
  }
@@ -220,23 +361,37 @@ function createRateLimiter(kv, config) {
220
361
  const windowStart = Math.floor(Date.now() / (config.windowSeconds * 1000));
221
362
  return `${prefix}:${ip}:${windowStart}`;
222
363
  }
223
- async function check(request) {
224
- const ip = getClientIp(request);
225
- const key = getWindowKey(ip);
226
- const current = await kv.get(key);
364
+ async function checkWithKey(fullKey) {
365
+ const current = await storage.get(fullKey);
227
366
  const count = current ? parseInt(current, 10) : 0;
228
367
  if (count >= config.maxRequests) {
229
- const retryAfter = config.windowSeconds - Date.now() % (config.windowSeconds * 1000) / 1000;
368
+ const retryAfter = Math.ceil(config.windowSeconds - Date.now() % (config.windowSeconds * 1000) / 1000);
230
369
  return {
231
370
  allowed: false,
232
371
  remaining: 0,
233
- retryAfter: Math.ceil(retryAfter)
372
+ retryAfter
234
373
  };
235
374
  }
236
- await kv.put(key, String(count + 1), {
375
+ const newCount = await storage.incr(fullKey, {
237
376
  expirationTtl: config.windowSeconds
238
377
  });
239
- return { allowed: true, remaining: config.maxRequests - count - 1 };
378
+ if (newCount > config.maxRequests) {
379
+ const retryAfter = Math.ceil(config.windowSeconds - Date.now() % (config.windowSeconds * 1000) / 1000);
380
+ return {
381
+ allowed: false,
382
+ remaining: 0,
383
+ retryAfter
384
+ };
385
+ }
386
+ return {
387
+ allowed: true,
388
+ remaining: config.maxRequests - newCount
389
+ };
390
+ }
391
+ async function check(request) {
392
+ const ip = getClientIp(request);
393
+ const key = getWindowKey(ip);
394
+ return checkWithKey(key);
240
395
  }
241
396
  async function enforce(request) {
242
397
  const result = await check(request);
@@ -254,7 +409,27 @@ function createRateLimiter(kv, config) {
254
409
  }
255
410
  return null;
256
411
  }
257
- return { check, enforce };
412
+ async function checkKey(key) {
413
+ const fullKey = `${prefix}:${key}`;
414
+ return checkWithKey(fullKey);
415
+ }
416
+ async function enforceKey(key) {
417
+ const result = await checkKey(key);
418
+ if (!result.allowed) {
419
+ return new Response(JSON.stringify({
420
+ error: "Rate limit exceeded",
421
+ retryAfter: result.retryAfter
422
+ }), {
423
+ status: 429,
424
+ headers: {
425
+ "Content-Type": "application/json",
426
+ "Retry-After": String(result.retryAfter ?? config.windowSeconds)
427
+ }
428
+ });
429
+ }
430
+ return null;
431
+ }
432
+ return { check, enforce, checkKey, enforceKey };
258
433
  }
259
434
  // src/middleware/validate.ts
260
435
  function validateJson(schema, data) {
@@ -7,10 +7,11 @@ export interface LogContext {
7
7
  module?: string;
8
8
  }
9
9
  export interface Logger {
10
+ debug(message: string, context?: Record<string, unknown>): void;
10
11
  info(message: string, context?: Record<string, unknown>): void;
11
12
  warn(message: string, context?: Record<string, unknown>): void;
12
13
  error(message: string, context?: Record<string, unknown>): void;
13
14
  }
14
15
  export declare function createLogger(ctx: LogContext): Logger;
15
- export declare function withRequestLog<E extends Record<string, unknown>>(handler: (request: Request, env: E, ctx: ExecutionContext) => Promise<Response>, logCtx: LogContext): (request: Request, env: E, ctx: ExecutionContext) => Promise<Response>;
16
+ export declare function withRequestLog<E>(handler: (request: Request, env: E, ctx: ExecutionContext) => Promise<Response>, logCtx: LogContext): (request: Request, env: E, ctx: ExecutionContext) => Promise<Response>;
16
17
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAWD,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACjE;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CA0BpD;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9D,OAAO,EAAE,CACP,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,CAAC,EACN,GAAG,EAAE,gBAAgB,KAClB,OAAO,CAAC,QAAQ,CAAC,EACtB,MAAM,EAAE,UAAU,GACjB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,QAAQ,CAAC,CA2BxE"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/middleware/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAWD,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAChE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACjE;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CA4BpD;AAED,wBAAgB,cAAc,CAAC,CAAC,EAC9B,OAAO,EAAE,CACP,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,CAAC,EACN,GAAG,EAAE,gBAAgB,KAClB,OAAO,CAAC,QAAQ,CAAC,EACtB,MAAM,EAAE,UAAU,GACjB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,QAAQ,CAAC,CA2BxE"}
@@ -18,7 +18,9 @@ interface RateLimitResult {
18
18
  export interface RateLimiter {
19
19
  check(request: Request): Promise<RateLimitResult>;
20
20
  enforce(request: Request): Promise<Response | null>;
21
+ checkKey(key: string): Promise<RateLimitResult>;
22
+ enforceKey(key: string): Promise<Response | null>;
21
23
  }
22
- export declare function createRateLimiter(kv: Env["CONFIG_KV"], config: RateLimitConfig): RateLimiter;
24
+ export declare function createRateLimiter(kv: Env["CONFIG_KV"] | undefined, config: RateLimitConfig): RateLimiter;
23
25
  export {};
24
26
  //# sourceMappingURL=rate-limit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,eAAe;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAClD,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;CACrD;AAED,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,GAAG,CAAC,WAAW,CAAC,EACpB,MAAM,EAAE,eAAe,GACtB,WAAW,CA0Db"}
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,eAAe;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAClD,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IACpD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAChD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;CACnD;AA6HD,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,GAAG,CAAC,WAAW,CAAC,GAAG,SAAS,EAChC,MAAM,EAAE,eAAe,GACtB,WAAW,CAiHb"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Path Resolution Service for Hoox
3
+ *
4
+ * Provides cross-OS utilities for resolving the $HOME/.hoox directory location
5
+ * and constructing type-safe paths within it.
6
+ *
7
+ * Supports macOS, Linux, and Windows with proper fallback handling.
8
+ */
9
+ /**
10
+ * Branded type for Hoox paths to prevent accidental string usage.
11
+ * This ensures type safety when working with paths.
12
+ */
13
+ export type HooxPath = string & {
14
+ readonly __brand: "HooxPath";
15
+ };
16
+ /**
17
+ * Gets the Hoox home directory location: $HOME/.hoox
18
+ *
19
+ * Behavior:
20
+ * - Returns $HOME/.hoox on macOS, Linux, Windows
21
+ * - Falls back to current working directory if HOME is not available
22
+ * - Resolves to absolute path
23
+ *
24
+ * @returns Absolute path to $HOME/.hoox as a branded HooxPath
25
+ * @throws Never — always returns a valid path
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const hooxHome = getHooxHome();
30
+ * // Returns: "/Users/alice/.hoox" (macOS)
31
+ * // Returns: "/home/alice/.hoox" (Linux)
32
+ * // Returns: "C:\\Users\\alice\\.hoox" (Windows)
33
+ * ```
34
+ */
35
+ export declare function getHooxHome(): HooxPath;
36
+ /**
37
+ * Resolves a relative path within the Hoox home directory.
38
+ *
39
+ * Behavior:
40
+ * - Joins the relative path with $HOME/.hoox
41
+ * - Resolves to absolute path
42
+ * - Prevents path traversal attacks (../ sequences)
43
+ *
44
+ * @param relativePath - Path relative to $HOME/.hoox (e.g., "repo", "config/wrangler.jsonc")
45
+ * @returns Absolute path as a branded HooxPath
46
+ * @throws Error if path contains suspicious traversal patterns
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const repoPath = resolveHooxPath("repo");
51
+ * // Returns: "/Users/alice/.hoox/repo"
52
+ *
53
+ * const configPath = resolveHooxPath("config/wrangler.jsonc");
54
+ * // Returns: "/Users/alice/.hoox/config/wrangler.jsonc"
55
+ * ```
56
+ */
57
+ export declare function resolveHooxPath(relativePath: string): HooxPath;
58
+ /**
59
+ * Checks if a given path is within the Hoox home directory.
60
+ *
61
+ * @param path - Path to check
62
+ * @returns true if path is within $HOME/.hoox, false otherwise
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * isWithinHooxHome("/Users/alice/.hoox/repo"); // true
67
+ * isWithinHooxHome("/Users/alice/other"); // false
68
+ * ```
69
+ */
70
+ export declare function isWithinHooxHome(path: string): boolean;
71
+ /**
72
+ * Gets the relative path from Hoox home directory.
73
+ *
74
+ * @param absolutePath - Absolute path to resolve
75
+ * @returns Relative path from $HOME/.hoox, or null if path is outside Hoox home
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * getRelativeHooxPath("/Users/alice/.hoox/repo/src");
80
+ * // Returns: "repo/src"
81
+ *
82
+ * getRelativeHooxPath("/Users/alice/other");
83
+ * // Returns: null
84
+ * ```
85
+ */
86
+ export declare function getRelativeHooxPath(absolutePath: string): string | null;
87
+ /**
88
+ * Constructs a path to the Hoox repository location.
89
+ *
90
+ * @returns Path to $HOME/.hoox/repo as a branded HooxPath
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * const repoPath = getHooxRepoPath();
95
+ * // Returns: "/Users/alice/.hoox/repo"
96
+ * ```
97
+ */
98
+ export declare function getHooxRepoPath(): HooxPath;
99
+ /**
100
+ * Constructs a path to the Hoox configuration directory.
101
+ *
102
+ * @returns Path to $HOME/.hoox/config as a branded HooxPath
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * const configDir = getHooxConfigDir();
107
+ * // Returns: "/Users/alice/.hoox/config"
108
+ * ```
109
+ */
110
+ export declare function getHooxConfigDir(): HooxPath;
111
+ /**
112
+ * Constructs a path to the Hoox data directory.
113
+ *
114
+ * @returns Path to $HOME/.hoox/data as a branded HooxPath
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const dataDir = getHooxDataDir();
119
+ * // Returns: "/Users/alice/.hoox/data"
120
+ * ```
121
+ */
122
+ export declare function getHooxDataDir(): HooxPath;
123
+ /**
124
+ * Constructs a path to the Hoox wrangler configuration file.
125
+ *
126
+ * @returns Path to $HOME/.hoox/config/wrangler.jsonc as a branded HooxPath
127
+ *
128
+ * @example
129
+ * ```typescript
130
+ * const wranglerPath = getHooxWranglerPath();
131
+ * // Returns: "/Users/alice/.hoox/config/wrangler.jsonc"
132
+ * ```
133
+ */
134
+ export declare function getHooxWranglerPath(): HooxPath;
135
+ /**
136
+ * Constructs a path to the Hoox state file.
137
+ *
138
+ * @returns Path to $HOME/.hoox/data/state.json as a branded HooxPath
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * const statePath = getHooxStatePath();
143
+ * // Returns: "/Users/alice/.hoox/data/state.json"
144
+ * ```
145
+ */
146
+ export declare function getHooxStatePath(): HooxPath;
147
+ //# sourceMappingURL=path-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../src/path-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAAC;AAUjE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAYtC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAiC9D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAStD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkBvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,QAAQ,CAE1C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,IAAI,QAAQ,CAE3C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,IAAI,QAAQ,CAEzC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,IAAI,QAAQ,CAE9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,IAAI,QAAQ,CAE3C"}
@@ -0,0 +1,101 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
+
18
+ // src/path-utils.ts
19
+ import { homedir } from "os";
20
+ import { join, resolve } from "path";
21
+ function createHooxPath(path) {
22
+ return path;
23
+ }
24
+ function getHooxHome() {
25
+ try {
26
+ const home = homedir();
27
+ if (!home || home.length === 0) {
28
+ return createHooxPath(resolve(process.cwd(), ".hoox"));
29
+ }
30
+ return createHooxPath(join(home, ".hoox"));
31
+ } catch {
32
+ return createHooxPath(resolve(process.cwd(), ".hoox"));
33
+ }
34
+ }
35
+ function resolveHooxPath(relativePath) {
36
+ if (!relativePath || typeof relativePath !== "string") {
37
+ throw new Error("relativePath must be a non-empty string");
38
+ }
39
+ if (relativePath.includes("..")) {
40
+ throw new Error(`Path traversal detected in relativePath: "${relativePath}"`);
41
+ }
42
+ const normalizedPath = relativePath.replace(/\\/g, "/");
43
+ const hooxHome = getHooxHome();
44
+ const fullPath = join(hooxHome, normalizedPath);
45
+ const resolvedPath = resolve(fullPath);
46
+ const resolvedHome = resolve(hooxHome);
47
+ if (!resolvedPath.startsWith(resolvedHome)) {
48
+ throw new Error(`Resolved path "${resolvedPath}" is outside Hoox home directory`);
49
+ }
50
+ return createHooxPath(resolvedPath);
51
+ }
52
+ function isWithinHooxHome(path) {
53
+ try {
54
+ const hooxHome = getHooxHome();
55
+ const resolvedPath = resolve(path);
56
+ const resolvedHome = resolve(hooxHome);
57
+ return resolvedPath.startsWith(resolvedHome);
58
+ } catch {
59
+ return false;
60
+ }
61
+ }
62
+ function getRelativeHooxPath(absolutePath) {
63
+ try {
64
+ if (!isWithinHooxHome(absolutePath)) {
65
+ return null;
66
+ }
67
+ const hooxHome = getHooxHome();
68
+ const resolvedPath = resolve(absolutePath);
69
+ const resolvedHome = resolve(hooxHome);
70
+ const relativePath = resolvedPath.slice(resolvedHome.length);
71
+ return relativePath.startsWith("/") || relativePath.startsWith("\\") ? relativePath.slice(1) : relativePath;
72
+ } catch {
73
+ return null;
74
+ }
75
+ }
76
+ function getHooxRepoPath() {
77
+ return resolveHooxPath("repo");
78
+ }
79
+ function getHooxConfigDir() {
80
+ return resolveHooxPath("config");
81
+ }
82
+ function getHooxDataDir() {
83
+ return resolveHooxPath("data");
84
+ }
85
+ function getHooxWranglerPath() {
86
+ return resolveHooxPath("config/wrangler.jsonc");
87
+ }
88
+ function getHooxStatePath() {
89
+ return resolveHooxPath("data/state.json");
90
+ }
91
+ export {
92
+ resolveHooxPath,
93
+ isWithinHooxHome,
94
+ getRelativeHooxPath,
95
+ getHooxWranglerPath,
96
+ getHooxStatePath,
97
+ getHooxRepoPath,
98
+ getHooxHome,
99
+ getHooxDataDir,
100
+ getHooxConfigDir
101
+ };