@nullix/zod-mongoose-studio 3.3.0 → 3.3.2

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 (119) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_fonts/57NSSoFy1VLVs2gqly8Ls9awBnZMFyXGrefpmqvdqmc-zJfbBtpgM4cDmcXBsqZNW79_kFnlpPd62b48glgdydA.woff2 +0 -0
  3. package/.output/public/_fonts/8VR2wSMN-3U4NbWAVYXlkRV6hA0jFBXP-0RtL3X7fko-x2gYI4qfmkRdxyQQUPaBZdZdgl1TeVrquF_TxHeM4lM.woff2 +0 -0
  4. package/.output/public/_fonts/GsKUclqeNLJ96g5AU593ug6yanivOiwjW_7zESNPChw-jHA4tBeM1bjF7LATGUpfBuSTyomIFrWBTzjF7txVYfg.woff2 +0 -0
  5. package/.output/public/_fonts/Ld1FnTo3yTIwDyGfTQ5-Fws9AWsCbKfMvgxduXr7JcY-W25bL8NF1fjpLRSOgJb7RoZPHqGQNwMTM7S9tHVoxx8.woff2 +0 -0
  6. package/.output/public/_fonts/NdzqRASp2bovDUhQT1IRE_EMqKJ2KYQdTCfFcBvL8yw-KhwZiS86o3fErOe5GGMExHUemmI_dBfaEFxjISZrBd0.woff2 +0 -0
  7. package/.output/public/_fonts/iTkrULNFJJkTvihIg1Vqi5IODRH_9btXCioVF5l98I8-AndUyau2HR2felA_ra8V2mutQgschhasE5FD1dXGJX8.woff2 +0 -0
  8. package/.output/public/_nuxt/B6lqezyA.js +27 -0
  9. package/.output/public/_nuxt/{IxYCG8hB.js → B7ScFmDB.js} +1 -1
  10. package/.output/public/_nuxt/B8gqXD2U.js +1 -0
  11. package/.output/public/_nuxt/{ikxafTHA.js → BC9tFNFa.js} +180 -184
  12. package/.output/public/_nuxt/{qwxNOyg7.js → Bbi9UBvi.js} +1 -1
  13. package/.output/public/_nuxt/{CNZorjDf.js → BcYnCaqL.js} +1 -1
  14. package/.output/public/_nuxt/{BzhSDy30.js → BlZ0x31M.js} +1 -1
  15. package/.output/public/_nuxt/Bw82IGM8.js +1 -0
  16. package/.output/public/_nuxt/{qQyvCkbV.js → C8xmwXC2.js} +1 -1
  17. package/.output/public/_nuxt/{Ce-T_1aD.js → C919AVYR.js} +1 -1
  18. package/.output/public/_nuxt/{nDZOJ7rN.js → CsQL5F-l.js} +2 -2
  19. package/.output/public/_nuxt/Czp4_2o-.js +33 -0
  20. package/.output/public/_nuxt/{CC3n3Aga.js → DJa7_7Hz.js} +1 -1
  21. package/.output/public/_nuxt/DKK0Mrrg.js +1 -0
  22. package/.output/public/_nuxt/DbUZZZZu.js +6 -0
  23. package/.output/public/_nuxt/{B7P0wQ35.js → DbqvVYbv.js} +1 -1
  24. package/.output/public/_nuxt/{Ick_23I4.js → JkiDMdYG.js} +1 -1
  25. package/.output/public/_nuxt/{81vqh61j.js → OVQ3C2Ge.js} +1 -1
  26. package/.output/public/_nuxt/{Cs8GXJCB.js → PXBhK7S_.js} +1 -1
  27. package/.output/public/_nuxt/{CAd-qHfI.js → YC-RLggm.js} +1 -1
  28. package/.output/public/_nuxt/builds/latest.json +1 -1
  29. package/.output/public/_nuxt/builds/meta/46174b5e-cab6-4a73-95f8-66a1c9b1be2b.json +1 -0
  30. package/.output/public/_nuxt/{tzj4nvDE.js → d1NBz903.js} +2 -2
  31. package/.output/public/_nuxt/{entry.J_CbC0oY.css → entry.DaqrxXuH.css} +1 -1
  32. package/.output/public/_nuxt/error-404.CTSXlFYa.css +1 -0
  33. package/.output/public/_nuxt/error-500.DqEfQnVZ.css +1 -0
  34. package/.output/public/_nuxt/{Bd32GOUy.js → iGzUozbF.js} +1 -1
  35. package/.output/public/_nuxt/{DjPT-NCE.js → iKW_mt0p.js} +1 -1
  36. package/.output/server/chunks/_/error-500.mjs.map +1 -1
  37. package/.output/server/chunks/build/client.precomputed.mjs +1 -1
  38. package/.output/server/chunks/build/{error-404-BN-7rNE4.mjs → error-404-KRkA0DPI.mjs} +10 -4
  39. package/.output/server/chunks/build/error-404-KRkA0DPI.mjs.map +1 -0
  40. package/.output/server/chunks/build/error-404-styles.I0NYw-Bn.mjs +8 -0
  41. package/.output/server/chunks/build/error-404-styles.I0NYw-Bn.mjs.map +1 -0
  42. package/.output/server/chunks/build/{error-500-DhIQ-2yt.mjs → error-500-DIIFf55p.mjs} +10 -4
  43. package/.output/server/chunks/build/error-500-DIIFf55p.mjs.map +1 -0
  44. package/.output/server/chunks/build/error-500-styles.D1cs9hQy.mjs +8 -0
  45. package/.output/server/chunks/build/error-500-styles.D1cs9hQy.mjs.map +1 -0
  46. package/.output/server/chunks/build/index-CB1N4gax.mjs +557 -0
  47. package/.output/server/chunks/build/index-CB1N4gax.mjs.map +1 -0
  48. package/.output/server/chunks/build/server.mjs +326 -248
  49. package/.output/server/chunks/build/server.mjs.map +1 -1
  50. package/.output/server/chunks/build/styles.mjs +4 -4
  51. package/.output/server/chunks/nitro/nitro.mjs +2319 -852
  52. package/.output/server/chunks/nitro/nitro.mjs.map +1 -1
  53. package/.output/server/chunks/routes/api/editor-types.get.mjs +113 -0
  54. package/.output/server/chunks/routes/api/editor-types.get.mjs.map +1 -0
  55. package/.output/server/chunks/routes/api/parse.post.mjs +26 -1448
  56. package/.output/server/chunks/routes/api/parse.post.mjs.map +1 -1
  57. package/.output/server/chunks/routes/api/resolve.get.mjs +6 -0
  58. package/.output/server/chunks/routes/api/resolve.get.mjs.map +1 -1
  59. package/.output/server/chunks/routes/api/studio/run.post.mjs +40 -0
  60. package/.output/server/chunks/routes/api/studio/run.post.mjs.map +1 -0
  61. package/.output/server/chunks/routes/renderer.mjs +1 -1
  62. package/.output/server/chunks/routes/renderer.mjs.map +1 -1
  63. package/.output/server/index.mjs +7 -1
  64. package/.output/server/index.mjs.map +1 -1
  65. package/.output/server/node_modules/acorn/dist/acorn.mjs +6266 -0
  66. package/.output/server/node_modules/acorn/package.json +50 -0
  67. package/.output/server/node_modules/ip-address/dist/common.js +112 -4
  68. package/.output/server/node_modules/ip-address/dist/ipv4.js +322 -87
  69. package/.output/server/node_modules/ip-address/dist/ipv6.js +654 -199
  70. package/.output/server/node_modules/ip-address/dist/v4/constants.js +45 -2
  71. package/.output/server/node_modules/ip-address/dist/v6/constants.js +55 -3
  72. package/.output/server/node_modules/ip-address/dist/v6/helpers.js +12 -3
  73. package/.output/server/node_modules/ip-address/package.json +49 -37
  74. package/.output/server/node_modules/ufo/dist/index.mjs +2 -2
  75. package/.output/server/node_modules/ufo/package.json +9 -9
  76. package/.output/server/package.json +4 -3
  77. package/README.md +65 -20
  78. package/app/assets/css/main.css +1 -0
  79. package/app/assets/css/studio.css +131 -0
  80. package/app/components/StudioConversion.vue +41 -0
  81. package/app/components/StudioDataPanel.vue +21 -0
  82. package/app/components/StudioEditor.vue +78 -102
  83. package/app/components/StudioExpandButton.vue +11 -0
  84. package/app/components/StudioOutput.vue +7 -1
  85. package/app/components/StudioSourcePanel.vue +16 -0
  86. package/app/components/StudioValidation.vue +47 -0
  87. package/app/components/ZodMongooseStudio.vue +216 -198
  88. package/app/composables/useStudioRunner.ts +142 -0
  89. package/app/pages/index.vue +1 -1
  90. package/app/plugins/monaco.client.ts +2 -0
  91. package/bin/index.mjs +5 -0
  92. package/nuxt.config.ts +4 -3
  93. package/package.json +6 -3
  94. package/server/api/editor-types.get.ts +7 -0
  95. package/server/api/parse.post.ts +17 -175
  96. package/server/api/studio/run.post.ts +29 -0
  97. package/server/middleware/security.ts +29 -7
  98. package/server/utils/editor-types.json +1 -0
  99. package/server/utils/example-data.ts +80 -0
  100. package/server/utils/parse-request.ts +28 -0
  101. package/server/utils/runner-client.ts +40 -0
  102. package/server/utils/studio-execution.ts +176 -0
  103. package/.output/public/_nuxt/BD40YYqZ.js +0 -33
  104. package/.output/public/_nuxt/BlQmLjV4.js +0 -1
  105. package/.output/public/_nuxt/CJ4Cv9fY.js +0 -61
  106. package/.output/public/_nuxt/builds/meta/495a8ebb-444d-4414-9b98-c181aa84d817.json +0 -1
  107. package/.output/public/_nuxt/error-404.64pZEiDn.css +0 -1
  108. package/.output/public/_nuxt/error-500.DHOpI0Ir.css +0 -1
  109. package/.output/public/_nuxt/rH_O4qXT.js +0 -1
  110. package/.output/server/chunks/build/error-404-BN-7rNE4.mjs.map +0 -1
  111. package/.output/server/chunks/build/error-404-styles.Byx46oLR.mjs +0 -8
  112. package/.output/server/chunks/build/error-404-styles.Byx46oLR.mjs.map +0 -1
  113. package/.output/server/chunks/build/error-500-DhIQ-2yt.mjs.map +0 -1
  114. package/.output/server/chunks/build/error-500-styles.BL3UT0Wa.mjs +0 -8
  115. package/.output/server/chunks/build/error-500-styles.BL3UT0Wa.mjs.map +0 -1
  116. package/.output/server/chunks/build/index-B4T6zHp0.mjs +0 -1904
  117. package/.output/server/chunks/build/index-B4T6zHp0.mjs.map +0 -1
  118. package/.output/server/node_modules/zod/index.js +0 -4
  119. /package/.output/public/_nuxt/{editor.CdBKkuhR.css → monaco.CdBKkuhR.css} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nullix/zod-mongoose-studio",
