@faasjs/http 8.0.0-beta.0 → 8.0.0-beta.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.
package/README.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @faasjs/http
2
2
 
3
+ FaasJS's http plugin.
4
+
5
+ [![License: MIT](https://img.shields.io/npm/l/@faasjs/http.svg)](https://github.com/faasjs/faasjs/blob/main/packages/http/LICENSE)
6
+ [![NPM Version](https://img.shields.io/npm/v/@faasjs/http.svg)](https://www.npmjs.com/package/@faasjs/http)
7
+
8
+ ## Install
9
+
10
+ ```sh
11
+ npm install @faasjs/http
12
+ ```
13
+
3
14
  ## Functions
4
15
 
5
16
  - [useHttp](functions/useHttp.md)
package/dist/index.cjs CHANGED
@@ -202,12 +202,25 @@ var HttpError = class _HttpError extends Error {
202
202
  }
203
203
  };
204
204
  var Name = "http";
205
+ function stringToStream(text) {
206
+ return new ReadableStream({
207
+ start(controller) {
208
+ try {
209
+ const encoder = new TextEncoder();
210
+ controller.enqueue(encoder.encode(text));
211
+ controller.close();
212
+ } catch (error) {
213
+ controller.error(error);
214
+ }
215
+ }
216
+ });
217
+ }
205
218
  function deepClone(obj) {
206
219
  if (obj === null || typeof obj !== "object") return obj;
207
220
  if (Array.isArray(obj)) return JSON.parse(JSON.stringify(obj));
208
221
  const clone = {};
209
222
  for (const key in obj) {
210
- if (!obj.hasOwnProperty(key)) continue;
223
+ if (!Object.hasOwn(obj, key)) continue;
211
224
  if (typeof obj[key] === "function") {
212
225
  clone[key] = obj[key];
213
226
  continue;
@@ -216,6 +229,30 @@ function deepClone(obj) {
216
229
  }
217
230
  return clone;
218
231
  }
232
+ function createCompressedStream(body, encoding) {
233
+ const compressStream = encoding === "br" ? zlib.createBrotliCompress() : encoding === "gzip" ? zlib.createGzip() : zlib.createDeflate();
234
+ return new ReadableStream({
235
+ async start(controller) {
236
+ try {
237
+ const compressed = await new Promise((resolve, reject) => {
238
+ const chunks = [];
239
+ compressStream.on("data", (chunk) => chunks.push(chunk));
240
+ compressStream.on("end", () => resolve(Buffer.concat(chunks)));
241
+ compressStream.on("error", reject);
242
+ compressStream.write(Buffer.from(body));
243
+ compressStream.end();
244
+ });
245
+ const chunkSize = 16 * 1024;
246
+ for (let i = 0; i < compressed.length; i += chunkSize) {
247
+ controller.enqueue(compressed.subarray(i, i + chunkSize));
248
+ }
249
+ controller.close();
250
+ } catch (error) {
251
+ controller.error(error);
252
+ }
253
+ }
254
+ });
255
+ }
219
256
  var Http = class {
220
257
  type = "http";
221
258
  name = Name;
@@ -301,6 +338,9 @@ var Http = class {
301
338
  data.response = error;
302
339
  }
303
340
  this.session.update();
341
+ if (this.response.body && !data.response) {
342
+ data.response = this.response.body;
343
+ }
304
344
  if (data.response)
305
345
  if (data.response instanceof Error || data.response.constructor?.name === "Error") {
306
346
  data.logger.error(data.response);
@@ -315,12 +355,17 @@ var Http = class {
315
355
  }
316
356
  } else if (Object.prototype.toString.call(data.response) === "[object Object]" && data.response.statusCode && data.response.headers)
317
357
  this.response = data.response;
318
- else this.response.body = JSON.stringify({ data: data.response });
358
+ else if (data.response instanceof ReadableStream)
359
+ this.response.body = data.response;
360
+ else
361
+ this.response.body = JSON.stringify({
362
+ data: data.response === void 0 ? null : data.response
363
+ });
319
364
  if (!this.response.statusCode)
320
- this.response.statusCode = this.response.body ? 200 : 201;
365
+ this.response.statusCode = data.response ? 200 : 204;
321
366
  this.response.headers = Object.assign(
322
367
  {
323
- "content-type": "application/json; charset=utf-8",
368
+ "content-type": this.response.body instanceof ReadableStream ? "text/plain; charset=utf-8" : "application/json; charset=utf-8",
324
369
  "cache-control": "no-cache, no-store",
325
370
  "x-faasjs-request-id": data.context.request_id
326
371
  },
@@ -330,26 +375,29 @@ var Http = class {
330
375
  data.response = Object.assign({}, data.response, this.response);
331
376
  const originBody = data.response.body;
332
377
  data.response.originBody = originBody;
333
- if (originBody && !data.response.isBase64Encoded && typeof originBody !== "string")
378
+ if (data.response.body instanceof ReadableStream) {
379
+ data.response.isBase64Encoded = true;
380
+ return;
381
+ }
382
+ if (originBody === void 0 && data.response.statusCode === 204) {
383
+ return;
384
+ }
385
+ if (originBody && typeof originBody !== "string")
334
386
  data.response.body = JSON.stringify(originBody);
335
- if (!data.response.body || data.response.isBase64Encoded || typeof data.response.body !== "string" || data.response.body.length < 1024)
387
+ else if (originBody === void 0)
388
+ data.response.body = JSON.stringify({ data: null });
389
+ if (!data.response.body || typeof data.response.body !== "string" || data.response.body.length < 1024) {
390
+ data.response.body = stringToStream(data.response.body);
336
391
  return;
392
+ }
337
393
  const acceptEncoding = this.headers["accept-encoding"] || this.headers["Accept-Encoding"];
338
394
  if (!acceptEncoding || !/(br|gzip|deflate)/.test(acceptEncoding)) return;
339
395
  try {
340
- if (acceptEncoding.includes("br")) {
341
- data.response.headers["Content-Encoding"] = "br";
342
- data.response.body = zlib.brotliCompressSync(originBody).toString("base64");
343
- } else if (acceptEncoding.includes("gzip")) {
344
- data.response.headers["Content-Encoding"] = "gzip";
345
- data.response.body = zlib.gzipSync(originBody).toString("base64");
346
- } else if (acceptEncoding.includes("deflate")) {
347
- data.response.headers["Content-Encoding"] = "deflate";
348
- data.response.body = zlib.deflateSync(originBody).toString("base64");
349
- } else throw Error("No matched compression.");
350
- data.response.isBase64Encoded = true;
396
+ const encoding = acceptEncoding.includes("br") ? "br" : acceptEncoding.includes("gzip") ? "gzip" : "deflate";
397
+ data.response.headers["Content-Encoding"] = encoding;
398
+ data.response.body = createCompressedStream(originBody, encoding);
351
399
  } catch (error) {
352
- console.error(error);
400
+ data.logger.error("Compression failed: %s", error.message);
353
401
  data.response.body = originBody;
354
402
  delete data.response.headers["Content-Encoding"];
355
403
  }
package/dist/index.d.ts CHANGED
@@ -103,7 +103,7 @@ type Response = {
103
103
  headers?: {
104
104
  [key: string]: string;
105
105
  };
106
- body?: string;
106
+ body?: string | ReadableStream;
107
107
  message?: string;
108
108
  };
109
109
  declare class HttpError extends Error {
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { brotliCompressSync, gzipSync, deflateSync } from 'zlib';
1
+ import { createBrotliCompress, createGzip, createDeflate } from 'zlib';
2
2
  import { deepMerge } from '@faasjs/deep_merge';
3
3
  import { usePlugin, useFunc } from '@faasjs/func';
4
4
  import { randomBytes, pbkdf2Sync, createCipheriv, createHmac, createDecipheriv } from 'crypto';
@@ -200,12 +200,25 @@ var HttpError = class _HttpError extends Error {
200
200
  }
201
201
  };
202
202
  var Name = "http";
203
+ function stringToStream(text) {
204
+ return new ReadableStream({
205
+ start(controller) {
206
+ try {
207
+ const encoder = new TextEncoder();
208
+ controller.enqueue(encoder.encode(text));
209
+ controller.close();
210
+ } catch (error) {
211
+ controller.error(error);
212
+ }
213
+ }
214
+ });
215
+ }
203
216
  function deepClone(obj) {
204
217
  if (obj === null || typeof obj !== "object") return obj;
205
218
  if (Array.isArray(obj)) return JSON.parse(JSON.stringify(obj));
206
219
  const clone = {};
207
220
  for (const key in obj) {
208
- if (!obj.hasOwnProperty(key)) continue;
221
+ if (!Object.hasOwn(obj, key)) continue;
209
222
  if (typeof obj[key] === "function") {
210
223
  clone[key] = obj[key];
211
224
  continue;
@@ -214,6 +227,30 @@ function deepClone(obj) {
214
227
  }
215
228
  return clone;
216
229
  }
230
+ function createCompressedStream(body, encoding) {
231
+ const compressStream = encoding === "br" ? createBrotliCompress() : encoding === "gzip" ? createGzip() : createDeflate();
232
+ return new ReadableStream({
233
+ async start(controller) {
234
+ try {
235
+ const compressed = await new Promise((resolve, reject) => {
236
+ const chunks = [];
237
+ compressStream.on("data", (chunk) => chunks.push(chunk));
238
+ compressStream.on("end", () => resolve(Buffer.concat(chunks)));
239
+ compressStream.on("error", reject);
240
+ compressStream.write(Buffer.from(body));
241
+ compressStream.end();
242
+ });
243
+ const chunkSize = 16 * 1024;
244
+ for (let i = 0; i < compressed.length; i += chunkSize) {
245
+ controller.enqueue(compressed.subarray(i, i + chunkSize));
246
+ }
247
+ controller.close();
248
+ } catch (error) {
249
+ controller.error(error);
250
+ }
251
+ }
252
+ });
253
+ }
217
254
  var Http = class {
218
255
  type = "http";
219
256
  name = Name;
@@ -299,6 +336,9 @@ var Http = class {
299
336
  data.response = error;
300
337
  }
301
338
  this.session.update();
339
+ if (this.response.body && !data.response) {
340
+ data.response = this.response.body;
341
+ }
302
342
  if (data.response)
303
343
  if (data.response instanceof Error || data.response.constructor?.name === "Error") {
304
344
  data.logger.error(data.response);
@@ -313,12 +353,17 @@ var Http = class {
313
353
  }
314
354
  } else if (Object.prototype.toString.call(data.response) === "[object Object]" && data.response.statusCode && data.response.headers)
315
355
  this.response = data.response;
316
- else this.response.body = JSON.stringify({ data: data.response });
356
+ else if (data.response instanceof ReadableStream)
357
+ this.response.body = data.response;
358
+ else
359
+ this.response.body = JSON.stringify({
360
+ data: data.response === void 0 ? null : data.response
361
+ });
317
362
  if (!this.response.statusCode)
318
- this.response.statusCode = this.response.body ? 200 : 201;
363
+ this.response.statusCode = data.response ? 200 : 204;
319
364
  this.response.headers = Object.assign(
320
365
  {
321
- "content-type": "application/json; charset=utf-8",
366
+ "content-type": this.response.body instanceof ReadableStream ? "text/plain; charset=utf-8" : "application/json; charset=utf-8",
322
367
  "cache-control": "no-cache, no-store",
323
368
  "x-faasjs-request-id": data.context.request_id
324
369
  },
@@ -328,26 +373,29 @@ var Http = class {
328
373
  data.response = Object.assign({}, data.response, this.response);
329
374
  const originBody = data.response.body;
330
375
  data.response.originBody = originBody;
331
- if (originBody && !data.response.isBase64Encoded && typeof originBody !== "string")
376
+ if (data.response.body instanceof ReadableStream) {
377
+ data.response.isBase64Encoded = true;
378
+ return;
379
+ }
380
+ if (originBody === void 0 && data.response.statusCode === 204) {
381
+ return;
382
+ }
383
+ if (originBody && typeof originBody !== "string")
332
384
  data.response.body = JSON.stringify(originBody);
333
- if (!data.response.body || data.response.isBase64Encoded || typeof data.response.body !== "string" || data.response.body.length < 1024)
385
+ else if (originBody === void 0)
386
+ data.response.body = JSON.stringify({ data: null });
387
+ if (!data.response.body || typeof data.response.body !== "string" || data.response.body.length < 1024) {
388
+ data.response.body = stringToStream(data.response.body);
334
389
  return;
390
+ }
335
391
  const acceptEncoding = this.headers["accept-encoding"] || this.headers["Accept-Encoding"];
336
392
  if (!acceptEncoding || !/(br|gzip|deflate)/.test(acceptEncoding)) return;
337
393
  try {
338
- if (acceptEncoding.includes("br")) {
339
- data.response.headers["Content-Encoding"] = "br";
340
- data.response.body = brotliCompressSync(originBody).toString("base64");
341
- } else if (acceptEncoding.includes("gzip")) {
342
- data.response.headers["Content-Encoding"] = "gzip";
343
- data.response.body = gzipSync(originBody).toString("base64");
344
- } else if (acceptEncoding.includes("deflate")) {
345
- data.response.headers["Content-Encoding"] = "deflate";
346
- data.response.body = deflateSync(originBody).toString("base64");
347
- } else throw Error("No matched compression.");
348
- data.response.isBase64Encoded = true;
394
+ const encoding = acceptEncoding.includes("br") ? "br" : acceptEncoding.includes("gzip") ? "gzip" : "deflate";
395
+ data.response.headers["Content-Encoding"] = encoding;
396
+ data.response.body = createCompressedStream(originBody, encoding);
349
397
  } catch (error) {
350
- console.error(error);
398
+ data.logger.error("Compression failed: %s", error.message);
351
399
  data.response.body = originBody;
352
400
  delete data.response.headers["Content-Encoding"];
353
401
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faasjs/http",
3
- "version": "v8.0.0-beta.0",
3
+ "version": "v8.0.0-beta.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -30,12 +30,12 @@
30
30
  "dist"
31
31
  ],
32
32
  "peerDependencies": {
33
- "@faasjs/func": ">=v8.0.0-beta.0",
34
- "@faasjs/logger": ">=v8.0.0-beta.0"
33
+ "@faasjs/func": ">=v8.0.0-beta.2",
34
+ "@faasjs/logger": ">=v8.0.0-beta.2"
35
35
  },
36
36
  "devDependencies": {
37
- "@faasjs/func": ">=v8.0.0-beta.0",
38
- "@faasjs/logger": ">=v8.0.0-beta.0"
37
+ "@faasjs/func": ">=v8.0.0-beta.2",
38
+ "@faasjs/logger": ">=v8.0.0-beta.2"
39
39
  },
40
40
  "engines": {
41
41
  "node": ">=24.0.0",