EdgeAIG's picture
download
raw
9.72 kB
/**
* Describes HTTP request and response bodies before they reach a platform
* adapter.
*
* `HttpBody` is the shared body representation used by the HTTP modules. Each
* variant stores the payload together with metadata that can be known before
* sending it, such as `contentType` and `contentLength`. This module includes
* body constructors for common payload shapes, support for schema-encoded JSON
* bodies, streaming and file-backed bodies, and the error type used when body
* construction fails.
*
* @since 4.0.0
*/
import * as Data from "../../Data.js";
import * as Effect from "../../Effect.js";
import * as FileSystem from "../../FileSystem.js";
import { format } from "../../Formatter.js";
import * as Inspectable from "../../Inspectable.js";
import * as Predicate from "../../Predicate.js";
import * as Schema from "../../Schema.js";
import * as SchemaParser from "../../SchemaParser.js";
import * as UrlParams from "./UrlParams.js";
const TypeId = "~effect/http/HttpBody";
/**
* Returns `true` if the provided value is an `HttpBody`.
*
* @category refinements
* @since 4.0.0
*/
export const isHttpBody = u => Predicate.hasProperty(u, TypeId);
const HttpBodyErrorTypeId = "~effect/http/HttpBody/HttpBodyError";
/**
* Error produced while constructing an HTTP body from JSON or schema-encoded input.
*
* @category errors
* @since 4.0.0
*/
export class HttpBodyError extends /*#__PURE__*/Data.TaggedError("HttpBodyError") {
/**
* Marks this value as an HTTP body error for runtime guards.
*
* @since 4.0.0
*/
[HttpBodyErrorTypeId] = HttpBodyErrorTypeId;
}
class Proto {
[TypeId];
constructor() {
this[TypeId] = TypeId;
}
[Inspectable.NodeInspectSymbol]() {
return this.toJSON();
}
toString() {
return format(this, {
ignoreToString: true
});
}
}
/**
* HTTP body variant representing the absence of request content.
*
* @category models
* @since 4.0.0
*/
export class Empty extends Proto {
_tag = "Empty";
toJSON() {
return {
_id: "effect/HttpBody",
_tag: "Empty"
};
}
}
/**
* Provides the singleton empty HTTP body.
*
* **When to use**
*
* Use when you need an HTTP body value that represents no body content.
*
* @category constants
* @since 4.0.0
*/
export const empty = /*#__PURE__*/new Empty();
/**
* HTTP body variant containing an arbitrary runtime body value with optional content metadata.
*
* @category models
* @since 4.0.0
*/
export class Raw extends Proto {
_tag = "Raw";
body;
contentType;
contentLength;
constructor(body, contentType, contentLength) {
super();
this.body = body;
this.contentType = contentType;
this.contentLength = contentLength;
}
toJSON() {
return {
_id: "effect/HttpBody",
_tag: "Raw",
body: this.body,
contentType: this.contentType,
contentLength: this.contentLength
};
}
}
/**
* Creates a raw HTTP body from an arbitrary value and optional `contentType` and `contentLength` metadata.
*
* @category constructors
* @since 4.0.0
*/
export const raw = (body, options) => new Raw(body, options?.contentType, options?.contentLength);
/**
* HTTP body variant backed by a `Uint8Array`.
*
* **Details**
*
* It stores the bytes, content type, and byte length.
*
* @category models
* @since 4.0.0
*/
export class Uint8Array extends Proto {
_tag = "Uint8Array";
body;
contentType;
contentLength;
constructor(body, contentType, contentLength) {
super();
this.body = body;
this.contentType = contentType;
this.contentLength = contentLength;
}
toJSON() {
const toString = this.contentType.startsWith("text/") || this.contentType.endsWith("json");
return {
_id: "effect/HttpBody",
_tag: "Uint8Array",
body: toString ? new TextDecoder().decode(this.body) : `Uint8Array(${this.body.length})`,
contentType: this.contentType,
contentLength: this.contentLength
};
}
}
/**
* Creates a byte-array HTTP body.
*
* **Details**
*
* The content type defaults to `application/octet-stream`, and the content length is the byte array length.
*
* @category constructors
* @since 4.0.0
*/
export const uint8Array = (body, contentType) => new Uint8Array(body, contentType ?? "application/octet-stream", body.length);
const encoder = /*#__PURE__*/new TextEncoder();
/**
* Creates a UTF-8 encoded text HTTP body.
*
* **Details**
*
* The content type defaults to `text/plain`.
*
* @category constructors
* @since 4.0.0
*/
export const text = (body, contentType) => uint8Array(encoder.encode(body), contentType ?? "text/plain");
/**
* Creates a JSON HTTP body using `JSON.stringify`, throwing if serialization fails.
*
* **Details**
*
* The content type defaults to `application/json`.
*
* @category constructors
* @since 4.0.0
*/
export const jsonUnsafe = (body, contentType) => text(JSON.stringify(body), contentType ?? "application/json");
/**
* Creates a JSON HTTP body in an `Effect`.
*
* **Details**
*
* `JSON.stringify` failures are captured as `HttpBodyError` values, and the content type defaults to `application/json`.
*
* @category constructors
* @since 4.0.0
*/
export const json = (body, contentType) => Effect.try({
try: () => text(JSON.stringify(body), contentType ?? "application/json"),
catch: cause => new HttpBodyError({
reason: {
_tag: "JsonError"
},
cause
})
});
/**
* Creates a JSON body constructor that first encodes values with the schema's JSON codec.
*
* **Details**
*
* Schema encoding issues and JSON serialization failures are returned as `HttpBodyError` values.
*
* @category constructors
* @since 4.0.0
*/
export const jsonSchema = (schema, options) => {
const encode = SchemaParser.encodeUnknownEffect(Schema.toCodecJson(schema));
return (body, contentType) => encode(body, options).pipe(Effect.mapError(issue => new HttpBodyError({
reason: {
_tag: "SchemaError",
issue
},
cause: issue
})), Effect.flatMap(body => json(body, contentType)));
};
/**
* Creates an `application/x-www-form-urlencoded` HTTP body from `UrlParams`.
*
* @category constructors
* @since 4.0.0
*/
export const urlParams = (urlParams, contentType) => text(UrlParams.toString(urlParams), contentType ?? "application/x-www-form-urlencoded");
/**
* HTTP body variant backed by Web `FormData`.
*
* **Details**
*
* The content type and content length are left unset so the runtime can supply multipart boundaries.
*
* @category models
* @since 4.0.0
*/
export class FormData extends Proto {
_tag = "FormData";
contentType = undefined;
contentLength = undefined;
formData;
constructor(formData) {
super();
this.formData = formData;
}
toJSON() {
return {
_id: "effect/HttpBody",
_tag: "FormData",
formData: this.formData
};
}
}
/**
* Wraps a Web `FormData` value as an HTTP body.
*
* @category constructors
* @since 4.0.0
*/
export const formData = body => new FormData(body);
const appendFormDataValue = (formData, key, value) => {
if (value == null) {
return;
}
if (typeof value === "object") {
formData.append(key, value);
return;
}
formData.append(key, String(value));
};
/**
* Creates a `FormData` HTTP body from a record.
*
* **Details**
*
* Array fields append each item under the same key; primitive values are stringified, `File` and `Blob` values are appended directly, and nullish values are skipped.
*
* @category constructors
* @since 4.0.0
*/
export const formDataRecord = entries => {
const data = new globalThis.FormData();
for (const [key, value] of Object.entries(entries)) {
if (Array.isArray(value)) {
for (const item of value) {
appendFormDataValue(data, key, item);
}
} else {
appendFormDataValue(data, key, value);
}
}
return formData(data);
};
/**
* HTTP body variant backed by a stream of `Uint8Array` chunks.
*
* @category models
* @since 4.0.0
*/
export class Stream extends Proto {
_tag = "Stream";
stream;
contentType;
contentLength;
constructor(stream, contentType, contentLength) {
super();
this.stream = stream;
this.contentType = contentType;
this.contentLength = contentLength;
}
toJSON() {
return {
_id: "effect/HttpBody",
_tag: "Stream",
contentType: this.contentType,
contentLength: this.contentLength
};
}
}
/**
* Creates a streaming HTTP body from a stream of byte chunks.
*
* **Details**
*
* The content type defaults to `application/octet-stream`; content length is optional.
*
* @category constructors
* @since 4.0.0
*/
export const stream = (body, contentType, contentLength) => new Stream(body, contentType ?? "application/octet-stream", contentLength);
/**
* Creates a streaming HTTP body for a file path.
*
* **Details**
*
* The effect requires `FileSystem`, stats the file to set the content length, and can fail with `PlatformError`.
*
* @category constructors
* @since 4.0.0
*/
export const file = (path, options) => Effect.flatMap(FileSystem.FileSystem, fs => Effect.map(fs.stat(path), info => stream(fs.stream(path, options), options?.contentType, Number(info.size))));
/**
* Creates a streaming HTTP body for a file path using already-known file information.
*
* **Details**
*
* The effect requires `FileSystem`, uses the provided file size as the content length, and can fail with `PlatformError`.
*
* @category constructors
* @since 4.0.0
*/
export const fileFromInfo = (path, info, options) => Effect.map(FileSystem.FileSystem, fs => stream(fs.stream(path, options), options?.contentType, Number(info.size)));
//# sourceMappingURL=HttpBody.js.map

Xet Storage Details

Size:
9.72 kB
·
Xet hash:
b39718a8806d9225084fa978061595c5bd33338245060312263bc17ae19f318a

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.