3
- "version": "3.3.0",
3
+ "version": "3.3.2",
4
4
  "description": "Monaco-powered TypeScript playground for @nullix/zod-mongoose",
5
5
  "main": "./nuxt.config.ts",
6
6
  "bin": {
@@ -39,12 +39,13 @@
39
39
  "provenance": true
40
40
  },
41
41
  "dependencies": {
42
+ "acorn": "8.16.0",
42
43
  "esbuild": "0.28.0",
43
44
  "get-port-please": "3.2.0",
44
45
  "mongoose": "9.6.3",
45
46
  "open": "11.0.0",
46
47
  "zod": "4.3.6",
47
- "@nullix/zod-mongoose": "3.3.0"
48
+ "@nullix/zod-mongoose": "3.3.2"
48
49
  },
49
50
  "devDependencies": {
50
51
  "@nuxt/ui": "4.6.0",
@@ -57,10 +58,12 @@
57
58
  "vue-tsc": "3.2.6"
58
59
  },
59
60
  "scripts": {
60
- "build": "nuxt build || echo 'Build finished with warnings'",
61
+ "generate:editor-types": "node scripts/generate-editor-types.mjs",
62
+ "build": "nuxt build",
61
63
  "dev": "nuxt dev -p 34003",
62
64
  "generate": "nuxt generate",
63
65
  "preview": "nuxt preview",
66
+ "test": "node --experimental-strip-types --test test/*.test.ts",
64
67
  "postinstall": "nuxt prepare"
65
68
  }
