@hoox-sh/hoox-shared 1.2.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 (161) hide show
  1. package/README.md +44 -0
  2. package/dist/agent-config-fields.d.ts +32 -0
  3. package/dist/agent-config-fields.d.ts.map +1 -0
  4. package/dist/agent-config-fields.js +220 -0
  5. package/dist/analytics.d.ts +47 -0
  6. package/dist/analytics.d.ts.map +1 -0
  7. package/dist/analytics.js +45 -0
  8. package/dist/api-client.d.ts +48 -0
  9. package/dist/api-client.d.ts.map +1 -0
  10. package/dist/api-client.js +191 -0
  11. package/dist/colors.d.ts +76 -0
  12. package/dist/colors.d.ts.map +1 -0
  13. package/dist/colors.js +82 -0
  14. package/dist/config.d.ts +61 -0
  15. package/dist/config.d.ts.map +1 -0
  16. package/dist/config.js +146 -0
  17. package/dist/cron-handler.d.ts +38 -0
  18. package/dist/cron-handler.d.ts.map +1 -0
  19. package/dist/cron-handler.js +48 -0
  20. package/dist/d1/index.d.ts +9 -0
  21. package/dist/d1/index.d.ts.map +1 -0
  22. package/dist/d1/index.js +360 -0
  23. package/dist/d1/repository.d.ts +86 -0
  24. package/dist/d1/repository.d.ts.map +1 -0
  25. package/dist/d1/repository.js +297 -0
  26. package/dist/d1/schemas.d.ts +89 -0
  27. package/dist/d1/schemas.d.ts.map +1 -0
  28. package/dist/d1/schemas.js +81 -0
  29. package/dist/dashboard-manifest.d.ts +135 -0
  30. package/dist/dashboard-manifest.d.ts.map +1 -0
  31. package/dist/dashboard-manifest.js +401 -0
  32. package/dist/errors.d.ts +44 -0
  33. package/dist/errors.d.ts.map +1 -0
  34. package/dist/errors.js +149 -0
  35. package/dist/exchange-client.d.ts +98 -0
  36. package/dist/exchange-client.d.ts.map +1 -0
  37. package/dist/exchange-client.js +107 -0
  38. package/dist/exchanges/base-exchange-client.d.ts +81 -0
  39. package/dist/exchanges/base-exchange-client.d.ts.map +1 -0
  40. package/dist/exchanges/base-exchange-client.js +123 -0
  41. package/dist/exchanges/index.d.ts +7 -0
  42. package/dist/exchanges/index.d.ts.map +1 -0
  43. package/dist/exchanges/index.js +123 -0
  44. package/dist/exchanges/types.d.ts +23 -0
  45. package/dist/exchanges/types.d.ts.map +1 -0
  46. package/dist/exchanges/types.js +1 -0
  47. package/dist/format-time.d.ts +28 -0
  48. package/dist/format-time.d.ts.map +1 -0
  49. package/dist/format-time.js +57 -0
  50. package/dist/formatters.d.ts +36 -0
  51. package/dist/formatters.d.ts.map +1 -0
  52. package/dist/formatters.js +155 -0
  53. package/dist/health.d.ts +19 -0
  54. package/dist/health.d.ts.map +1 -0
  55. package/dist/health.js +160 -0
  56. package/dist/index.d.ts +72 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +4198 -0
  59. package/dist/kvKeys.d.ts +76 -0
  60. package/dist/kvKeys.d.ts.map +1 -0
  61. package/dist/kvKeys.js +107 -0
  62. package/dist/kvUtils.d.ts +30 -0
  63. package/dist/kvUtils.d.ts.map +1 -0
  64. package/dist/kvUtils.js +170 -0
  65. package/dist/legal.d.ts +15 -0
  66. package/dist/legal.d.ts.map +1 -0
  67. package/dist/legal.js +40 -0
  68. package/dist/middleware/auth.d.ts +102 -0
  69. package/dist/middleware/auth.d.ts.map +1 -0
  70. package/dist/middleware/auth.js +151 -0
  71. package/dist/middleware/cors.d.ts +58 -0
  72. package/dist/middleware/cors.d.ts.map +1 -0
  73. package/dist/middleware/cors.js +77 -0
  74. package/dist/middleware/index.d.ts +14 -0
  75. package/dist/middleware/index.d.ts.map +1 -0
  76. package/dist/middleware/index.js +635 -0
  77. package/dist/middleware/logger.d.ts +17 -0
  78. package/dist/middleware/logger.d.ts.map +1 -0
  79. package/dist/middleware/logger.js +202 -0
  80. package/dist/middleware/rate-limit.d.ts +30 -0
  81. package/dist/middleware/rate-limit.d.ts.map +1 -0
  82. package/dist/middleware/rate-limit.js +181 -0
  83. package/dist/middleware/security-headers.d.ts +43 -0
  84. package/dist/middleware/security-headers.d.ts.map +1 -0
  85. package/dist/middleware/security-headers.js +58 -0
  86. package/dist/middleware/validate.d.ts +25 -0
  87. package/dist/middleware/validate.d.ts.map +1 -0
  88. package/dist/middleware/validate.js +56 -0
  89. package/dist/operator-transport.d.ts +65 -0
  90. package/dist/operator-transport.d.ts.map +1 -0
  91. package/dist/operator-transport.js +87 -0
  92. package/dist/path-utils.d.ts +190 -0
  93. package/dist/path-utils.d.ts.map +1 -0
  94. package/dist/path-utils.js +188 -0
  95. package/dist/queue-handler.d.ts +47 -0
  96. package/dist/queue-handler.d.ts.map +1 -0
  97. package/dist/queue-handler.js +46 -0
  98. package/dist/router.d.ts +15 -0
  99. package/dist/router.d.ts.map +1 -0
  100. package/dist/router.js +130 -0
  101. package/dist/schemas/index.d.ts +8 -0
  102. package/dist/schemas/index.d.ts.map +1 -0
  103. package/dist/schemas/index.js +705 -0
  104. package/dist/schemas/registry.d.ts +12 -0
  105. package/dist/schemas/registry.d.ts.map +1 -0
  106. package/dist/schemas/registry.js +433 -0
  107. package/dist/schemas/types.d.ts +83 -0
  108. package/dist/schemas/types.d.ts.map +1 -0
  109. package/dist/schemas/types.js +1 -0
  110. package/dist/schemas/validators.d.ts +39 -0
  111. package/dist/schemas/validators.d.ts.map +1 -0
  112. package/dist/schemas/validators.js +290 -0
  113. package/dist/service-bindings.d.ts +78 -0
  114. package/dist/service-bindings.d.ts.map +1 -0
  115. package/dist/service-bindings.js +248 -0
  116. package/dist/session.d.ts +39 -0
  117. package/dist/session.d.ts.map +1 -0
  118. package/dist/session.js +261 -0
  119. package/dist/sse.d.ts +39 -0
  120. package/dist/sse.d.ts.map +1 -0
  121. package/dist/sse.js +154 -0
  122. package/dist/stores/config-store.d.ts +91 -0
  123. package/dist/stores/config-store.d.ts.map +1 -0
  124. package/dist/stores/config-store.js +255 -0
  125. package/dist/stores/service-store.d.ts +266 -0
  126. package/dist/stores/service-store.d.ts.map +1 -0
  127. package/dist/stores/service-store.js +516 -0
  128. package/dist/stores/ui-store.d.ts +67 -0
  129. package/dist/stores/ui-store.d.ts.map +1 -0
  130. package/dist/stores/ui-store.js +62 -0
  131. package/dist/test-utils.d.ts +136 -0
  132. package/dist/test-utils.d.ts.map +1 -0
  133. package/dist/test-utils.js +128 -0
  134. package/dist/types/errors.d.ts +20 -0
  135. package/dist/types/errors.d.ts.map +1 -0
  136. package/dist/types/errors.js +1 -0
  137. package/dist/types/router.d.ts +36 -0
  138. package/dist/types/router.d.ts.map +1 -0
  139. package/dist/types/router.js +1 -0
  140. package/dist/types.d.ts +442 -0
  141. package/dist/types.d.ts.map +1 -0
  142. package/dist/types.js +83 -0
  143. package/dist/wizard/engine.d.ts +46 -0
  144. package/dist/wizard/engine.d.ts.map +1 -0
  145. package/dist/wizard/engine.js +525 -0
  146. package/dist/wizard/index.d.ts +11 -0
  147. package/dist/wizard/index.d.ts.map +1 -0
  148. package/dist/wizard/index.js +544 -0
  149. package/dist/wizard/persistence.d.ts +18 -0
  150. package/dist/wizard/persistence.d.ts.map +1 -0
  151. package/dist/wizard/persistence.js +31 -0
  152. package/dist/wizard/presets.d.ts +38 -0
  153. package/dist/wizard/presets.d.ts.map +1 -0
  154. package/dist/wizard/presets.js +223 -0
  155. package/dist/wizard/provisioner.d.ts +15 -0
  156. package/dist/wizard/provisioner.d.ts.map +1 -0
  157. package/dist/wizard/provisioner.js +1 -0
  158. package/dist/wizard/types.d.ts +76 -0
  159. package/dist/wizard/types.d.ts.map +1 -0
  160. package/dist/wizard/types.js +1 -0
  161. package/package.json +117 -0
