theokit 0.48.7 → 0.48.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 (67) hide show
  1. package/README.md +86 -0
  2. package/dist/{actions-virtual-module-GB5FWUPS.js → actions-virtual-module-LB4NGONI.js} +2 -3
  3. package/dist/{actions-virtual-module-GB5FWUPS.js.map → actions-virtual-module-LB4NGONI.js.map} +1 -1
  4. package/dist/{agent-47XWDGIR.js → agent-SK4TOO6M.js} +2 -2
  5. package/dist/{app-typed-client-5QDNHANF.js → app-typed-client-G3J5G7E5.js} +2 -3
  6. package/dist/{app-typed-client-5QDNHANF.js.map → app-typed-client-G3J5G7E5.js.map} +1 -1
  7. package/dist/{build-4CAGKYYT.js → build-FMRO4W4Q.js} +4 -4
  8. package/dist/{chunk-H6YBKIUN.js → chunk-BIVCP6X5.js} +575 -161
  9. package/dist/chunk-BIVCP6X5.js.map +1 -0
  10. package/dist/{chunk-UDPJP42U.js → chunk-GPX7I5ZY.js} +31 -4
  11. package/dist/chunk-GPX7I5ZY.js.map +1 -0
  12. package/dist/{chunk-3GUXSNHY.js → chunk-GRI4XHBJ.js} +86 -3
  13. package/dist/chunk-GRI4XHBJ.js.map +1 -0
  14. package/dist/{chunk-A4ZEP3FE.js → chunk-GVW5EKEU.js} +11 -2
  15. package/dist/chunk-GVW5EKEU.js.map +1 -0
  16. package/dist/{chunk-AXQF3I4L.js → chunk-Q2S2IGSM.js} +40 -123
  17. package/dist/chunk-Q2S2IGSM.js.map +1 -0
  18. package/dist/{chunk-GH32R55S.js → chunk-S433N3CT.js} +2 -2
  19. package/dist/{chunk-JZ25BBLP.js → chunk-SO64VQOD.js} +4 -4
  20. package/dist/chunk-V7MTRM6Q.js +90 -0
  21. package/dist/chunk-V7MTRM6Q.js.map +1 -0
  22. package/dist/{chunk-XSCZWSE3.js → chunk-YML4ZDFA.js} +11 -2
  23. package/dist/chunk-YML4ZDFA.js.map +1 -0
  24. package/dist/cli/index.js +19 -9
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/cookies-QWVS7OGS.js +23 -0
  27. package/dist/{dev-F67X4F5E.js → dev-P2MODWTG.js} +6 -7
  28. package/dist/{dev-F67X4F5E.js.map → dev-P2MODWTG.js.map} +1 -1
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +1 -1
  31. package/dist/{info-MSBQ72EM.js → info-XW7HGUPH.js} +3 -3
  32. package/dist/{internal-api-ICU7ITJD.js → internal-api-V5L65YSJ.js} +7 -9
  33. package/dist/{load-config-2LP2OEKD.js → load-config-VKD6LKWU.js} +3 -3
  34. package/dist/{mcp-AJHKPAU4.js → mcp-EQHTPQ2L.js} +3 -3
  35. package/dist/{openapi-FWNYHB6T.js → openapi-HZXR335W.js} +3 -3
  36. package/dist/{routes-6AMNVFMI.js → routes-OLNQX7CT.js} +3 -3
  37. package/dist/{schema-Z3Y7SFLE.js → schema-ZO3OSWVG.js} +2 -2
  38. package/dist/server/index.js +1 -1
  39. package/dist/server/rate-limit/index.d.ts +49 -1
  40. package/dist/server/rate-limit/index.js +1 -1
  41. package/dist/{start-IITIEJTY.js → start-KR2JJR7K.js} +149 -20
  42. package/dist/start-KR2JJR7K.js.map +1 -0
  43. package/dist/vite-plugin/index.js +1 -1
  44. package/dist/{vite-plugin-MXHY6MSV.js → vite-plugin-EQMDMDXV.js} +6 -7
  45. package/dist/vite-plugin-EQMDMDXV.js.map +1 -0
  46. package/package.json +11 -1
  47. package/dist/chunk-3GUXSNHY.js.map +0 -1
  48. package/dist/chunk-7CQ6DEWZ.js +0 -421
  49. package/dist/chunk-7CQ6DEWZ.js.map +0 -1
  50. package/dist/chunk-A4ZEP3FE.js.map +0 -1
  51. package/dist/chunk-AXQF3I4L.js.map +0 -1
  52. package/dist/chunk-H6YBKIUN.js.map +0 -1
  53. package/dist/chunk-UDPJP42U.js.map +0 -1
  54. package/dist/chunk-XSCZWSE3.js.map +0 -1
  55. package/dist/start-IITIEJTY.js.map +0 -1
  56. /package/dist/{agent-47XWDGIR.js.map → agent-SK4TOO6M.js.map} +0 -0
  57. /package/dist/{build-4CAGKYYT.js.map → build-FMRO4W4Q.js.map} +0 -0
  58. /package/dist/{chunk-GH32R55S.js.map → chunk-S433N3CT.js.map} +0 -0
  59. /package/dist/{chunk-JZ25BBLP.js.map → chunk-SO64VQOD.js.map} +0 -0
  60. /package/dist/{internal-api-ICU7ITJD.js.map → cookies-QWVS7OGS.js.map} +0 -0
  61. /package/dist/{info-MSBQ72EM.js.map → info-XW7HGUPH.js.map} +0 -0
  62. /package/dist/{load-config-2LP2OEKD.js.map → internal-api-V5L65YSJ.js.map} +0 -0
  63. /package/dist/{schema-Z3Y7SFLE.js.map → load-config-VKD6LKWU.js.map} +0 -0
  64. /package/dist/{mcp-AJHKPAU4.js.map → mcp-EQHTPQ2L.js.map} +0 -0
  65. /package/dist/{openapi-FWNYHB6T.js.map → openapi-HZXR335W.js.map} +0 -0
  66. /package/dist/{routes-6AMNVFMI.js.map → routes-OLNQX7CT.js.map} +0 -0
  67. /package/dist/{vite-plugin-MXHY6MSV.js.map → schema-ZO3OSWVG.js.map} +0 -0