66
69
  }
@@ -0,0 +1,7 @@
1
+ import { setHeader } from "h3";
2
+ import editorTypes from "../utils/editor-types.json";
3
+
4
+ export default defineEventHandler((event) => {
5
+ setHeader(event, "Cache-Control", "public, max-age=3600");
6
+ return editorTypes;
7
+ });
@@ -1,187 +1,29 @@
1
- import { defineEventHandler, readBody, createError } from "h3";
2
- import vm from "node:vm";
3
- import { transformSync } from "esbuild";
4
- import util from "node:util";
5
- import { fileURLToPath } from "node:url";
6
- import { dirname } from "node:path";
1
+ import { defineEventHandler, createError } from "h3";
2
+ import { executeStudio, StudioExecutionError } from "../utils/studio-execution";
3
+ import { readStudioRequest } from "../utils/parse-request";
7
4
 
8
- import * as zod from "zod";
9
- import * as mongoose from "mongoose";
10
- import * as mongooseZod from "@nullix/zod-mongoose";
11
-
12
- // Initialize Mongoose in ESM environment
13
- mongooseZod.setMongoose(mongoose.default || mongoose);
14
-
15
- // Shim __filename and __dirname for ESM environments (Nitro)
16
- let _filename = "/sandbox/main.ts";
17
- let _dirname = "/sandbox";
18
-
19
- try {
20
- _filename = fileURLToPath(import.meta.url);
21
- _dirname = dirname(_filename);
22
- } catch {
23
- // Fallback if import.meta.url is not available or valid in the current context
24
- }
25
-
26
- // The converter exposes a module-level Hookable instance. User code can add
27
- // hooks to it, so parse requests must not overlap and must always clean it up
28
- // before the next request starts.
5
+ const MAX_PENDING_PARSES = 3;
29
6
  let parseQueue = Promise.resolve();
