@aetherframework/middleware 1.0.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.
@@ -0,0 +1,214 @@
1
+
2
+ /**
3
+ * @license MIT
4
+ * Copyright (c) 2026-present AetherFramework Contributors.
5
+ * SPDX-License-Identifier: MIT
6
+ * @module @aetherframework/middleware/middleware/json.js
7
+ */
8
+ /**
9
+ * Create JSON parsing middleware for AetherJS
10
+ * @param {Object} options - JSON parser configuration
11
+ * @returns {Function} - JSON parser middleware function
12
+ */
13
+ function createJsonMiddleware(options = {}) {
14
+ // Load configuration from environment variables
15
+ const envConfig = {
16
+ limit: process.env.BODY_LIMIT_JSON,
17
+ strict: process.env.JSON_STRICT,
18
+ reviver: process.env.JSON_REVIVER,
19
+ enable: process.env.JSON_ENABLE,
20
+ };
21
+
22
+ // Default configuration
23
+ const defaults = {
24
+ enabled: envConfig.enable !== "false",
25
+ limit: parseSize(envConfig.limit || "1mb"),
26
+ strict: envConfig.strict !== "false",
27
+ reviver: envConfig.reviver ? eval(`(${envConfig.reviver})`) : null,
28
+
29
+ // Error handling
30
+ onError: (context, error) => {
31
+ context.setStatus(400).json({
32
+ error: "Bad Request",
33
+ message: "Invalid JSON format",
34
+ details: error.message,
35
+ });
36
+ },
37
+
38
+ // Size limit exceeded handler
39
+ onLimitExceeded: (context, limit) => {
40
+ context.setStatus(413).json({
41
+ error: "Payload Too Large",
42
+ message: `JSON payload exceeds ${limit} bytes limit`,
43
+ });
44
+ },
45
+ };
46
+
47
+ // Parse size string to bytes
48
+ function parseSize(size) {
49
+ const units = {
50
+ b: 1,
51
+ kb: 1024,
52
+ mb: 1024 * 1024,
53
+ gb: 1024 * 1024 * 1024,
54
+ };
55
+
56
+ // 1. 确保 size 是字符串,并转换为小写以便匹配
57
+ const lowerSize = String(size).toLowerCase();
58
+
59
+ // 2. 执行正则匹配
60
+ const match = lowerSize.match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)$/);
61
+
62
+ if (!match) {
63
+ throw new Error(`Invalid size format: ${size}`);
64
+ }
65
+
66
+ // 3. 从匹配数组中提取数值部分 (index 1) 和单位部分 (index 2)
67
+ const value = parseFloat(match[1]); // 提取第一个捕获组(数字)
68
+ const unit = match[2]; // 提取第二个捕获组(单位)
69
+
70
+ // 4. 计算并返回字节数
71
+ return value * (units[unit] || 1);
72
+ }
73
+
74
+ // Merge with provided options
75
+ const config = { ...defaults, ...options };
76
+
77
+ /**
78
+ * Parse JSON from request body
79
+ * @param {Object} request - HTTP request object
80
+ * @returns {Promise<Object>} - Parsed JSON object
81
+ */
82
+ async function parseJson(request) {
83
+ return new Promise((resolve, reject) => {
84
+ const chunks = [];
85
+ let totalLength = 0;
86
+
87
+ request.on("data", (chunk) => {
88
+ totalLength += chunk.length;
89
+
90
+ if (totalLength > config.limit) {
91
+ request.destroy();
92
+ reject(new Error(`JSON payload exceeds ${config.limit} bytes limit`));
93
+ return;
94
+ }
95
+
96
+ chunks.push(chunk);
97
+ });
98
+
99
+ request.on("end", () => {
100
+ try {
101
+ const buffer = Buffer.concat(chunks);
102
+ const text = buffer.toString("utf8");
103
+
104
+ if (config.strict && text.trim() === "") {
105
+ reject(new Error("Empty JSON payload"));
106
+ return;
107
+ }
108
+
109
+ const parsed = config.reviver
110
+ ? JSON.parse(text, config.reviver)
111
+ : JSON.parse(text);
112
+
113
+ resolve(parsed);
114
+ } catch (error) {
115
+ reject(error);
116
+ }
117
+ });
118
+
119
+ request.on("error", (error) => {
120
+ reject(error);
121
+ });
122
+ });
123
+ }
124
+
125
+ /**
126
+ * JSON middleware function
127
+ * @param {AetherContext} context - AetherJS execution context
128
+ * @param {Function} next - Next middleware function
129
+ */
130
+ return async function jsonMiddleware(context, next) {
131
+ if (!config.enabled) {
132
+ return typeof next === 'function' ? await next() : undefined;
133
+ }
134
+
135
+ // Skip if not JSON content type
136
+ const contentType = context.getHeader("content-type") || "";
137
+ if (!contentType.includes("application/json")) {
138
+ return typeof next === 'function' ? await next() : undefined;
139
+ }
140
+
141
+ // Skip if no body is expected
142
+ if (context.method === "GET" || context.method === "HEAD") {
143
+ return typeof next === 'function' ? await next() : undefined;
144
+ }
145
+
146
+ const contentLength = parseInt(context.getHeader("content-length")) || 0;
147
+
148
+ // Skip if no content
149
+ if (contentLength === 0) {
150
+ return typeof next === 'function' ? await next() : undefined;
151
+ }
152
+
153
+ // Check size limit
154
+ if (contentLength > config.limit) {
155
+ return config.onLimitExceeded(context, config.limit);
156
+ }
157
+
158
+ try {
159
+ // Parse JSON body
160
+ const json = await parseJson(context._request);
161
+
162
+ // Store parsed JSON in context
163
+ context.setState("json", json);
164
+ context.setState("body", json);
165
+
166
+ // Add JSON methods to context
167
+ context.jsonBody = json;
168
+ context.getJson = () => json;
169
+
170
+ if (typeof next === 'function') {
171
+ await next();
172
+ }
173
+ } catch (error) {
174
+ if (error.message.includes("exceeds")) {
175
+ return config.onLimitExceeded(context, config.limit);
176
+ } else {
177
+ return config.onError(context, error);
178
+ }
179
+ }
180
+ };
181
+
182
+ }
183
+
184
+ // Add utility functions to the middleware
185
+ createJsonMiddleware.parse = function (text, reviver) {
186
+ try {
187
+ return reviver ? JSON.parse(text, reviver) : JSON.parse(text);
188
+ } catch (error) {
189
+ throw new Error(`JSON parse error: ${error.message}`);
190
+ }
191
+ };
192
+
193
+ createJsonMiddleware.stringify = function (value, replacer, space) {
194
+ try {
195
+ return JSON.stringify(value, replacer, space);
196
+ } catch (error) {
197
+ throw new Error(`JSON stringify error: ${error.message}`);
198
+ }
199
+ };
200
+
201
+ createJsonMiddleware.isValid = function (text) {
202
+ try {
203
+ JSON.parse(text);
204
+ return true;
205
+ } catch {
206
+ return false;
207
+ }
208
+ };
209
+
210
+ createJsonMiddleware.format = function (json, space = 2) {
211
+ return JSON.stringify(json, null, space);
212
+ };
213
+
214
+ export default createJsonMiddleware;