@jango-blockchained/hoox-shared 1.0.8 → 1.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.
Files changed (102) hide show
  1. package/README.md +17 -0
  2. package/dist/analytics.d.ts +23 -2
  3. package/dist/analytics.d.ts.map +1 -1
  4. package/dist/analytics.js +14 -3
  5. package/dist/api-client.d.ts +12 -2
  6. package/dist/api-client.d.ts.map +1 -1
  7. package/dist/api-client.js +74 -7
  8. package/dist/colors.d.ts +63 -25
  9. package/dist/colors.d.ts.map +1 -1
  10. package/dist/colors.js +60 -20
  11. package/dist/config.d.ts +34 -1
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js +61 -6
  14. package/dist/cron-handler.d.ts +34 -0
  15. package/dist/cron-handler.d.ts.map +1 -0
  16. package/dist/cron-handler.js +48 -0
  17. package/dist/d1/index.js +50 -6
  18. package/dist/d1/repository.d.ts +5 -0
  19. package/dist/d1/repository.d.ts.map +1 -1
  20. package/dist/d1/repository.js +297 -0
  21. package/dist/d1/schemas.js +81 -0
  22. package/dist/errors.d.ts +1 -0
  23. package/dist/errors.d.ts.map +1 -1
  24. package/dist/errors.js +48 -5
  25. package/dist/exchanges/base-exchange-client.d.ts +68 -0
  26. package/dist/exchanges/base-exchange-client.d.ts.map +1 -0
  27. package/dist/exchanges/base-exchange-client.js +120 -0
  28. package/dist/exchanges/index.d.ts +3 -0
  29. package/dist/exchanges/index.d.ts.map +1 -0
  30. package/dist/exchanges/index.js +120 -0
  31. package/dist/exchanges/types.d.ts +19 -0
  32. package/dist/exchanges/types.d.ts.map +1 -0
  33. package/dist/exchanges/types.js +1 -0
  34. package/dist/health.js +48 -5
  35. package/dist/index.d.ts +30 -11
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +1058 -73
  38. package/dist/kvUtils.js +48 -5
  39. package/dist/middleware/auth.d.ts +46 -7
  40. package/dist/middleware/auth.d.ts.map +1 -1
  41. package/dist/middleware/auth.js +151 -0
  42. package/dist/middleware/cors.d.ts +28 -2
  43. package/dist/middleware/cors.d.ts.map +1 -1
  44. package/dist/middleware/cors.js +77 -0
  45. package/dist/middleware/index.d.ts +2 -2
  46. package/dist/middleware/index.d.ts.map +1 -1
  47. package/dist/middleware/index.js +361 -101
  48. package/dist/middleware/logger.d.ts +2 -1
  49. package/dist/middleware/logger.d.ts.map +1 -1
  50. package/dist/middleware/logger.js +202 -0
  51. package/dist/middleware/rate-limit.d.ts +3 -1
  52. package/dist/middleware/rate-limit.d.ts.map +1 -1
  53. package/dist/middleware/rate-limit.js +181 -0
  54. package/dist/middleware/security-headers.js +58 -0
  55. package/dist/middleware/validate.js +56 -0
  56. package/dist/operator-transport.d.ts +61 -0
  57. package/dist/operator-transport.d.ts.map +1 -0
  58. package/dist/operator-transport.js +87 -0
  59. package/dist/path-utils.d.ts +205 -0
  60. package/dist/path-utils.d.ts.map +1 -0
  61. package/dist/path-utils.js +188 -0
  62. package/dist/queue-handler.d.ts +43 -0
  63. package/dist/queue-handler.d.ts.map +1 -0
  64. package/dist/queue-handler.js +46 -0
  65. package/dist/router.d.ts +2 -2
  66. package/dist/router.d.ts.map +1 -1
  67. package/dist/router.js +15 -8
  68. package/dist/schemas/index.js +7 -4
  69. package/dist/schemas/registry.js +387 -0
  70. package/dist/schemas/types.js +1 -0
  71. package/dist/schemas/validators.d.ts.map +1 -1
  72. package/dist/schemas/validators.js +290 -0
  73. package/dist/service-bindings.d.ts +68 -26
  74. package/dist/service-bindings.d.ts.map +1 -1
  75. package/dist/service-bindings.js +219 -2
  76. package/dist/session.d.ts +9 -5
  77. package/dist/session.d.ts.map +1 -1
  78. package/dist/session.js +179 -4
  79. package/dist/sse.d.ts +12 -3
  80. package/dist/sse.d.ts.map +1 -1
  81. package/dist/sse.js +70 -7
  82. package/dist/stores/config-store.d.ts.map +1 -1
  83. package/dist/stores/config-store.js +129 -6
  84. package/dist/stores/service-store.d.ts +50 -1
  85. package/dist/stores/service-store.d.ts.map +1 -1
  86. package/dist/stores/service-store.js +162 -27
  87. package/dist/test-utils.d.ts +132 -0
  88. package/dist/test-utils.d.ts.map +1 -0
  89. package/dist/test-utils.js +128 -0
  90. package/dist/types/router.d.ts +18 -8
  91. package/dist/types/router.d.ts.map +1 -1
  92. package/dist/types.d.ts +124 -5
  93. package/dist/types.d.ts.map +1 -1
  94. package/dist/types.js +11 -0
  95. package/dist/wizard/engine.d.ts.map +1 -1
  96. package/dist/wizard/engine.js +501 -0
  97. package/dist/wizard/index.js +2 -1
  98. package/dist/wizard/persistence.js +31 -0
  99. package/dist/wizard/presets.js +199 -0
  100. package/dist/wizard/provisioner.js +1 -0
  101. package/dist/wizard/types.js +1 -0
  102. package/package.json +46 -12