7
+ let pendingParses = 0;
30
8
 
31
- async function acquireParseLock(): Promise<() => void> {
9
+ async function acquireLocalLock(): Promise<() => void> {
10
+ if (pendingParses >= MAX_PENDING_PARSES) throw createError({ statusCode: 503, message: "Studio is busy. Try again shortly." });
11
+ pendingParses++;
32
12
  const previous = parseQueue;
33
13
  let release!: () => void;
34
-
35
- parseQueue = new Promise<void>((resolve) => {
36
- release = resolve;
37
- });
38
-
14
+ parseQueue = new Promise<void>((resolve) => { release = () => { pendingParses--; resolve(); }; });
39
15
  await previous;
40
16
  return release;
41
17
  }
42
18
 
43
- function getErrorMessage(error: unknown): string {
44
- if (error instanceof Error) return error.message || error.name;
45
- return String(error);
46
- }
47
-
48
- // @ts-ignore
49
- if (typeof __filename === "undefined") {
50
- // @ts-ignore
51
- globalThis.__filename = _filename;
52
- }
53
- // @ts-ignore
54
- if (typeof __dirname === "undefined") {
55
- // @ts-ignore
56
- globalThis.__dirname = _dirname;
57
- }
58
-
59
19
  export default defineEventHandler(async (event) => {
60
- const config = useRuntimeConfig(event);
61
- const isDocsMode = config.public.isDocsMode;
62
- const { sourceCode } = await readBody(event);
63
-
64
- if (!sourceCode) {
65
- throw createError({ statusCode: 400, message: "Missing sourceCode" });
66
- }
67
-
68
- const releaseParseLock = await acquireParseLock();
69
-
70
- // 1. Transpile exactly what the user wrote
71
- try {
72
- // A user hook can throw before the converter has completed. Since hooks are
73
- // global within the server process, cleanup must happen for both success and
74
- // failure, otherwise the user's hook would affect every later visitor.
75
- mongooseZod.hooks.removeAllHooks();
76
-
77
- let jsCode: string;
78
- try {
79
- jsCode = transformSync(sourceCode, {
80
- loader: "ts",
81
- format: "cjs",
82
- target: "esnext",
83
- // Provide these globals during transpilation just in case
84
- define: {
85
- __filename: '"/sandbox/main.ts"',
86
- __dirname: '"/sandbox"',
87
- },
88
- }).code;
89
- } catch (error: unknown) {
90
- throw createError({
91
- statusCode: 400,
92
- message: `Compilation Error: ${getErrorMessage(error)}`,
93
- });
94
- }
95
- // 2. Map imports securely
96
- const sandboxRequire = (moduleName: string) => {
97
- if (moduleName === "zod" || moduleName === "zod/v4") return zod;
98
- if (moduleName === "@nullix/zod-mongoose") return mongooseZod;
99
- if (moduleName === "mongoose") return mongoose.default || mongoose;
100
- throw new Error(`Security Exception: Module "${moduleName}" is blocked.`);
101
- };
102
-
103
- const sandbox = {
104
- require: sandboxRequire,
105
- console: { log: () => {}, warn: () => {}, error: () => {} },
106
- __filename: "/sandbox/main.ts",
107
- __dirname: "/sandbox",
108
- };
109
- const context = vm.createContext(sandbox);
110
-
111
- const script = new vm.Script(`
112
- ((__filename, __dirname) => {
113
- let exports = {};
114
- let module = { exports };
115
-
116
- ${jsCode}
117
-
118
- return module.exports;
119
- })("/sandbox/main.ts", "/sandbox");
120
- `);
121
-
122
- // 3. Execute code and grab the default export
123
- const rawExport = script.runInContext(context, {
124
- timeout: isDocsMode ? 1000 : 5000,
125
- });
126
-
127
- const schema = rawExport.default || rawExport;
128
-
129
- if (!schema || !schema._def) {
130
- throw new Error("You must `export default` a valid Zod schema.");
131
- }
132
-
133
- // 4. Run BOTH functions on the user's schema
134
- const extractedDef = mongooseZod.extractMongooseDef(schema);
135
- const mongooseSchema = mongooseZod.toMongooseSchema(schema, { modelName: 'ParsedSchema' });
136
-
137
- // Format the outputs nicely
138
- const formatOpts = { depth: null, showHidden: false, colors: false };
139
-
140
- // Mongoose schemas are massive class instances, so we extract the raw .obj definition to display
141
- const formatValue = (val: any): any => {
142
- if (typeof val === "function") {
143
- return val.name || val.toString();
144
- }
145
- if (Array.isArray(val)) {
146
- return val.map(formatValue);
147
- }
148
- if (
149
- val !== null &&
150
- typeof val === "object" &&
151
- val.constructor === Object
152
- ) {
153
- const formatted: any = {};
154
- for (const key in val) {
155
- formatted[key] = formatValue(val[key]);
156
- }
157
- return formatted;
158
- }
159
- return val;
160
- };
161
-
162
- const formattedDef = formatValue(extractedDef);
163
- // schemaObj is a Mongoose Schema instance, we only care about its .obj for display
164
- const formattedSchema = formatValue(mongooseSchema.obj);
165
- // @ts-ignore
166
- const formattedTree = formatValue(mongooseSchema.tree);
167
- // @ts-ignore
168
- const formattedPaths = formatValue(mongooseSchema.paths);
169
-
170
- return {
171
- definition: util.inspect(formattedDef, formatOpts),
172
- schemaObj: util.inspect(formattedSchema, formatOpts),
173
- schemaTree: util.inspect(formattedTree, formatOpts),
174
- schemaPaths: util.inspect(formattedPaths, formatOpts),
175
- };
176
- } catch (err: any) {
177
- const message = getErrorMessage(err);
178
- const isCompilationError = err?.statusCode === 400;
179
- throw createError({
180
- statusCode: isCompilationError ? 400 : 500,
181
- message: isCompilationError ? message : `Execution Error: ${message}`,
182
- });
183
- } finally {
184
- mongooseZod.hooks.removeAllHooks();
185
- releaseParseLock();
20
+ if (useRuntimeConfig(event).public.isDocsMode) {
21
+ throw createError({ statusCode: 403, message: "Local Studio execution is unavailable in docs mode." });
186
22
  }
23
+ const body = await readStudioRequest(event);
24
+ const release = await acquireLocalLock();
25
+ try { return executeStudio(body, 5000); }
26
+ catch (error: any) {
27
+ throw createError({ statusCode: error instanceof StudioExecutionError ? 400 : 500, message: error?.message || "Studio execution failed." });
28
+ } finally { release(); }
187
29
  });
@@ -0,0 +1,29 @@
1
+ import { createError, defineEventHandler } from "h3";
2
+ import { readStudioRequest } from "../../utils/parse-request";
3
+ import { runInContainer } from "../../utils/runner-client";
4
+
5
+ export default defineEventHandler(async (event) => {
6
+ if (!useRuntimeConfig(event).public.isDocsMode) {
7
+ throw createError({
8
+ statusCode: 403,
9
+ message: "Use the local Studio endpoint in CLI mode.",
10
+ });
11
+ }
12
+ const socketPath =
13
+ process.env.STUDIO_RUNNER_SOCKET || "/run/zod-studio/runner.sock";
14
+ if (!socketPath)
15
+ throw createError({
16
+ statusCode: 503,
17
+ message: "Studio runner is not configured.",
18
+ });
19
+ const body = await readStudioRequest(event);
20
+ try {
21
+ return await runInContainer(body, socketPath);
22
+ } catch (error: any) {
23
+ if (error?.statusCode) throw error;
24
+ throw createError({
25
+ statusCode: 503,
26
+ message: "Studio runner is unavailable. Try again shortly.",
27
+ });
28
+ }
29
+ });
@@ -1,14 +1,25 @@
1
- import { defineEventHandler, createError, getRequestIP } from "h3";
1
+ import { defineEventHandler, createError, getHeader, getRequestIP, setHeader } from "h3";
2
2
 
3
3
  // Simple in-memory rate limiting map: IP -> array of timestamps
4
4
  const rateLimitMap = new Map<string, number[]>();
5
- const RATE_LIMIT = 2; // 2 requests
5
+ const RATE_LIMIT = 8; // Allow quick edits and shortcut runs without throttling normal use.
6
6
  const TIME_WINDOW = 1000; // per 1 second (1000ms)
7
+ const GLOBAL_RATE_LIMIT = 20;
8
+ let globalWindowStart = 0;
9
+ let globalRequests = 0;
7
10
 
8
11
  export default defineEventHandler((event) => {
9
- const isDocsMode = process.env.DOCS_MODE === "true";
12
+ const isDocsMode = useRuntimeConfig(event).public.isDocsMode;
10
13
  const path = event.path;
11
14
 
15
+ if (!isDocsMode && path.startsWith("/api/")) {
16
+ const host = getHeader(event, "host") || "";
17
+ const origin = getHeader(event, "origin");
18
+ const localHost = /^(localhost|127\.0\.0\.1)(:\d+)?$/.test(host);
19
+ const localOrigin = !origin || /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/.test(origin);
20
+ if (!localHost || !localOrigin) throw createError({ statusCode: 403, message: "Local Studio accepts only loopback requests." });
21
+ }
22
+
12
23
  // 1. Environment Check: Disable local resolving in Docs mode
13
24
  if (isDocsMode && path.startsWith("/api/resolve")) {
14
25
  throw createError({
@@ -17,8 +28,8 @@ export default defineEventHandler((event) => {
17
28
  });
18
29
  }
19
30
 
20
- // 2. Rate Limiting for Parse endpoint in Docs Mode
21
- if (isDocsMode && path.startsWith("/api/parse")) {
31
+ // 2. Rate limiting for the parse endpoint in Docs Mode
32
+ if (isDocsMode && path === "/api/studio/run") {
22
33
  const ip = getRequestIP(event) || "unknown";
23
34
  const now = Date.now();
24
35
 
@@ -27,14 +38,25 @@ export default defineEventHandler((event) => {
27
38
  // Filter timestamps within the current window
28
39
  const recentRequests = timestamps.filter((t) => now - t < TIME_WINDOW);
29
40
 
30
- if (recentRequests.length >= RATE_LIMIT) {
41
+ if (now - globalWindowStart >= TIME_WINDOW) {
42
+ globalWindowStart = now;
43
+ globalRequests = 0;
44
+ }
45
+ if (rateLimitMap.size > 1024) {
46
+ for (const [key, entries] of rateLimitMap) {
47
+ if (entries.every((time) => now - time >= TIME_WINDOW)) rateLimitMap.delete(key);
48
+ }
49
+ }
50
+ if (recentRequests.length >= RATE_LIMIT || globalRequests >= GLOBAL_RATE_LIMIT || (rateLimitMap.size >= 1024 && !rateLimitMap.has(ip))) {
51
+ setHeader(event, "Retry-After", 1);
31
52
  throw createError({
32
53
  statusCode: 429,
33
- message: "Too Many Requests. Limit is 2 requests per second.",
54
+ message: "Too many runs. Wait a moment and try again.",
34
55
  });
35
56
  }
36
57
 
37
58
  recentRequests.push(now);
38
59
  rateLimitMap.set(ip, recentRequests);
60
+ globalRequests++;
39
61
  }
40
62
  });