@fedify/express 2.5.0-dev.2378 → 2.5.0-dev.2424
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.
- package/dist/index.cjs +83 -1
- package/dist/index.js +83 -1
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ function integrateFederation(federation, contextDataFactory) {
|
|
|
20
20
|
},
|
|
21
21
|
onNotAcceptable: () => {
|
|
22
22
|
notAcceptable = true;
|
|
23
|
+
installNotAcceptableFallback(req, res);
|
|
23
24
|
body?.restore();
|
|
24
25
|
next();
|
|
25
26
|
return new Response("Not acceptable", {
|
|
@@ -31,7 +32,7 @@ function integrateFederation(federation, contextDataFactory) {
|
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
});
|
|
34
|
-
if (notFound || notAcceptable
|
|
35
|
+
if (notFound || notAcceptable) return;
|
|
35
36
|
await setEResponse(res, response);
|
|
36
37
|
res.end();
|
|
37
38
|
res.status = () => res;
|
|
@@ -157,5 +158,86 @@ function setEResponse(res, response) {
|
|
|
157
158
|
});
|
|
158
159
|
});
|
|
159
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Waits for downstream handling to finish before replacing an unsent 404.
|
|
163
|
+
* A matched route can still fall through, and next() does not wait for an
|
|
164
|
+
* asynchronous handler. Already committed responses belong to the application.
|
|
165
|
+
*/
|
|
166
|
+
function installNotAcceptableFallback(req, res) {
|
|
167
|
+
const end = res.end;
|
|
168
|
+
const send = res.send;
|
|
169
|
+
let pending = true;
|
|
170
|
+
let applicationResponse = false;
|
|
171
|
+
const wrappedSend = function(body) {
|
|
172
|
+
applicationResponse = true;
|
|
173
|
+
return send.call(this, body);
|
|
174
|
+
};
|
|
175
|
+
res.send = wrappedSend;
|
|
176
|
+
const wrapEnd = (end) => function(...args) {
|
|
177
|
+
if (pending) {
|
|
178
|
+
Object.defineProperty(this, "end", {
|
|
179
|
+
configurable: true,
|
|
180
|
+
enumerable: true,
|
|
181
|
+
writable: true,
|
|
182
|
+
value: end
|
|
183
|
+
});
|
|
184
|
+
if (this.send === wrappedSend) this.send = send;
|
|
185
|
+
}
|
|
186
|
+
if (!pending || applicationResponse || this.headersSent || this.statusCode !== 404 || !isDefaultNotFound(req, this, args[0])) {
|
|
187
|
+
pending = false;
|
|
188
|
+
return end.apply(this, args);
|
|
189
|
+
}
|
|
190
|
+
pending = false;
|
|
191
|
+
const callback = args.find((arg) => typeof arg === "function");
|
|
192
|
+
const body = "Not acceptable";
|
|
193
|
+
this.statusCode = 406;
|
|
194
|
+
this.statusMessage = "Not Acceptable";
|
|
195
|
+
for (const header of [
|
|
196
|
+
"Content-Encoding",
|
|
197
|
+
"Content-Language",
|
|
198
|
+
"Content-Range",
|
|
199
|
+
"ETag",
|
|
200
|
+
"Last-Modified",
|
|
201
|
+
"Transfer-Encoding"
|
|
202
|
+
]) this.removeHeader(header);
|
|
203
|
+
this.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
204
|
+
this.setHeader("Content-Length", node_buffer.Buffer.byteLength(body));
|
|
205
|
+
this.vary("Accept");
|
|
206
|
+
return end.call(this, req.method === "HEAD" ? void 0 : body, "utf8", callback);
|
|
207
|
+
};
|
|
208
|
+
let wrappedEnd = wrapEnd(end);
|
|
209
|
+
Object.defineProperty(res, "end", {
|
|
210
|
+
configurable: true,
|
|
211
|
+
enumerable: true,
|
|
212
|
+
get: () => wrappedEnd,
|
|
213
|
+
set: (value) => {
|
|
214
|
+
wrappedEnd = wrapEnd(value);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Express offers no public router-exhaustion callback to middleware. Recognize
|
|
220
|
+
* finalhandler's default page, failing safe when its format changes. An app
|
|
221
|
+
* response identical to that default page cannot be distinguished from it.
|
|
222
|
+
*/
|
|
223
|
+
function isDefaultNotFound(req, res, chunk) {
|
|
224
|
+
if (res.getHeader("Content-Security-Policy") !== "default-src 'none'" || res.getHeader("X-Content-Type-Options") !== "nosniff" || res.getHeader("Content-Type") !== "text/html; charset=utf-8") return false;
|
|
225
|
+
const encodedPath = (req.originalUrl ?? req.url).split("?")[0].replace(/(^|[^\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF]([^\uDC00-\uDFFF]|$)/g, "$1�$2").replace(/(?:[^\x21\x23-\x3B\x3D\x3F-\x5F\x61-\x7A\x7C\x7E]|%(?:[^0-9A-Fa-f]|[0-9A-Fa-f][^0-9A-Fa-f]|$))+/g, encodeURI);
|
|
226
|
+
const page = `<!DOCTYPE html>
|
|
227
|
+
<html lang="en">
|
|
228
|
+
<head>
|
|
229
|
+
<meta charset="utf-8">
|
|
230
|
+
<title>Error</title>
|
|
231
|
+
</head>
|
|
232
|
+
<body>
|
|
233
|
+
<pre>${`Cannot ${req.method} ${encodedPath}`.replace(/[&<>"']/g, (character) => ({
|
|
234
|
+
"&": "&",
|
|
235
|
+
"<": "<",
|
|
236
|
+
">": ">",
|
|
237
|
+
"\"": """,
|
|
238
|
+
"'": "'"
|
|
239
|
+
})[character])}</pre>\n</body>\n</html>\n`;
|
|
240
|
+
return req.method === "HEAD" ? chunk == null && Number(res.getHeader("Content-Length")) === node_buffer.Buffer.byteLength(page) : chunk === page;
|
|
241
|
+
}
|
|
160
242
|
//#endregion
|
|
161
243
|
exports.integrateFederation = integrateFederation;
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ function integrateFederation(federation, contextDataFactory) {
|
|
|
19
19
|
},
|
|
20
20
|
onNotAcceptable: () => {
|
|
21
21
|
notAcceptable = true;
|
|
22
|
+
installNotAcceptableFallback(req, res);
|
|
22
23
|
body?.restore();
|
|
23
24
|
next();
|
|
24
25
|
return new Response("Not acceptable", {
|
|
@@ -30,7 +31,7 @@ function integrateFederation(federation, contextDataFactory) {
|
|
|
30
31
|
});
|
|
31
32
|
}
|
|
32
33
|
});
|
|
33
|
-
if (notFound || notAcceptable
|
|
34
|
+
if (notFound || notAcceptable) return;
|
|
34
35
|
await setEResponse(res, response);
|
|
35
36
|
res.end();
|
|
36
37
|
res.status = () => res;
|
|
@@ -156,5 +157,86 @@ function setEResponse(res, response) {
|
|
|
156
157
|
});
|
|
157
158
|
});
|
|
158
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Waits for downstream handling to finish before replacing an unsent 404.
|
|
162
|
+
* A matched route can still fall through, and next() does not wait for an
|
|
163
|
+
* asynchronous handler. Already committed responses belong to the application.
|
|
164
|
+
*/
|
|
165
|
+
function installNotAcceptableFallback(req, res) {
|
|
166
|
+
const end = res.end;
|
|
167
|
+
const send = res.send;
|
|
168
|
+
let pending = true;
|
|
169
|
+
let applicationResponse = false;
|
|
170
|
+
const wrappedSend = function(body) {
|
|
171
|
+
applicationResponse = true;
|
|
172
|
+
return send.call(this, body);
|
|
173
|
+
};
|
|
174
|
+
res.send = wrappedSend;
|
|
175
|
+
const wrapEnd = (end) => function(...args) {
|
|
176
|
+
if (pending) {
|
|
177
|
+
Object.defineProperty(this, "end", {
|
|
178
|
+
configurable: true,
|
|
179
|
+
enumerable: true,
|
|
180
|
+
writable: true,
|
|
181
|
+
value: end
|
|
182
|
+
});
|
|
183
|
+
if (this.send === wrappedSend) this.send = send;
|
|
184
|
+
}
|
|
185
|
+
if (!pending || applicationResponse || this.headersSent || this.statusCode !== 404 || !isDefaultNotFound(req, this, args[0])) {
|
|
186
|
+
pending = false;
|
|
187
|
+
return end.apply(this, args);
|
|
188
|
+
}
|
|
189
|
+
pending = false;
|
|
190
|
+
const callback = args.find((arg) => typeof arg === "function");
|
|
191
|
+
const body = "Not acceptable";
|
|
192
|
+
this.statusCode = 406;
|
|
193
|
+
this.statusMessage = "Not Acceptable";
|
|
194
|
+
for (const header of [
|
|
195
|
+
"Content-Encoding",
|
|
196
|
+
"Content-Language",
|
|
197
|
+
"Content-Range",
|
|
198
|
+
"ETag",
|
|
199
|
+
"Last-Modified",
|
|
200
|
+
"Transfer-Encoding"
|
|
201
|
+
]) this.removeHeader(header);
|
|
202
|
+
this.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
203
|
+
this.setHeader("Content-Length", Buffer.byteLength(body));
|
|
204
|
+
this.vary("Accept");
|
|
205
|
+
return end.call(this, req.method === "HEAD" ? void 0 : body, "utf8", callback);
|
|
206
|
+
};
|
|
207
|
+
let wrappedEnd = wrapEnd(end);
|
|
208
|
+
Object.defineProperty(res, "end", {
|
|
209
|
+
configurable: true,
|
|
210
|
+
enumerable: true,
|
|
211
|
+
get: () => wrappedEnd,
|
|
212
|
+
set: (value) => {
|
|
213
|
+
wrappedEnd = wrapEnd(value);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Express offers no public router-exhaustion callback to middleware. Recognize
|
|
219
|
+
* finalhandler's default page, failing safe when its format changes. An app
|
|
220
|
+
* response identical to that default page cannot be distinguished from it.
|
|
221
|
+
*/
|
|
222
|
+
function isDefaultNotFound(req, res, chunk) {
|
|
223
|
+
if (res.getHeader("Content-Security-Policy") !== "default-src 'none'" || res.getHeader("X-Content-Type-Options") !== "nosniff" || res.getHeader("Content-Type") !== "text/html; charset=utf-8") return false;
|
|
224
|
+
const encodedPath = (req.originalUrl ?? req.url).split("?")[0].replace(/(^|[^\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF]([^\uDC00-\uDFFF]|$)/g, "$1�$2").replace(/(?:[^\x21\x23-\x3B\x3D\x3F-\x5F\x61-\x7A\x7C\x7E]|%(?:[^0-9A-Fa-f]|[0-9A-Fa-f][^0-9A-Fa-f]|$))+/g, encodeURI);
|
|
225
|
+
const page = `<!DOCTYPE html>
|
|
226
|
+
<html lang="en">
|
|
227
|
+
<head>
|
|
228
|
+
<meta charset="utf-8">
|
|
229
|
+
<title>Error</title>
|
|
230
|
+
</head>
|
|
231
|
+
<body>
|
|
232
|
+
<pre>${`Cannot ${req.method} ${encodedPath}`.replace(/[&<>"']/g, (character) => ({
|
|
233
|
+
"&": "&",
|
|
234
|
+
"<": "<",
|
|
235
|
+
">": ">",
|
|
236
|
+
"\"": """,
|
|
237
|
+
"'": "'"
|
|
238
|
+
})[character])}</pre>\n</body>\n</html>\n`;
|
|
239
|
+
return req.method === "HEAD" ? chunk == null && Number(res.getHeader("Content-Length")) === Buffer.byteLength(page) : chunk === page;
|
|
240
|
+
}
|
|
159
241
|
//#endregion
|
|
160
242
|
export { integrateFederation };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/express",
|
|
3
|
-
"version": "2.5.0-dev.
|
|
3
|
+
"version": "2.5.0-dev.2424+97214632",
|
|
4
4
|
"description": "Integrate Fedify with Express",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Fedify",
|
|
@@ -46,13 +46,14 @@
|
|
|
46
46
|
],
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"express": ">=4.0.0",
|
|
49
|
-
"@fedify/fedify": "^2.5.0-dev.
|
|
49
|
+
"@fedify/fedify": "^2.5.0-dev.2424+97214632"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/express": "^4.0.0",
|
|
53
53
|
"@types/node": "^24.12.2",
|
|
54
54
|
"tsdown": "^0.22.0",
|
|
55
|
-
"typescript": "^6.0.0"
|
|
55
|
+
"typescript": "^6.0.0",
|
|
56
|
+
"@fedify/fixture": "2.0.0"
|
|
56
57
|
},
|
|
57
58
|
"scripts": {
|
|
58
59
|
"build:self": "tsdown",
|