@@ -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) {
@@ -98,6 +141,184 @@ var Errors = {
98
141
  }
99
142
  };
100
143
 
144
+ // src/middleware/auth.ts
145
+ function timingSafeEqual(a, b) {
146
+ const encoder = new TextEncoder;
147
+ const aBuf = encoder.encode(a);
148
+ const bBuf = encoder.encode(b);
149
+ const len = Math.max(aBuf.length, bBuf.length);
150
+ let result = aBuf.length === bBuf.length ? 0 : 1;
151
+ for (let i = 0;i < len; i++) {
152
+ const av = i < aBuf.length ? aBuf[i] : 0;
153
+ const bv = i < bBuf.length ? bBuf[i] : 0;
154
+ result |= av ^ bv;
155
+ }
156
+ return result === 0;
157
+ }
158
+ function resolveOperatorApiKey(env) {
159
+ const preferred = env.OPERATOR_API_KEY;
160
+ if (typeof preferred === "string" && preferred.length > 0) {
161
+ return preferred;
162
+ }
163
+ const legacy = env.INTERNAL_API_KEY;
164
+ if (typeof legacy === "string" && legacy.length > 0) {
165
+ return legacy;
166
+ }
167
+ return;
168
+ }
169
+ async function requireAuth(request, env) {
170
+ return requireOperatorAuth(request, env);
171
+ }
172
+ async function requireOperatorAuth(request, env) {
173
+ const apiKey = resolveOperatorApiKey(env);
174
+ if (!apiKey) {
175
+ return new Response(JSON.stringify({
176
+ error: "Operator API key not configured",
177
+ hint: "Set OPERATOR_API_KEY (preferred) or INTERNAL_API_KEY on the Worker"
178
+ }), { status: 401, headers: { "Content-Type": "application/json" } });
179
+ }
180
+ const authHeader = request.headers.get("Authorization");
181
+ const expectedHeader = `Bearer ${apiKey}`;
182
+ if (!authHeader || !timingSafeEqual(authHeader, expectedHeader)) {
183
+ return new Response(JSON.stringify({ error: "Unauthorized" }), {
184
+ status: 401,
185
+ headers: { "Content-Type": "application/json" }
186
+ });
187
+ }
188
+ return null;
189
+ }
190
+ function createOperatorAuthMiddleware() {
191
+ return async (request, env, _ctx) => {
192
+ const denied = await requireOperatorAuth(request, env);
193
+ if (denied)
194
+ return denied;
195
+ return;
196
+ };
197
+ }
198
+ function normalizeKeyNames(keyName) {
199
+ if (typeof keyName === "string") {
200
+ return [keyName];
201
+ }
202
+ return [...keyName];
203
+ }
204
+ function collectInternalAuthKeys(env, keyNames) {
205
+ const keys = [];
206
+ for (const name of normalizeKeyNames(keyNames)) {
207
+ const value = env[name];
208
+ if (typeof value === "string" && value.length > 0) {
209
+ keys.push(value);
210
+ }
211
+ }
212
+ return keys;
213
+ }
214
+ function matchesAnyInternalAuthKey(providedKey, expectedKeys) {
215
+ for (const expected of expectedKeys) {
216
+ if (timingSafeEqual(providedKey, expected)) {
217
+ return true;
218
+ }
219
+ }
220
+ return false;
221
+ }
222
+ function internalAuthNotConfiguredResponse(keyNames) {
223
+ const label = normalizeKeyNames(keyNames).join(" | ");
224
+ return new Response(JSON.stringify({
225
+ success: false,
226
+ error: `Internal auth key(s) not configured: ${label}`
227
+ }), { status: 401, headers: { "Content-Type": "application/json" } });
228
+ }
229
+ function requireInternalAuth(request, env, keyName = "INTERNAL_KEY_BINDING") {
230
+ const expectedKeys = collectInternalAuthKeys(env, keyName);
231
+ if (expectedKeys.length === 0) {
232
+ return internalAuthNotConfiguredResponse(keyName);
233
+ }
234
+ const providedKey = request.headers.get("X-Internal-Auth-Key");
235
+ if (!providedKey || !matchesAnyInternalAuthKey(providedKey, expectedKeys)) {
236
+ return new Response(JSON.stringify({ success: false, error: "Unauthorized" }), { status: 401, headers: { "Content-Type": "application/json" } });
237
+ }
238
+ return null;
239
+ }
240
+ function createInternalAuthMiddleware(keyName = "INTERNAL_KEY_BINDING") {
241
+ return async (request, env, _ctx) => {
242
+ const expectedKeys = collectInternalAuthKeys(env, keyName);
243
+ if (expectedKeys.length === 0) {
244
+ return internalAuthNotConfiguredResponse(keyName);
245
+ }
246
+ const providedKey = request.headers.get("X-Internal-Auth-Key");
247
+ if (!providedKey || !matchesAnyInternalAuthKey(providedKey, expectedKeys)) {
248
+ return new Response(JSON.stringify({ success: false, error: "Unauthorized" }), { status: 401, headers: { "Content-Type": "application/json" } });
249
+ }
250
+ return;
251
+ };
252
+ }
253
+ function checkInternalAuth(request, env, keyName = "INTERNAL_KEY_BINDING") {
254
+ const expectedKeys = collectInternalAuthKeys(env, keyName);
255
+ if (expectedKeys.length === 0) {
256
+ return {
257
+ authorized: false,
258
+ error: `${normalizeKeyNames(keyName).join(" | ")} not configured`
259
+ };
260
+ }
261
+ const providedKey = request.headers.get("X-Internal-Auth-Key");
262
+ if (!providedKey || !matchesAnyInternalAuthKey(providedKey, expectedKeys)) {
263
+ return { authorized: false, error: "Unauthorized" };
264
+ }
265
+ return { authorized: true };
266
+ }
267
+
268
+ // src/middleware/cors.ts
269
+ var DEFAULT_OPTIONS = {
270
+ allowOrigin: "",
271
+ allowMethods: "GET, POST, OPTIONS, PUT, DELETE",
272
+ allowHeaders: "Content-Type, Authorization, X-Request-ID",
273
+ allowCredentials: false,
274
+ maxAge: 86400
275
+ };
276
+ function corsHeaders(options) {
277
+ const opts = { ...DEFAULT_OPTIONS, ...options };
278
+ const headers = {
279
+ "Access-Control-Allow-Methods": opts.allowMethods,
280
+ "Access-Control-Allow-Headers": opts.allowHeaders,
281
+ "Access-Control-Max-Age": String(opts.maxAge)
282
+ };
283
+ if (opts.allowOrigin) {
284
+ headers["Access-Control-Allow-Origin"] = opts.allowOrigin;
285
+ }
286
+ if (opts.allowCredentials) {
287
+ headers["Access-Control-Allow-Credentials"] = "true";
288
+ }
289
+ return headers;
290
+ }
291
+ function publicCorsHeaders(origin = "*", options) {
292
+ return corsHeaders({ ...options, allowOrigin: origin });
293
+ }
294
+ function internalCorsHeaders() {
295
+ return {};
296
+ }
297
+ function resolveCorsOptions(request, env) {
298
+ const raw = env && typeof env === "object" && "CORS_ALLOW_ORIGIN" in env && typeof env.CORS_ALLOW_ORIGIN === "string" ? env.CORS_ALLOW_ORIGIN : undefined;
299
+ const configured = raw?.trim();
300
+ if (!configured) {
301
+ return {};
302
+ }
303
+ const allowed = configured.split(",").map((entry) => entry.trim()).filter(Boolean);
304
+ const origin = request.headers.get("Origin");
305
+ if (origin && allowed.includes(origin)) {
306
+ return { allowOrigin: origin, allowCredentials: true };
307
+ }
308
+ if (!origin && allowed.length === 1) {
309
+ return { allowOrigin: allowed[0] };
310
+ }
311
+ return {};
312
+ }
313
+ function handleCorsPreflightRequest(request, options) {
314
+ if (request.method !== "OPTIONS")
315
+ return null;
316
+ return new Response(null, {
317
+ status: 204,
318
+ headers: corsHeaders(options)
319
+ });
320
+ }
321
+
101
322
  // src/middleware/logger.ts