@@ -5,9 +5,9 @@ import {
5
5
  } from "./chunk-GEQJAFSV.js";
6
6
  import {
7
7
  loadConfig
8
- } from "./chunk-GH32R55S.js";
8
+ } from "./chunk-S433N3CT.js";
9
9
  import "./chunk-HBU2GIMZ.js";
10
- import "./chunk-A4ZEP3FE.js";
10
+ import "./chunk-GVW5EKEU.js";
11
11
  import "./chunk-7VPQHEAJ.js";
12
12
  import {
13
13
  scanServerActions,
@@ -62,4 +62,4 @@ async function routesCommand() {
62
62
  export {
63
63
  routesCommand
64
64
  };
65
- //# sourceMappingURL=routes-6AMNVFMI.js.map
65
+ //# sourceMappingURL=routes-OLNQX7CT.js.map
@@ -6,7 +6,7 @@ import {
6
6
  securityHeadersSchema,
7
7
  storageSchema,
8
8
  theoConfigSchema
9
- } from "./chunk-A4ZEP3FE.js";
9
+ } from "./chunk-GVW5EKEU.js";
10
10
  import "./chunk-7VPQHEAJ.js";
11
11
  export {
12
12
  cacheSchema,
@@ -15,4 +15,4 @@ export {
15
15
  storageSchema,
16
16
  theoConfigSchema
17
17
  };
18
- //# sourceMappingURL=schema-Z3Y7SFLE.js.map
18
+ //# sourceMappingURL=schema-ZO3OSWVG.js.map
@@ -47,7 +47,7 @@ import {
47
47
  deriveKey,
48
48
  deriveKeyFromRequest,
49
49
  matchRoutePattern
50
- } from "../chunk-UDPJP42U.js";
50
+ } from "../chunk-GPX7I5ZY.js";
51
51
  import {
52
52
  ChannelManager
53
53
  } from "../chunk-4PBJ266F.js";
@@ -4,6 +4,41 @@ import { R as RateLimitStore } from '../../rate-limit-store-BEJnhWdw.js';
4
4
  export { I as InMemoryStore, a as RateLimitState } from '../../rate-limit-store-BEJnhWdw.js';
5
5
  import { IncomingMessage } from 'node:http';
6
6
 
7
+ /**
8
+ * Resolves the address a rate-limit bucket should be keyed on.
9
+ *
10
+ * ## Why this is not just `req.socket.remoteAddress`
11
+ *
12
+ * Behind any reverse proxy — Caddy, nginx, a load balancer, an ingress controller — the socket
13
+ * address is the PROXY's, identical for every visitor on the internet. Keying buckets on it gives
14
+ * the whole world one shared budget, so the first few requests each minute exhaust it and everyone
15
+ * else is refused. That is worse than no limiting: a limit meant to stop one abusive client becomes
16
+ * a denial of service any single client can trigger for everybody.
17
+ *
18
+ * ## Why it is opt-in
19
+ *
20
+ * `x-forwarded-for` is a request header, and a request header is whatever the client typed. Reading
21
+ * it without being told to would let anyone bypass the limiter by rotating a forged value — a
22
+ * one-line `curl -H`. So the default is to trust nothing and use the socket.
23
+ *
24
+ * ## Why the RIGHTMOST entry
25
+ *
26
+ * Each proxy APPENDS the address that connected to it. A client that sends `x-forwarded-for: 1.2.3.4`
27
+ * through one proxy produces `1.2.3.4, <real client>` — the forgery lands on the left, and the entry
28
+ * the trusted proxy wrote is last. Counting from the right therefore skips exactly the hops the
29
+ * operator vouched for, and everything a client can influence stays to the left of where we look.
30
+ *
31
+ * This mirrors the `'trusted-proxy'` policy in `adapters/web-shim.ts`, which documents the same rule
32
+ * for the Web-Request runtimes.
33
+ */
34
+ /**
35
+ * How many proxies sit in front of the app.
36
+ *
37
+ * `false` (the default) trusts nothing and uses the socket address. `true` means one trusted proxy.
38
+ * A number names the hop count for a longer chain — a CDN in front of your own proxy is `2`.
39
+ */
40
+ type TrustProxy = boolean | number;
41
+
7
42
  /**
8
43
  * T2.2 — Per-route + per-user rate limiting.
9
44
  *
@@ -29,6 +64,19 @@ interface RouteRateLimitConfig {
29
64
  cookieName?: string;
30
65
  /** Optional shared store (for multi-route correlation). Default per-limiter InMemoryStore. */
31
66
  store?: RateLimitStore;
67
+ /**
68
+ * How many reverse proxies sit in front of the app, for `keyBy: 'ip'`. Default `false` — trust
69
+ * none and key on the socket address.
70
+ *
71
+ * Set this whenever the app is behind Caddy, nginx, a load balancer or an ingress controller:
72
+ * without it every visitor keys on the proxy's address and shares one bucket, so a handful of
73
+ * requests exhausts the budget for the entire internet. With it, the client address is read from
74
+ * `x-forwarded-for` counting in from the right, past exactly the hops declared here.
75
+ *
76
+ * Leaving it off by default is deliberate — `x-forwarded-for` is client-writable, and honouring it
77
+ * uninvited turns the limiter into a one-header bypass. See `client-ip.ts`.
78
+ */
79
+ trustProxy?: TrustProxy;
32
80
  }