@@ -0,0 +1,202 @@
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/errors.ts
19
+ function toError(err, fallback = "Unknown error") {
20
+ if (err instanceof Error)
21
+ return err.message;
22
+ if (typeof err === "string")
23
+ return err;
24
+ if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
25
+ return err.message;
26
+ }
27
+ if (err === null || err === undefined)
28
+ return fallback;
29
+ try {
30
+ return JSON.stringify(err) || fallback;
31
+ } catch {
32
+ return fallback;
33
+ }
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
+ }
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;
63
+ if (data instanceof Error) {
64
+ return { name: data.name, message: data.message };
65
+ }
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
+ }
85
+ const sanitized = {};
86
+ for (const k of keys) {
87
+ if (k === "stack" || k === "cause")
88
+ continue;
89
+ const v = obj[k];
90
+ sanitized[k] = v instanceof Error ? { name: v.name, message: v.message } : sanitizeOutput(v);
91
+ }
92
+ return sanitized;
93
+ }
94
+ return data;
95
+ }
96
+ function createJsonResponse(data, status = 200) {
97
+ return new Response(JSON.stringify(sanitizeOutput(data)), {
98
+ status,
99
+ headers: { "Content-Type": "application/json" }
100
+ });
101
+ }
102
+ function createSuccessResponse(result) {
103
+ return createJsonResponse({ success: true, result }, 200);
104
+ }
105
+ function createErrorResponse(error, status) {
106
+ const message = typeof error === "string" ? error : error.message;
107
+ const statusCode = typeof error === "string" ? status ?? 500 : error.status ?? 500;
108
+ const body = { success: false, error: message };
109
+ if (typeof error !== "string" && error.code)
110
+ body.code = error.code;
111
+ if (typeof error !== "string" && error.details !== undefined)
112
+ body.details = error.details;
113
+ return new Response(JSON.stringify(body), {
114
+ status: statusCode,
115
+ headers: { "Content-Type": "application/json" }
116
+ });
117
+ }
118
+ var Errors = {
119
+ badRequest: (message) => createErrorResponse({ message, status: 400, code: "BAD_REQUEST" }),
120
+ unauthorized: (message = "Unauthorized") => createErrorResponse({ message, status: 401, code: "UNAUTHORIZED" }),
121
+ forbidden: (message = "Forbidden") => createErrorResponse({ message, status: 403, code: "FORBIDDEN" }),
122
+ notFound: (message = "Not found") => createErrorResponse({ message, status: 404, code: "NOT_FOUND" }),
123
+ methodNotAllowed: (message = "Method not allowed") => createErrorResponse({ message, status: 405, code: "METHOD_NOT_ALLOWED" }),
124
+ rateLimited: (retryAfter) => {
125
+ const res = createErrorResponse({
126
+ message: "Rate limit exceeded",
127
+ status: 429,
128
+ code: "RATE_LIMITED"
129
+ });
130
+ if (retryAfter)
131
+ res.headers.set("Retry-After", String(retryAfter));
132
+ return res;
133
+ },
134
+ internal: (err) => {
135
+ const message = err ? toError(err, "Internal server error") : "Internal server error";
136
+ return createErrorResponse({
137
+ message,
138
+ status: 500,
139
+ code: "INTERNAL_ERROR"
140
+ });
141
+ }
142
+ };
143
+
144
+ // src/middleware/logger.ts
145
+ function createLogger(ctx) {
146
+ const base = { service: ctx.service, module: ctx.module };
147
+ function emit(level, message, context) {
148
+ const entry = {
149
+ level,
150
+ timestamp: new Date().toISOString(),
151
+ ...base,
152
+ message,
153
+ ...context && { context }
154
+ };
155
+ const line = JSON.stringify(entry);
156
+ if (level === "error")
157
+ console.error(line);
158
+ else if (level === "warn")
159
+ console.warn(line);
160
+ else if (level === "debug")
161
+ console.debug(line);
162
+ else
163
+ console.info(line);
164
+ }
165
+ return {
166
+ debug: (msg, ctx2) => emit("debug", msg, ctx2),
167
+ info: (msg, ctx2) => emit("info", msg, ctx2),
168
+ warn: (msg, ctx2) => emit("warn", msg, ctx2),
169
+ error: (msg, ctx2) => emit("error", msg, ctx2)
170
+ };
171
+ }
172
+ function withRequestLog(handler, logCtx) {
173
+ return async (request, env, ctx) => {
174
+ const start = Date.now();
175
+ const logger = createLogger(logCtx);
176
+ try {
177
+ const response = await handler(request, env, ctx);
178
+ const duration = Date.now() - start;
179
+ const url = new URL(request.url);
180
+ logger.info(`${request.method} ${url.pathname}`, {
181
+ method: request.method,
182
+ path: url.pathname,
183
+ status: response.status,
184
+ durationMs: duration
185
+ });
186
+ return response;
187
+ } catch (error) {
188
+ const duration = Date.now() - start;
189
+ logger.error("Request failed", {
190
+ method: request.method,
191
+ path: new URL(request.url).pathname,
192
+ durationMs: duration,
193
+ error: toError(error)
194
+ });
195
+ throw error;
196
+ }
197
+ };
198
+ }
199
+ export {
200
+ withRequestLog,
201
+ createLogger
202
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Copyright (c) 2026 HOOX · HOOX · jango-blockchained (hoox-sh)
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /**
6
+ * Rate limiting middleware for Cloudflare Workers
7
+ * Adapted from workers/agent-worker/src/middleware/rate-limit.ts
8
+ *
9
+ * @available — Available for consumer use. Import from "@hoox-sh/hoox-shared/middleware".
10
+ */
11
+ import type { Env } from "../types";
12
+ export interface RateLimitConfig {
13
+ maxRequests: number;
14
+ windowSeconds: number;
15
+ keyPrefix?: string;
16
+ }
17
+ interface RateLimitResult {
18
+ allowed: boolean;
19
+ remaining: number;
20
+ retryAfter?: number;
21
+ }
22
+ export interface RateLimiter {
23
+ check(request: Request): Promise<RateLimitResult>;
24
+ enforce(request: Request): Promise<Response | null>;
25
+ checkKey(key: string): Promise<RateLimitResult>;
26
+ enforceKey(key: string): Promise<Response | null>;
27
+ }
28
+ export declare function createRateLimiter(kv: Env["CONFIG_KV"] | undefined, config: RateLimitConfig): RateLimiter;
29
+ export {};
30
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;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;AAuHD,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,GAAG,CAAC,WAAW,CAAC,GAAG,SAAS,EAChC,MAAM,EAAE,eAAe,GACtB,WAAW,CAiHb"}
@@ -0,0 +1,181 @@
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/middleware/rate-limit.ts
19
+ function createStorage(kv) {
20
+ if (!kv) {
21
+ const memory = new Map;
22
+ return {
23
+ async get(key) {
24
+ const entry = memory.get(key);
25
+ if (!entry)
26
+ return null;
27
+ if (Date.now() > entry.expiresAt) {
28
+ memory.delete(key);
29
+ return null;
30
+ }
31
+ return entry.value;
32
+ },
33
+ async put(key, value, opts) {
34
+ const ttl = opts?.expirationTtl ?? 0;
35
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
36
+ memory.set(key, { value, expiresAt });
37
+ },
38
+ async incr(key, opts) {
39
+ let entry = memory.get(key);
40
+ let newValue;
41
+ let attempts = 0;
42
+ const maxAttempts = 100;
43
+ while (true) {
44
+ const count = entry ? parseInt(entry.value, 10) : 0;
45
+ newValue = count + 1;
46
+ if (!entry) {
47
+ const ttl = opts?.expirationTtl ?? 0;
48
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
49
+ if (memory.get(key) === undefined) {
50
+ memory.set(key, { value: String(newValue), expiresAt });
51
+ return newValue;
52
+ }
53
+ entry = memory.get(key);
54
+ } else {
55
+ if (Date.now() > entry.expiresAt) {
56
+ memory.delete(key);
57
+ entry = undefined;
58
+ continue;
59
+ }
60
+ const currentEntry = memory.get(key);
61
+ if (currentEntry === entry) {
62
+ const ttl = opts?.expirationTtl ?? 0;
63
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
64
+ memory.set(key, { value: String(newValue), expiresAt });
65
+ return newValue;
66
+ }
67
+ entry = currentEntry;
68
+ }
69
+ attempts++;
70
+ if (attempts >= maxAttempts) {
71
+ const current = await this.get(key);
72
+ const count2 = current ? parseInt(current, 10) : 0;
73
+ newValue = count2 + 1;
74
+ const ttl = opts?.expirationTtl ?? 0;
75
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
76
+ memory.set(key, { value: String(newValue), expiresAt });
77
+ return newValue;
78
+ }
79
+ }
80
+ }
81
+ };
82
+ }
83
+ return {
84
+ async get(key) {
85
+ return kv.get(key);
86
+ },
87
+ async put(key, value, opts) {
88
+ await kv.put(key, value, opts);
89
+ },
90
+ async incr(key, opts) {
91
+ const current = await kv.get(key);
92
+ const count = current ? parseInt(current, 10) : 0;
93
+ const next = count + 1;
94
+ await kv.put(key, String(next), opts);
95
+ return next;
96
+ }
97
+ };
98
+ }
99
+ function createRateLimiter(kv, config) {
100
+ const prefix = config.keyPrefix ?? "rate-limit";
101
+ const storage = createStorage(kv);
102
+ function getClientIp(request) {
103
+ return request.headers.get("CF-Connecting-IP") ?? "unknown";
104
+ }
105
+ function getWindowKey(ip) {
106
+ const windowStart = Math.floor(Date.now() / (config.windowSeconds * 1000));
107
+ return `${prefix}:${ip}:${windowStart}`;
108
+ }
109
+ async function checkWithKey(fullKey) {
110
+ const current = await storage.get(fullKey);
111
+ const count = current ? parseInt(current, 10) : 0;
112
+ if (count >= config.maxRequests) {
113
+ const retryAfter = Math.ceil(config.windowSeconds - Date.now() % (config.windowSeconds * 1000) / 1000);
114
+ return {
115
+ allowed: false,
116
+ remaining: 0,
117
+ retryAfter
118
+ };
119
+ }
120
+ const newCount = await storage.incr(fullKey, {
121
+ expirationTtl: config.windowSeconds
122
+ });
123
+ if (newCount > config.maxRequests) {
124
+ const retryAfter = Math.ceil(config.windowSeconds - Date.now() % (config.windowSeconds * 1000) / 1000);
125
+ return {
126
+ allowed: false,
127
+ remaining: 0,
128
+ retryAfter
129
+ };
130
+ }
131
+ return {
132
+ allowed: true,
133
+ remaining: config.maxRequests - newCount
134
+ };
135
+ }
136
+ async function check(request) {
137
+ const ip = getClientIp(request);
138
+ const key = getWindowKey(ip);
139
+ return checkWithKey(key);
140
+ }
141
+ async function enforce(request) {
142
+ const result = await check(request);
143
+ if (!result.allowed) {
144
+ return new Response(JSON.stringify({
145
+ error: "Rate limit exceeded",
146
+ retryAfter: result.retryAfter
147
+ }), {
148
+ status: 429,
149
+ headers: {
150
+ "Content-Type": "application/json",
151
+ "Retry-After": String(result.retryAfter ?? config.windowSeconds)
152
+ }
153
+ });
154
+ }
155
+ return null;
156
+ }
157
+ async function checkKey(key) {
158
+ const fullKey = `${prefix}:${key}`;
159
+ return checkWithKey(fullKey);
160
+ }
161
+ async function enforceKey(key) {
162
+ const result = await checkKey(key);
163
+ if (!result.allowed) {
164
+ return new Response(JSON.stringify({
165
+ error: "Rate limit exceeded",
166
+ retryAfter: result.retryAfter
167
+ }), {
168
+ status: 429,
169
+ headers: {
170
+ "Content-Type": "application/json",
171
+ "Retry-After": String(result.retryAfter ?? config.windowSeconds)
172
+ }
173
+ });
174
+ }
175
+ return null;
176
+ }
177
+ return { check, enforce, checkKey, enforceKey };
178
+ }
179
+ export {
180
+ createRateLimiter
181
+ };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Copyright (c) 2026 HOOX · HOOX · jango-blockchained (hoox-sh)
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /**
6
+ * Security Headers middleware for Cloudflare Workers
7
+ * Provides standardized security headers for all worker responses
8
+ *
9
+ * @available — Available for consumer use. Import from "@hoox-sh/hoox-shared/middleware".
10
+ */
11
+ export interface SecurityHeadersOptions {
12
+ /** X-Content-Type-Options (default: "nosniff") */
13
+ xContentTypeOptions?: string;
14
+ /** X-Frame-Options (default: "DENY") */
15
+ xFrameOptions?: string;
16
+ /** X-XSS-Protection (default: "1; mode=block") */
17
+ xXssProtection?: string;
18
+ /** Referrer-Policy (default: "strict-origin-when-cross-origin") */
19
+ referrerPolicy?: string;
20
+ /** Permissions-Policy (default: restrictive — all disabled) */
21
+ permissionsPolicy?: string;
22
+ /** Strict-Transport-Security (default: "max-age=31536000; includeSubDomains") */
23
+ strictTransportSecurity?: string;
24
+ /** Content-Security-Policy (default: "default-src 'self'") */
25
+ contentSecurityPolicy?: string;
26
+ }
27
+ export declare const SECURITY_HEADERS_DEFAULTS: Required<SecurityHeadersOptions>;
28
+ /**
29
+ * Create security headers object for use in responses.
30
+ * Merges provided options over defaults.
31
+ */
32
+ export declare function secureHeaders(options?: SecurityHeadersOptions): Record<string, string>;
33
+ /**
34
+ * Wrap an existing Response with security headers.
35
+ * Preserves existing response headers (security headers take precedence).
36
+ *
37
+ * @example
38
+ * return wrapWithSecurityHeaders(
39
+ * createJsonResponse({ success: true }, 200)
40
+ * );
41
+ */
42
+ export declare function wrapWithSecurityHeaders(response: Response, options?: SecurityHeadersOptions): Response;
43
+ //# sourceMappingURL=security-headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-headers.d.ts","sourceRoot":"","sources":["../../src/middleware/security-headers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;GAKG;AAEH,MAAM,WAAW,sBAAsB;IACrC,kDAAkD;IAClD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,wCAAwC;IACxC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iFAAiF;IACjF,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,8DAA8D;IAC9D,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,sBAAsB,CAStE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,CAAC,EAAE,sBAAsB,GAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAexB;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,QAAQ,CAUV"}
@@ -0,0 +1,58 @@
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/middleware/security-headers.ts
19
+ var SECURITY_HEADERS_DEFAULTS = {
20
+ xContentTypeOptions: "nosniff",
21
+ xFrameOptions: "DENY",
22
+ xXssProtection: "1; mode=block",
23
+ referrerPolicy: "strict-origin-when-cross-origin",
24
+ permissionsPolicy: "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()",
25
+ strictTransportSecurity: "max-age=31536000; includeSubDomains",
26
+ contentSecurityPolicy: "default-src 'self'"
27
+ };
28
+ function secureHeaders(options) {
29
+ const opts = { ...SECURITY_HEADERS_DEFAULTS, ...options };
30
+ const headers = {
31
+ "X-Content-Type-Options": opts.xContentTypeOptions,
32
+ "X-Frame-Options": opts.xFrameOptions,
33
+ "X-XSS-Protection": opts.xXssProtection,
34
+ "Referrer-Policy": opts.referrerPolicy,
35
+ "Permissions-Policy": opts.permissionsPolicy,
36
+ "Strict-Transport-Security": opts.strictTransportSecurity
37
+ };
38
+ if (opts.contentSecurityPolicy) {
39
+ headers["Content-Security-Policy"] = opts.contentSecurityPolicy;
40
+ }
41
+ return headers;
42
+ }
43
+ function wrapWithSecurityHeaders(response, options) {
44
+ const headers = new Headers(response.headers);
45
+ for (const [key, value] of Object.entries(secureHeaders(options))) {
46
+ headers.set(key, value);
47
+ }
48
+ return new Response(response.body, {
49
+ status: response.status,
50
+ statusText: response.statusText,
51
+ headers
52
+ });
53
+ }
54
+ export {
55
+ wrapWithSecurityHeaders,
56
+ secureHeaders,
57
+ SECURITY_HEADERS_DEFAULTS
58
+ };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Copyright (c) 2026 HOOX · HOOX · jango-blockchained (hoox-sh)
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /**
6
+ * Validation middleware for Cloudflare Workers
7
+ * Uses Zod for runtime schema validation.
8
+ *
9
+ * @available — Available for consumer use. Import from "@hoox-sh/hoox-shared/middleware".
10
+ */
11
+ import { z } from "zod";
12
+ import type { Result } from "../types";
13
+ /**
14
+ * Validate unknown data against a Zod schema, returning a Result type.
15
+ * Provides structured error messages from Zod issue paths.
16
+ */
17
+ export declare function validateJson<T extends z.ZodTypeAny>(schema: T, data: unknown): Result<z.infer<T>>;
18
+ /**
19
+ * Legacy signature: parse request body as JSON object.
20
+ * Kept for backward compatibility with existing workers.
21
+ */
22
+ export declare function validateJsonLegacy(request: Request): Promise<Result<Record<string, unknown>>>;
23
+ export declare function requireField<T>(body: Record<string, unknown>, field: string): Result<T>;
24
+ export declare function optionalField<T>(body: Record<string, unknown>, field: string, defaultValue: T): T;
25
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/middleware/validate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC,UAAU,EACjD,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,OAAO,GACZ,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAWpB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAU1C;AAED,wBAAgB,YAAY,CAAC,CAAC,EAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,MAAM,GACZ,MAAM,CAAC,CAAC,CAAC,CAKX;AAED,wBAAgB,aAAa,CAAC,CAAC,EAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,CAAC,GACd,CAAC,CAGH"}
@@ -0,0 +1,56 @@
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/middleware/validate.ts
19
+ function validateJson(schema, data) {
20
+ const result = schema.safeParse(data);
21
+ if (!result.success) {
22
+ return {
23
+ ok: false,
24
+ error: result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
25
+ };
26
+ }
27
+ return { ok: true, value: result.data };
28
+ }
29
+ async function validateJsonLegacy(request) {
30
+ try {
31
+ const body = await request.json();
32
+ if (typeof body !== "object" || body === null || Array.isArray(body)) {
33
+ return { ok: false, error: "Request body must be a JSON object" };
34
+ }
35
+ return { ok: true, value: body };
36
+ } catch {
37
+ return { ok: false, error: "Invalid JSON in request body" };
38
+ }
39
+ }
40
+ function requireField(body, field) {
41
+ if (!(field in body)) {
42
+ return { ok: false, error: `Missing required field: ${field}` };
43
+ }
44
+ return { ok: true, value: body[field] };
45
+ }
46
+ function optionalField(body, field, defaultValue) {
47
+ if (!(field in body))
48
+ return defaultValue;
49
+ return body[field];
50
+ }
51
+ export {
52
+ validateJsonLegacy,
53
+ validateJson,
54
+ requireField,
55
+ optionalField
56
+ };
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Copyright (c) 2026 HOOX · HOOX · jango-blockchained (hoox-sh)
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /**
6
+ * Operator transport profile — how CLI/TUI clients reach the management plane.
7
+ *
8
+ * Transports:
9
+ * public — HTTPS + optional Bearer (default)
10
+ * access — Cloudflare Access service-token headers (+ optional Bearer)
11
+ * mtls — reserved (Enterprise); headers same as public until TLS certs land
12
+ * tunnel — reserved (private hostname); headers same as public
13
+ *
14
+ * Client maps HOOX_API_TOKEN → Authorization: Bearer …
15
+ * Server maps OPERATOR_API_KEY (preferred) or INTERNAL_API_KEY → requireOperatorAuth
16
+ */
17
+ export type OperatorTransport = "public" | "access" | "mtls" | "tunnel";
18
+ export interface OperatorTransportProfile {
19
+ transport: OperatorTransport;
20
+ /** API base URL without trailing slash */
21
+ apiBase: string;
22
+ /** Bearer token (HOOX_API_TOKEN); empty if unset */
23
+ bearerToken: string;
24
+ /** CF Access service token client id */
25
+ accessClientId: string;
26
+ /** CF Access service token client secret */
27
+ accessClientSecret: string;
28
+ }
29
+ export interface OperatorTransportEnv {
30
+ HOOX_API_URL?: string;
31
+ HOOX_API_TOKEN?: string;
32
+ HOOX_TRANSPORT?: string;
33
+ CF_ACCESS_CLIENT_ID?: string;
34
+ CF_ACCESS_CLIENT_SECRET?: string;
35
+ [key: string]: string | undefined;
36
+ }
37
+ export interface ResolveOperatorTransportOptions {
38
+ /**
39
+ * Fallback transport when HOOX_TRANSPORT is unset
40
+ * (e.g. from ~/.hoox/config.json).
41
+ */
42
+ configTransport?: string;
43
+ /** Fallback API base when HOOX_API_URL is unset. */
44
+ configApiUrl?: string;
45
+ /** Fallback bearer when HOOX_API_TOKEN is unset. */
46
+ configApiToken?: string;
47
+ }
48
+ /**
49
+ * Resolve operator transport from env (process.env by default).
50
+ * Pure + injectable for tests. Optional config fallbacks for file-backed prefs.
51
+ */
52
+ export declare function resolveOperatorTransportProfile(env?: OperatorTransportEnv, options?: ResolveOperatorTransportOptions): OperatorTransportProfile;
53
+ /**
54
+ * Build HTTP headers for operator management requests.
55
+ * Never logs secrets; callers must not print the result in debug without redaction.
56
+ */
57
+ export declare function buildOperatorAuthHeaders(profile: OperatorTransportProfile): Record<string, string>;
58
+ /** True when the profile has any client-side operator credential. */
59
+ export declare function hasOperatorClientCredentials(profile: OperatorTransportProfile): boolean;
60
+ /**
61
+ * Join base URL and path without double slashes.
62
+ * Paths should start with `/`.
63
+ */
64
+ export declare function operatorUrl(profile: OperatorTransportProfile, path: string): string;
65
+ //# sourceMappingURL=operator-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operator-transport.d.ts","sourceRoot":"","sources":["../src/operator-transport.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAExE,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC;AAwBD,MAAM,WAAW,+BAA+B;IAC9C;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,GAAG,GAAE,oBAA0D,EAC/D,OAAO,GAAE,+BAAoC,GAC5C,wBAAwB,CAyB1B;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,wBAAwB,GAChC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAuBxB;AAED,qEAAqE;AACrE,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAGT;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,wBAAwB,EACjC,IAAI,EAAE,MAAM,GACX,MAAM,CAIR"}