102
323
  function createLogger(ctx) {
103
324
  const base = { service: ctx.service, module: ctx.module };
@@ -114,10 +335,13 @@ function createLogger(ctx) {
114
335
  console.error(line);
115
336
  else if (level === "warn")
116
337
  console.warn(line);
338
+ else if (level === "debug")
339
+ console.debug(line);
117
340
  else
118
341
  console.info(line);
119
342
  }
120
343
  return {
344
+ debug: (msg, ctx2) => emit("debug", msg, ctx2),
121
345
  info: (msg, ctx2) => emit("info", msg, ctx2),
122
346
  warn: (msg, ctx2) => emit("warn", msg, ctx2),
123
347
  error: (msg, ctx2) => emit("error", msg, ctx2)
@@ -150,69 +374,91 @@ function withRequestLog(handler, logCtx) {
150
374
  }
151
375
  };
152
376
  }
153
- // src/middleware/auth.ts
154
- function timingSafeEqual(a, b) {
155
- if (a.length !== b.length)
156
- return false;
157
- const encoder = new TextEncoder;
158
- const aBuf = encoder.encode(a);
159
- const bBuf = encoder.encode(b);
160
- let result = 0;
161
- for (let i = 0;i < aBuf.length; i++) {
162
- result |= aBuf[i] ^ bBuf[i];
163
- }
164
- return result === 0;
165
- }
166
- async function requireAuth(request, env) {
167
- const apiKey = env.INTERNAL_API_KEY;
168
- if (!apiKey) {
169
- return new Response(JSON.stringify({ error: "Internal API key not configured" }), { status: 401, headers: { "Content-Type": "application/json" } });
170
- }
171
- const authHeader = request.headers.get("Authorization");
172
- const expectedHeader = `Bearer ${apiKey}`;
173
- if (!authHeader || !timingSafeEqual(authHeader, expectedHeader)) {
174
- return new Response(JSON.stringify({ error: "Unauthorized" }), {
175
- status: 401,
176
- headers: { "Content-Type": "application/json" }
177
- });
178
- }
179
- return null;
180
- }
181
- function requireInternalAuth(request, env, keyName = "INTERNAL_KEY_BINDING") {
182
- const expectedKey = env[keyName];
183
- if (!expectedKey) {
184
- return new Response(JSON.stringify({
185
- success: false,
186
- error: `Internal auth key ${keyName} not configured`
187
- }), { status: 401, headers: { "Content-Type": "application/json" } });
188
- }
189
- const providedKey = request.headers.get("X-Internal-Auth-Key");
190
- if (!providedKey || !timingSafeEqual(providedKey, expectedKey)) {
191
- return new Response(JSON.stringify({ success: false, error: "Unauthorized" }), { status: 401, headers: { "Content-Type": "application/json" } });
377
+
378
+ // src/middleware/rate-limit.ts
379
+ function createStorage(kv) {
380
+ if (!kv) {
381
+ const memory = new Map;
382
+ return {
383
+ async get(key) {
384
+ const entry = memory.get(key);
385
+ if (!entry)
386
+ return null;
387
+ if (Date.now() > entry.expiresAt) {
388
+ memory.delete(key);
389
+ return null;
390
+ }
391
+ return entry.value;
392
+ },
393
+ async put(key, value, opts) {
394
+ const ttl = opts?.expirationTtl ?? 0;
395
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
396
+ memory.set(key, { value, expiresAt });
397
+ },
398
+ async incr(key, opts) {
399
+ let entry = memory.get(key);
400
+ let newValue;
401
+ let attempts = 0;
402
+ const maxAttempts = 100;
403
+ while (true) {
404
+ const count = entry ? parseInt(entry.value, 10) : 0;
405
+ newValue = count + 1;
406
+ if (!entry) {
407
+ const ttl = opts?.expirationTtl ?? 0;
408
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
409
+ if (memory.get(key) === undefined) {
410
+ memory.set(key, { value: String(newValue), expiresAt });
411
+ return newValue;
412
+ }
413
+ entry = memory.get(key);
414
+ } else {
415
+ if (Date.now() > entry.expiresAt) {
416
+ memory.delete(key);
417
+ entry = undefined;
418
+ continue;
419
+ }
420
+ const currentEntry = memory.get(key);
421
+ if (currentEntry === entry) {
422
+ const ttl = opts?.expirationTtl ?? 0;
423
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
424
+ memory.set(key, { value: String(newValue), expiresAt });
425
+ return newValue;
426
+ }
427
+ entry = currentEntry;
428
+ }
429
+ attempts++;
430
+ if (attempts >= maxAttempts) {
431
+ const current = await this.get(key);
432
+ const count2 = current ? parseInt(current, 10) : 0;
433
+ newValue = count2 + 1;
434
+ const ttl = opts?.expirationTtl ?? 0;
435
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
436
+ memory.set(key, { value: String(newValue), expiresAt });
437
+ return newValue;
438
+ }
439
+ }
440
+ }
441
+ };
192
442
  }
193
- return null;
194
- }
195
- function createInternalAuthMiddleware(keyName = "INTERNAL_KEY_BINDING") {
196
- return (request, env) => {
197
- const result = requireInternalAuth(request, env, keyName);
198
- if (result)
199
- return result;
443
+ return {
444
+ async get(key) {
445
+ return kv.get(key);
446
+ },
447
+ async put(key, value, opts) {
448
+ await kv.put(key, value, opts);
449
+ },
450
+ async incr(key, opts) {
451
+ const current = await kv.get(key);
452
+ const count = current ? parseInt(current, 10) : 0;
453
+ const next = count + 1;
454
+ await kv.put(key, String(next), opts);
455
+ return next;
456
+ }
200
457
  };
201
458
  }
202
- function checkInternalAuth(request, env, keyName = "INTERNAL_KEY_BINDING") {
203
- const expectedKey = env[keyName];
204
- if (!expectedKey) {
205
- return { authorized: false, error: `${keyName} not configured` };
206
- }
207
- const providedKey = request.headers.get("X-Internal-Auth-Key");
208
- if (!providedKey || !timingSafeEqual(providedKey, expectedKey)) {
209
- return { authorized: false, error: "Unauthorized" };
210
- }
211
- return { authorized: true };
212
- }
213
- // src/middleware/rate-limit.ts
214
459
  function createRateLimiter(kv, config) {
215
460
  const prefix = config.keyPrefix ?? "rate-limit";
461
+ const storage = createStorage(kv);
216
462
  function getClientIp(request) {
217
463
  return request.headers.get("CF-Connecting-IP") ?? "unknown";
218
464
  }
@@ -220,23 +466,37 @@ function createRateLimiter(kv, config) {
220
466
  const windowStart = Math.floor(Date.now() / (config.windowSeconds * 1000));
221
467
  return `${prefix}:${ip}:${windowStart}`;
222
468
  }
223
- async function check(request) {
224
- const ip = getClientIp(request);
225
- const key = getWindowKey(ip);
226
- const current = await kv.get(key);
469
+ async function checkWithKey(fullKey) {
470
+ const current = await storage.get(fullKey);
227
471
  const count = current ? parseInt(current, 10) : 0;
228
472
  if (count >= config.maxRequests) {
229
- const retryAfter = config.windowSeconds - Date.now() % (config.windowSeconds * 1000) / 1000;
473
+ const retryAfter = Math.ceil(config.windowSeconds - Date.now() % (config.windowSeconds * 1000) / 1000);
230
474
  return {
231
475
  allowed: false,
232
476
  remaining: 0,
233
- retryAfter: Math.ceil(retryAfter)
477
+ retryAfter
234
478
  };
235
479
  }
236
- await kv.put(key, String(count + 1), {
480
+ const newCount = await storage.incr(fullKey, {
237
481
  expirationTtl: config.windowSeconds
238
482
  });
239
- return { allowed: true, remaining: config.maxRequests - count - 1 };
483
+ if (newCount > config.maxRequests) {
484
+ const retryAfter = Math.ceil(config.windowSeconds - Date.now() % (config.windowSeconds * 1000) / 1000);
485
+ return {
486
+ allowed: false,
487
+ remaining: 0,
488
+ retryAfter
489
+ };
490
+ }
491
+ return {
492
+ allowed: true,
493
+ remaining: config.maxRequests - newCount
494
+ };
495
+ }
496
+ async function check(request) {
497
+ const ip = getClientIp(request);
498
+ const key = getWindowKey(ip);
499
+ return checkWithKey(key);
240
500
  }
241
501
  async function enforce(request) {
242
502
  const result = await check(request);
@@ -254,8 +514,29 @@ function createRateLimiter(kv, config) {
254
514
  }
255
515
  return null;
256
516
  }
257
- return { check, enforce };
517
+ async function checkKey(key) {
518
+ const fullKey = `${prefix}:${key}`;
519
+ return checkWithKey(fullKey);
520
+ }
521
+ async function enforceKey(key) {
522
+ const result = await checkKey(key);
523
+ if (!result.allowed) {
524
+ return new Response(JSON.stringify({
525
+ error: "Rate limit exceeded",
526
+ retryAfter: result.retryAfter
527
+ }), {
528
+ status: 429,
529
+ headers: {
530
+ "Content-Type": "application/json",
531
+ "Retry-After": String(result.retryAfter ?? config.windowSeconds)
532
+ }
533
+ });
534
+ }
535
+ return null;
536
+ }
537
+ return { check, enforce, checkKey, enforceKey };
258
538
  }
539
+
259
540
  // src/middleware/validate.ts
260
541
  function validateJson(schema, data) {
261
542
  const result = schema.safeParse(data);
@@ -289,35 +570,7 @@ function optionalField(body, field, defaultValue) {
289
570
  return defaultValue;
290
571
  return body[field];
291
572
  }
292
- // src/middleware/cors.ts
293
- var DEFAULT_OPTIONS = {
294
- allowOrigin: "*",
295
- allowMethods: "GET, POST, OPTIONS, PUT, DELETE",
296
- allowHeaders: "Content-Type, Authorization, X-Request-ID, X-Internal-Auth-Key",
297
- allowCredentials: false,
298
- maxAge: 86400
299
- };
300
- function corsHeaders(options) {
301
- const opts = { ...DEFAULT_OPTIONS, ...options };
302
- const headers = {
303
- "Access-Control-Allow-Origin": opts.allowOrigin,
304
- "Access-Control-Allow-Methods": opts.allowMethods,
305
- "Access-Control-Allow-Headers": opts.allowHeaders,
306
- "Access-Control-Max-Age": String(opts.maxAge)
307
- };
308
- if (opts.allowCredentials) {
309
- headers["Access-Control-Allow-Credentials"] = "true";
310
- }
311
- return headers;
312
- }
313
- function handleCorsPreflightRequest(request, options) {
314
- if (request.method !== "OPTIONS")
315
- return null;
316
- return new Response(null, {
317
- status: 204,
318
- headers: corsHeaders(options)
319
- });
320
- }
573
+
321
574
  // src/middleware/security-headers.ts
322
575
  var SECURITY_HEADERS_DEFAULTS = {
323
576
  xContentTypeOptions: "nosniff",
@@ -361,15 +614,22 @@ export {
361
614
  validateJson,
362
615
  timingSafeEqual,
363
616
  secureHeaders,
617
+ resolveOperatorApiKey,
618
+ resolveCorsOptions,
619
+ requireOperatorAuth,
364
620
  requireInternalAuth,
365
621
  requireField,
366
622
  requireAuth,
623
+ publicCorsHeaders,
367
624
  optionalField,
625
+ internalCorsHeaders,
368
626
  handleCorsPreflightRequest,
369
627
  createRateLimiter,
628
+ createOperatorAuthMiddleware,
370
629
  createLogger,
371
630
  createInternalAuthMiddleware,
372
631
  corsHeaders,
632
+ collectInternalAuthKeys,
373
633
  checkInternalAuth,
374
634
  SECURITY_HEADERS_DEFAULTS
375
635
  };
@@ -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"}