33
81
  /**
34
82
  * Test whether `path` matches `pattern`. String patterns are compared
@@ -44,7 +92,7 @@ declare function matchRoutePattern(path: string, pattern: string | RegExp): bool
44
92
  * we fall back to IP so anonymous users still get rate-limited rather
45
93
  * than sharing an empty bucket.
46
94
  */
47
- declare function deriveKey(req: IncomingMessage, keyBy: KeyByMode, cookieName: string): Promise<string>;
95
+ declare function deriveKey(req: IncomingMessage, keyBy: KeyByMode, cookieName: string, trustProxy?: TrustProxy): Promise<string>;
48
96
  /**
49
97
  * Per-route rate limiter factory. Returns a sync checker compatible with
50
98
  * the existing api-middleware shape.
@@ -4,7 +4,7 @@ import {
4
4
  deriveKey,
5
5
  deriveKeyFromRequest,
6
6
  matchRoutePattern
7
- } from "../../chunk-UDPJP42U.js";
7
+ } from "../../chunk-GPX7I5ZY.js";
8
8
  import {
9
9
  InMemoryStore,
10
10
  createRateLimiter,
@@ -3,10 +3,14 @@ import "tsx/esm";
3
3
  import {
4
4
  CONTROLLER_MANIFEST_FILE
5
5
  } from "./chunk-47CINLWF.js";
6
+ import {
7
+ parseCookieHeader
8
+ } from "./chunk-V7MTRM6Q.js";
6
9
  import {
7
10
  preflightNodeAndBindings
8
11
  } from "./chunk-KTMQUYED.js";
9
12
  import {
13
+ applyNonceToInlineScripts,
10
14
  extractHeadTags,
11
15
  handleAgentApproval,
12
16
  injectIntoHead,
@@ -14,30 +18,30 @@ import {
14
18
  resolveTransformer,
15
19
  serveAgentAuxRoute,
16
20
  writeWebResponseToServerResponse
17
- } from "./chunk-3GUXSNHY.js";
21
+ } from "./chunk-GRI4XHBJ.js";
18
22
  import "./chunk-2CVV6CNN.js";
19
23
  import {
20
24
  findRootDiv
21
25
  } from "./chunk-3PWQQWT6.js";
22
26
  import {
23
27
  loadConfig
24
- } from "./chunk-GH32R55S.js";
28
+ } from "./chunk-S433N3CT.js";
25
29
  import {
26
30
  loadEnv
27
31
  } from "./chunk-HBU2GIMZ.js";
28
- import "./chunk-A4ZEP3FE.js";
32
+ import "./chunk-GVW5EKEU.js";
29
33
  import "./chunk-7VPQHEAJ.js";
30
34
  import {
35
+ InMemoryStore,
31
36
  buildSecurityHeaders,
32
37
  createPluginRunnerFromConfig,
33
38
  createProductionLoader,
34
- createRateLimiter,
35
39
  executeAction,
36
40
  executeRoute,
37
41
  findSuggestion,
38
42
  generateNonce,
39
43
  mountAgent
40
- } from "./chunk-H6YBKIUN.js";
44
+ } from "./chunk-BIVCP6X5.js";
41
45
  import {
42
46
  getApprovalRegistry,
43
47
  resolveProvider
@@ -96,6 +100,131 @@ async function serveReservedRoute(pathname, routes = {}) {
96
100
  return null;
97
101
  }
98
102
 
103
+ // src/server/rate-limit/client-ip.ts
104
+ function hopCount(trustProxy) {
105
+ if (trustProxy === true) return 1;
106
+ if (trustProxy === false) return 0;
107
+ return trustProxy;
108
+ }
109
+ function headerValue(req, name) {
110
+ const raw = req.headers[name];
111
+ if (Array.isArray(raw)) return raw[raw.length - 1];
112
+ return raw;
113
+ }
114
+ function resolveClientIp(req, trustProxy = false) {
115
+ const socketAddress = req.socket?.remoteAddress ?? "unknown";
116
+ const hops = hopCount(trustProxy);
117
+ if (hops < 1) return socketAddress;
118
+ const forwarded = headerValue(req, "x-forwarded-for");
119
+ if (forwarded) {
120
+ const entries = forwarded.split(",").map((entry) => entry.trim()).filter(Boolean);
121
+ const index = entries.length - hops;
122
+ if (index >= 0 && index < entries.length) return entries[index];
123
+ }
124
+ const realIp = headerValue(req, "x-real-ip");
125
+ if (realIp) return realIp.trim();
126
+ return socketAddress;
127
+ }
128
+
129
+ // src/server/rate-limit/rate-limit-per-route.ts
130
+ function normalizePath(input) {
131
+ const noQuery = input.split("?")[0];
132
+ if (noQuery.length > 1 && noQuery.endsWith("/")) return noQuery.slice(0, -1);
133
+ return noQuery;
134
+ }
135
+ function matchRoutePattern(path, pattern) {
136
+ const canonical = normalizePath(path);
137
+ if (typeof pattern === "string") {
138
+ return canonical === normalizePath(pattern);
139
+ }
140
+ pattern.lastIndex = 0;
141
+ return pattern.test(canonical);
142
+ }
143
+ async function hashFragment(input) {
144
+ const buf = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
145
+ const bytes = new Uint8Array(buf);
146
+ let bin = "";
147
+ for (const b of bytes) bin += String.fromCharCode(b);
148
+ const b64url = btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
149
+ return b64url.slice(0, 16);
150
+ }
151
+ function readCookie(req, name) {
152
+ return parseCookieHeader(req.headers.cookie ?? void 0).get(name);
153
+ }
154
+ async function deriveKey(req, keyBy, cookieName, trustProxy = false) {
155
+ if (typeof keyBy === "function") return keyBy(req);
156
+ const ip = resolveClientIp(req, trustProxy);
157
+ switch (keyBy) {
158
+ case "session": {
159
+ const cookie = readCookie(req, cookieName);
160
+ return cookie ? `session:${await hashFragment(cookie)}` : `ip:${ip}`;
161
+ }
162
+ case "user": {
163
+ const userId = req.user?.id;
164
+ return userId ? `user:${userId}` : `ip:${ip}`;
165
+ }
166
+ case "ip":
167
+ default:
168
+ return `ip:${ip}`;
169
+ }
170
+ }
171
+ function createRouteRateLimiter(config) {
172
+ const isFlat = "windowMs" in config && "max" in config && !("default" in config) && !("routes" in config);
173
+ const cfg = isFlat ? { default: config } : config;
174
+ const store = cfg.store ?? new InMemoryStore();
175
+ if (!(store instanceof InMemoryStore)) {
176
+ throw new Error(
177
+ "createRouteRateLimiter: async RateLimitStore implementations require a dedicated async middleware path. Use the InMemoryStore default for the sync facade."
178
+ );
179
+ }
180
+ const inMemoryStore = store;
181
+ const keyBy = cfg.keyBy ?? "ip";
182
+ const cookieName = cfg.cookieName ?? "theo_session";
183
+ const trustProxy = cfg.trustProxy ?? false;
184
+ const patternList = [];
185
+ if (cfg.routes) {
186
+ for (const [pattern, c] of Object.entries(cfg.routes)) patternList.push([pattern, c]);
187
+ }
188
+ if (cfg.routePatterns) {
189
+ for (const tuple of cfg.routePatterns) patternList.push(tuple);
190
+ }
191
+ return async function checkRouteRateLimit(req) {
192
+ const url = req.url ?? "";
193
+ let matched;
194
+ for (const [pattern, c] of patternList) {
195
+ if (matchRoutePattern(url, pattern)) {
196
+ matched = c;
197
+ break;
198
+ }
199
+ }
200
+ const effective = matched ?? cfg.default;
201
+ if (!effective) {
202
+ return { limited: false, headers: {} };
203
+ }
204
+ const bucketSuffix = typeof matched === "undefined" ? "*default*" : normalizePath(url);
205
+ const key = `${await deriveKey(req, keyBy, cookieName, trustProxy)}|${bucketSuffix}`;
206
+ const state = inMemoryStore.incrSync(key, effective.windowMs);
207
+ if (state.count > effective.max) {
208
+ const retryAfter = Math.ceil((state.resetAt - Date.now()) / 1e3);
209
+ return {
210
+ limited: true,
211
+ headers: {
212
+ "X-RateLimit-Limit": String(effective.max),
213
+ "X-RateLimit-Remaining": "0",
214
+ "Retry-After": String(retryAfter)
215
+ }
216
+ };
217
+ }
218
+ return {
219
+ limited: false,
220
+ headers: {
221
+ "X-RateLimit-Limit": String(effective.max),
222
+ "X-RateLimit-Remaining": String(Math.max(0, effective.max - state.count))
223
+ }
224
+ };
225
+ };
226
+ }
227
+
99
228
  // src/cli/commands/start/assert-sdk-compatible.ts
100
229
  import { readFileSync } from "fs";
101
230
  import { createRequire } from "module";
@@ -201,7 +330,7 @@ async function configureStorageManagerFromConfig(storageConfig) {
201
330
  if (storageConfig === void 0 || storageConfig === null) return;
202
331
  try {
203
332
  const { getStorageManager } = await import("./storage-manager-3O5PDO6O.js");
204
- const { storageSchema } = await import("./schema-Z3Y7SFLE.js");
333
+ const { storageSchema } = await import("./schema-ZO3OSWVG.js");
205
334
  const parsed = storageSchema.parse(storageConfig);
206
335
  getStorageManager().configure(parsed);
207
336
  } catch (err) {
@@ -358,9 +487,9 @@ async function loadCompiledController(absPath) {
358
487
  return await import(pathToFileURL(absPath).href);
359
488
  }
360
489
  var X_REQUEST_ID = "x-request-id";
361
- function applyRateLimit(c, method) {
490
+ async function applyRateLimit(c, method) {
362
491
  if (!c.rateLimiter) return false;
363
- const check = c.rateLimiter(c.req);
492
+ const check = await c.rateLimiter(c.req);
364
493
  for (const [k, v] of Object.entries(check.headers)) c.res.setHeader(k, v);
365
494
  if (check.limited) {
366
495
  sendError(c.res, "RATE_LIMITED", "Too many requests", 429, void 0, c.requestId);
@@ -378,7 +507,7 @@ function applyRateLimit(c, method) {
378
507
  async function tryServeAction(c) {
379
508
  if (!c.url.startsWith("/api/__actions/")) return false;
380
509
  c.res.setHeader(X_REQUEST_ID, c.requestId);
381
- if (applyRateLimit(c, c.req.method ?? "POST")) return true;
510
+ if (await applyRateLimit(c, c.req.method ?? "POST")) return true;
382
511
  const pathAfterPrefix = c.url.slice("/api/__actions/".length).split("?")[0];
383
512
  const segments = pathAfterPrefix.split("/").filter(Boolean);
384
513
  if (segments.length < 2) {
@@ -467,7 +596,7 @@ async function tryServeAgent(c) {
467
596
  const urlPath = c.url.split("?")[0];
468
597
  if (isApprovalPath(urlPath)) {
469
598
  c.res.setHeader(X_REQUEST_ID, c.requestId);
470
- if (applyRateLimit(c, c.req.method ?? "POST")) return true;
599
+ if (await applyRateLimit(c, c.req.method ?? "POST")) return true;
471
600
  const method2 = (c.req.method ?? "POST").toUpperCase();
472
601
  if (method2 !== "POST") {
473
602
  sendError(
@@ -518,7 +647,7 @@ async function tryServeAgent(c) {
518
647
  const agent = c.cachedAgents.find((a) => a.agentPath === urlPath);
519
648
  if (!agent) return false;
520
649
  c.res.setHeader(X_REQUEST_ID, c.requestId);
521
- if (applyRateLimit(c, c.req.method ?? "POST")) return true;
650
+ if (await applyRateLimit(c, c.req.method ?? "POST")) return true;
522
651
  const method = (c.req.method ?? "POST").toUpperCase();
523
652
  if (method !== "POST") {
524
653
  sendError(
@@ -570,7 +699,7 @@ async function tryServeAgent(c) {
570
699
  async function tryServeApiRoute(c) {
571
700
  if (!c.url.startsWith("/api/")) return false;
572
701
  c.res.setHeader(X_REQUEST_ID, c.requestId);
573
- if (applyRateLimit(c, c.req.method ?? "GET")) return true;
702
+ if (await applyRateLimit(c, c.req.method ?? "GET")) return true;
574
703
  const match = matchRoute(c.url, c.cachedRoutes);
575
704
  if (!match) {
576
705
  if (c.controllersDistDir !== void 0) {
@@ -702,23 +831,24 @@ function send500(res, custom500Html) {
702
831
  res.end(custom500Html ?? "<h1>500 \u2014 Server Error</h1>");
703
832
  }
704
833
  }
705
- function withHoistedHead(htmlHead, ssrHtml) {
834
+ function withHoistedHead(htmlHead, ssrHtml, nonce) {
706
835
  const { html, headTags } = extractHeadTags(ssrHtml);
707
- return { head: injectIntoHead(htmlHead, headTags), body: html };
836
+ const head = applyNonceToInlineScripts(injectIntoHead(htmlHead, headTags), nonce);
837
+ return { head, body: html };
708
838
  }
709
839
  function buildSsrHtml(ctx, result, nonce) {
710
840
  if (typeof result === "string") {
711
- const { head, body } = withHoistedHead(ctx.htmlHead, result);
841
+ const { head, body } = withHoistedHead(ctx.htmlHead, result, nonce);
712
842
  return head + body + ctx.htmlTail;
713
843
  }
714
844
  if (isSsrRenderResult(result)) {
715
845
  const rendered = asSsrRenderResult(result);
716
846
  const dataJson = JSON.stringify(rendered.hydrationData).replace(/</g, "\\u003c");
717
847
  const hydrationScript = `<script${nonce ? ` nonce="${nonce}"` : ""}>window.__staticRouterHydrationData=${dataJson}</script>`;
718
- const { head, body } = withHoistedHead(ctx.htmlHead, rendered.html);
848
+ const { head, body } = withHoistedHead(ctx.htmlHead, rendered.html, nonce);
719
849
  return head + body + hydrationScript + ctx.htmlTail;
720
850
  }
721
- return ctx.htmlHead + ctx.htmlTail;
851
+ return applyNonceToInlineScripts(ctx.htmlHead, nonce) + ctx.htmlTail;
722
852
  }
723
853
  async function handleSsrStreaming(ctx, req, res, url, nonce) {
724
854
  if (!ctx.ssrStreamingEnabled || !ctx.ssrRenderStreaming) return false;
@@ -880,8 +1010,7 @@ async function startCommand(options) {
880
1010
  wsRoutes: cachedWsRoutes,
881
1011
  agents: cachedAgents
882
1012
  } = loadRoutesAndActions(distDir, serverDir, config.agentsDir);
883
- const flatRateLimit = config.rateLimit && "windowMs" in config.rateLimit && "max" in config.rateLimit ? config.rateLimit : void 0;
884
- const rateLimiter = flatRateLimit ? createRateLimiter(flatRateLimit) : null;
1013
+ const rateLimiter = config.rateLimit ? createRouteRateLimiter(config.rateLimit) : null;
885
1014
  const ssr = await setupSsr({
886
1015
  distDir,
887
1016
  indexHtml,
@@ -938,4 +1067,4 @@ export {
938
1067
  resolveSsrEntry,
939
1068
  startCommand
940
1069
  };
941
- //# sourceMappingURL=start-IITIEJTY.js.map
1070
+ //# sourceMappingURL=start-KR2JJR7K.js.map