@hardlydifficult/http 1.0.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.
package/dist/http.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { IncomingMessage, ServerResponse } from "http";
2
+ export declare const MAX_BODY_BYTES: number;
3
+ /** Read the full request body as a string, rejecting if it exceeds maxBytes. */
4
+ export declare function readBody(req: IncomingMessage, maxBytes?: number): Promise<string>;
5
+ /** Send a JSON response */
6
+ export declare function sendJson(res: ServerResponse, status: number, body: unknown, corsOrigin: string): void;
7
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAE5D,eAAO,MAAM,cAAc,QAAc,CAAC;AAE1C,gFAAgF;AAChF,wBAAgB,QAAQ,CACtB,GAAG,EAAE,eAAe,EACpB,QAAQ,SAAiB,GACxB,OAAO,CAAC,MAAM,CAAC,CAwCjB;AAED,2BAA2B;AAC3B,wBAAgB,QAAQ,CACtB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,MAAM,GACjB,IAAI,CAQN"}
package/dist/http.js ADDED
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_BODY_BYTES = void 0;
4
+ exports.readBody = readBody;
5
+ exports.sendJson = sendJson;
6
+ exports.MAX_BODY_BYTES = 1024 * 1024; // 1 MB
7
+ /** Read the full request body as a string, rejecting if it exceeds maxBytes. */
8
+ function readBody(req, maxBytes = exports.MAX_BODY_BYTES) {
9
+ return new Promise((resolve, reject) => {
10
+ const chunks = [];
11
+ let totalBytes = 0;
12
+ let done = false;
13
+ const onError = (err) => {
14
+ if (!done) {
15
+ done = true;
16
+ reject(err);
17
+ }
18
+ };
19
+ req.on("data", (chunk) => {
20
+ if (done) {
21
+ return;
22
+ }
23
+ totalBytes += chunk.length;
24
+ if (totalBytes > maxBytes) {
25
+ done = true;
26
+ // Replace error handler with a no-op before destroying to prevent
27
+ // the destroy-induced error event from becoming an uncaught exception.
28
+ req.removeListener("error", onError);
29
+ req.on("error", () => {
30
+ // swallow errors after rejection
31
+ });
32
+ req.destroy();
33
+ reject(new Error("Payload too large"));
34
+ return;
35
+ }
36
+ chunks.push(chunk);
37
+ });
38
+ req.on("end", () => {
39
+ if (!done) {
40
+ done = true;
41
+ resolve(Buffer.concat(chunks).toString());
42
+ }
43
+ });
44
+ req.on("error", onError);
45
+ });
46
+ }
47
+ /** Send a JSON response */
48
+ function sendJson(res, status, body, corsOrigin) {
49
+ res.writeHead(status, {
50
+ "Content-Type": "application/json",
51
+ "Access-Control-Allow-Origin": corsOrigin,
52
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
53
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
54
+ });
55
+ res.end(JSON.stringify(body));
56
+ }
57
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":";;;AAKA,4BA2CC;AAGD,4BAaC;AA9DY,QAAA,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAElD,gFAAgF;AAChF,SAAgB,QAAQ,CACtB,GAAoB,EACpB,QAAQ,GAAG,sBAAc;IAEzB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,IAAI,GAAG,KAAK,CAAC;QAEjB,MAAM,OAAO,GAAG,CAAC,GAAU,EAAE,EAAE;YAC7B,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,IAAI,GAAG,IAAI,CAAC;gBACZ,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;QACH,CAAC,CAAC;QAEF,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAC/B,IAAI,IAAI,EAAE,CAAC;gBACT,OAAO;YACT,CAAC;YACD,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC;YAC3B,IAAI,UAAU,GAAG,QAAQ,EAAE,CAAC;gBAC1B,IAAI,GAAG,IAAI,CAAC;gBACZ,kEAAkE;gBAClE,uEAAuE;gBACvE,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBACrC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;oBACnB,iCAAiC;gBACnC,CAAC,CAAC,CAAC;gBACH,GAAG,CAAC,OAAO,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;gBACvC,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACjB,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,IAAI,GAAG,IAAI,CAAC;gBACZ,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,2BAA2B;AAC3B,SAAgB,QAAQ,CACtB,GAAmB,EACnB,MAAc,EACd,IAAa,EACb,UAAkB;IAElB,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE;QACpB,cAAc,EAAE,kBAAkB;QAClC,6BAA6B,EAAE,UAAU;QACzC,8BAA8B,EAAE,iCAAiC;QACjE,8BAA8B,EAAE,6BAA6B;KAC9D,CAAC,CAAC;IACH,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { safeCompare } from "./safeCompare.js";
2
+ export { readBody, sendJson, MAX_BODY_BYTES } from "./http.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_BODY_BYTES = exports.sendJson = exports.readBody = exports.safeCompare = void 0;
4
+ var safeCompare_js_1 = require("./safeCompare.js");
5
+ Object.defineProperty(exports, "safeCompare", { enumerable: true, get: function () { return safeCompare_js_1.safeCompare; } });
6
+ var http_js_1 = require("./http.js");
7
+ Object.defineProperty(exports, "readBody", { enumerable: true, get: function () { return http_js_1.readBody; } });
8
+ Object.defineProperty(exports, "sendJson", { enumerable: true, get: function () { return http_js_1.sendJson; } });
9
+ Object.defineProperty(exports, "MAX_BODY_BYTES", { enumerable: true, get: function () { return http_js_1.MAX_BODY_BYTES; } });
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,mDAA+C;AAAtC,6GAAA,WAAW,OAAA;AACpB,qCAA+D;AAAtD,mGAAA,QAAQ,OAAA;AAAE,mGAAA,QAAQ,OAAA;AAAE,yGAAA,cAAc,OAAA"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Constant-time string comparison to prevent timing attacks.
3
+ *
4
+ * Uses crypto.timingSafeEqual under the hood. Handles different-length
5
+ * strings safely by comparing against a same-length dummy first (so the
6
+ * comparison always runs in time proportional to `a.length`).
7
+ */
8
+ export declare function safeCompare(a: string, b: string): boolean;
9
+ //# sourceMappingURL=safeCompare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safeCompare.d.ts","sourceRoot":"","sources":["../src/safeCompare.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAYzD"}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.safeCompare = safeCompare;
4
+ const crypto_1 = require("crypto");
5
+ /**
6
+ * Constant-time string comparison to prevent timing attacks.
7
+ *
8
+ * Uses crypto.timingSafeEqual under the hood. Handles different-length
9
+ * strings safely by comparing against a same-length dummy first (so the
10
+ * comparison always runs in time proportional to `a.length`).
11
+ */
12
+ function safeCompare(a, b) {
13
+ const bufA = Buffer.from(a);
14
+ const bufB = Buffer.from(b);
15
+ if (bufA.length !== bufB.length) {
16
+ // Compare bufA against itself so the timing is consistent,
17
+ // then return false.
18
+ (0, crypto_1.timingSafeEqual)(bufA, bufA);
19
+ return false;
20
+ }
21
+ return (0, crypto_1.timingSafeEqual)(bufA, bufB);
22
+ }
23
+ //# sourceMappingURL=safeCompare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safeCompare.js","sourceRoot":"","sources":["../src/safeCompare.ts"],"names":[],"mappings":";;AASA,kCAYC;AArBD,mCAAyC;AAEzC;;;;;;GAMG;AACH,SAAgB,WAAW,CAAC,CAAS,EAAE,CAAS;IAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE5B,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,2DAA2D;QAC3D,qBAAqB;QACrB,IAAA,wBAAe,EAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAA,wBAAe,EAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@hardlydifficult/http",
3
+ "version": "1.0.0",
4
+ "main": "./dist/index.js",
5
+ "types": "./dist/index.d.ts",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "test": "vitest run",
12
+ "test:watch": "vitest",
13
+ "test:coverage": "vitest run --coverage",
14
+ "lint": "tsc --noEmit",
15
+ "clean": "rm -rf dist"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "25.2.3",
19
+ "typescript": "5.9.3",
20
+ "vitest": "4.0.18"
21
+ },
22
+ "engines": {
23
+ "node": ">=18.0.0"
24
+ }
25
+ }