| /** | |
| * Attaches HTTP API metadata to Effect Schema values. | |
| * | |
| * This module is the schema-side bridge for HttpApi endpoint builders, | |
| * generated clients, and OpenAPI support. It does not define routes or perform | |
| * IO. Instead, the helpers annotate schemas so the surrounding HTTP API tooling | |
| * can choose response status codes, content types, body codecs, multipart | |
| * handling, and no-body response behavior. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| import { constVoid } from "../../Function.js"; | |
| import * as Predicate from "../../Predicate.js"; | |
| import * as Schema from "../../Schema.js"; | |
| import * as SchemaAST from "../../SchemaAST.js"; | |
| import * as SchemaTransformation from "../../SchemaTransformation.js"; | |
| import * as Stream from "../../Stream.js"; | |
| import { hasBody } from "../http/HttpMethod.js"; | |
| const statusCodeByLiteral = { | |
| Continue: 100, | |
| SwitchingProtocols: 101, | |
| Processing: 102, | |
| EarlyHints: 103, | |
| OK: 200, | |
| Ok: 200, | |
| Created: 201, | |
| Accepted: 202, | |
| NonAuthoritativeInformation: 203, | |
| NoContent: 204, | |
| ResetContent: 205, | |
| PartialContent: 206, | |
| MultiStatus: 207, | |
| AlreadyReported: 208, | |
| ImUsed: 226, | |
| MultipleChoices: 300, | |
| MovedPermanently: 301, | |
| Found: 302, | |
| SeeOther: 303, | |
| NotModified: 304, | |
| TemporaryRedirect: 307, | |
| PermanentRedirect: 308, | |
| BadRequest: 400, | |
| Unauthorized: 401, | |
| PaymentRequired: 402, | |
| Forbidden: 403, | |
| NotFound: 404, | |
| MethodNotAllowed: 405, | |
| NotAcceptable: 406, | |
| ProxyAuthenticationRequired: 407, | |
| RequestTimeout: 408, | |
| Conflict: 409, | |
| Gone: 410, | |
| LengthRequired: 411, | |
| PreconditionFailed: 412, | |
| PayloadTooLarge: 413, | |
| UriTooLong: 414, | |
| UnsupportedMediaType: 415, | |
| RangeNotSatisfiable: 416, | |
| ExpectationFailed: 417, | |
| ImATeapot: 418, | |
| MisdirectedRequest: 421, | |
| UnprocessableEntity: 422, | |
| Locked: 423, | |
| FailedDependency: 424, | |
| TooEarly: 425, | |
| UpgradeRequired: 426, | |
| PreconditionRequired: 428, | |
| TooManyRequests: 429, | |
| RequestHeaderFieldsTooLarge: 431, | |
| UnavailableForLegalReasons: 451, | |
| InternalServerError: 500, | |
| NotImplemented: 501, | |
| BadGateway: 502, | |
| ServiceUnavailable: 503, | |
| GatewayTimeout: 504, | |
| HttpVersionNotSupported: 505, | |
| VariantAlsoNegotiates: 506, | |
| InsufficientStorage: 507, | |
| LoopDetected: 508, | |
| NotExtended: 510, | |
| NetworkAuthenticationRequired: 511 | |
| }; | |
| const StreamSchemaTypeId = "~effect/httpapi/HttpApiSchema/Stream"; | |
| export function status(code) { | |
| const statusCode = typeof code === "string" ? statusCodeByLiteral[code] : code; | |
| return self => self.annotate({ | |
| httpApiStatus: statusCode | |
| }); | |
| } | |
| /** | |
| * Creates a void schema with the given HTTP status code. | |
| * This is used to represent empty responses with a specific status code. | |
| * | |
| * @see {@link NoContent} for the predefined 204 no content schema. | |
| * | |
| * @category Empty | |
| * @since 4.0.0 | |
| */ | |
| export const Empty = code => Schema.Void.pipe(status(code)); | |
| /** | |
| * Schema for empty HTTP responses with status code 204. | |
| * | |
| * @category Empty | |
| * @since 4.0.0 | |
| */ | |
| export const NoContent = /*#__PURE__*/Empty(204); | |
| /** | |
| * Schema for empty HTTP responses with status code 201. | |
| * | |
| * @category Empty | |
| * @since 4.0.0 | |
| */ | |
| export const Created = /*#__PURE__*/Empty(201); | |
| /** | |
| * Schema for empty HTTP responses with status code 202. | |
| * | |
| * @category Empty | |
| * @since 4.0.0 | |
| */ | |
| export const Accepted = /*#__PURE__*/Empty(202); | |
| /** | |
| * Marks a schema as a no-content response while preserving a decoded client value. | |
| * | |
| * **Details** | |
| * | |
| * The server encodes the response as `void`; generated clients call `decode` to | |
| * produce the schema's decoded value when the response has no body. | |
| * | |
| * @see {@link NoContent} for a void schema with the status code 204. | |
| * @see {@link Empty} for creating a void schema with a specific status code. | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export function asNoContent(options) { | |
| return self => { | |
| return Schema.Void.pipe(Schema.decodeTo(Schema.toType(self), SchemaTransformation.transform({ | |
| decode: options.decode, | |
| encode: constVoid | |
| }))); | |
| }; | |
| } | |
| const streamSchema = /*#__PURE__*/Schema.declare(Stream.isStream); | |
| /** | |
| * Creates a Server-Sent Events streaming success response schema. | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export const StreamSse = options => { | |
| const events = options.events ?? (options.data === undefined ? undefined : Schema.Struct({ | |
| id: Schema.UndefinedOr(Schema.String), | |
| event: Schema.String, | |
| data: Schema.fromJsonString(options.data) | |
| })); | |
| if (events === undefined) { | |
| throw new Error("StreamSse requires either an events schema or a data schema"); | |
| } | |
| return Schema.make(streamSchema.ast, { | |
| [StreamSchemaTypeId]: StreamSchemaTypeId, | |
| _tag: "StreamSse", | |
| mode: "sse", | |
| sseMode: options.events === undefined ? "data" : "events", | |
| contentType: options.contentType ?? defaultStreamContentType("sse"), | |
| events, | |
| error: options.error ?? Schema.Never | |
| }); | |
| }; | |
| /** | |
| * Creates a streaming `Uint8Array` success response schema. | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export const StreamUint8Array = options => Schema.make(streamSchema.ast, { | |
| [StreamSchemaTypeId]: StreamSchemaTypeId, | |
| _tag: "StreamUint8Array", | |
| mode: "uint8array", | |
| contentType: options?.contentType ?? defaultStreamContentType("uint8array") | |
| }); | |
| /** @internal */ | |
| export const isStreamSchema = u => Schema.isSchema(u) && Predicate.hasProperty(u, StreamSchemaTypeId); | |
| /** @internal */ | |
| export const isStreamSse = u => isStreamSchema(u) && u._tag === "StreamSse"; | |
| /** @internal */ | |
| export const isStreamUint8Array = u => isStreamSchema(u) && u._tag === "StreamUint8Array"; | |
| /** @internal */ | |
| export function getStreamMetadata(self) { | |
| return self._tag === "StreamSse" ? { | |
| mode: self.mode, | |
| sseMode: self.sseMode, | |
| contentType: self.contentType, | |
| events: self.events, | |
| error: self.error | |
| } : { | |
| mode: self.mode, | |
| contentType: self.contentType | |
| }; | |
| } | |
| function defaultStreamContentType(mode) { | |
| switch (mode) { | |
| case "sse": | |
| return "text/event-stream"; | |
| case "uint8array": | |
| return "application/octet-stream"; | |
| } | |
| } | |
| /** | |
| * Runtime brand key used to mark schemas as buffered multipart payloads. | |
| * | |
| * @category type IDs | |
| * @since 4.0.0 | |
| */ | |
| export const MultipartTypeId = "~effect/httpapi/HttpApiSchema/Multipart"; | |
| /** | |
| * Marks a schema as a multipart payload. | |
| * | |
| * @see {@link asMultipartStream} for a multipart stream payload. | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export function asMultipart(options) { | |
| return self => self.pipe(Schema.brand(MultipartTypeId)).annotate({ | |
| "~httpApiEncoding": { | |
| _tag: "Multipart", | |
| mode: "buffered", | |
| contentType: defaultContentType("Multipart"), | |
| limits: options | |
| } | |
| }); | |
| } | |
| /** | |
| * Runtime brand key used to mark schemas as streaming multipart payloads. | |
| * | |
| * @category type IDs | |
| * @since 4.0.0 | |
| */ | |
| export const MultipartStreamTypeId = "~effect/httpapi/HttpApiSchema/MultipartStream"; | |
| /** | |
| * Marks a schema as a multipart stream payload. | |
| * | |
| * @see {@link asMultipart} for a buffered multipart payload. | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export function asMultipartStream(options) { | |
| return self => self.pipe(Schema.brand(MultipartStreamTypeId)).annotate({ | |
| "~httpApiEncoding": { | |
| _tag: "Multipart", | |
| mode: "stream", | |
| contentType: defaultContentType("Multipart"), | |
| limits: options | |
| } | |
| }); | |
| } | |
| function asNonMultipartEncoding(self, options) { | |
| return self.annotate({ | |
| "~httpApiEncoding": { | |
| _tag: options._tag, | |
| contentType: options.contentType ?? defaultContentType(options._tag) | |
| } | |
| }); | |
| } | |
| function defaultContentType(_tag) { | |
| switch (_tag) { | |
| case "Multipart": | |
| return "multipart/form-data"; | |
| case "Json": | |
| return "application/json"; | |
| case "FormUrlEncoded": | |
| return "application/x-www-form-urlencoded"; | |
| case "Uint8Array": | |
| return "application/octet-stream"; | |
| case "Text": | |
| return "text/plain"; | |
| } | |
| } | |
| /** | |
| * Marks a schema as a JSON payload / response. | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export function asJson(options) { | |
| return self => asNonMultipartEncoding(self, { | |
| _tag: "Json", | |
| ...options | |
| }); | |
| } | |
| /** | |
| * Marks a schema as an `application/x-www-form-urlencoded` payload or response. | |
| * | |
| * **Details** | |
| * | |
| * The schema's encoded side must be a record of strings. | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export function asFormUrlEncoded(options) { | |
| return self => asNonMultipartEncoding(self, { | |
| _tag: "FormUrlEncoded", | |
| ...options | |
| }); | |
| } | |
| /** | |
| * Marks a schema as a text payload / response. | |
| * | |
| * **Details** | |
| * | |
| * The schema encoded side must be a string. | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export function asText(options) { | |
| return self => asNonMultipartEncoding(self, { | |
| _tag: "Text", | |
| ...options | |
| }); | |
| } | |
| /** | |
| * Marks a schema as a binary payload / response. | |
| * | |
| * **Details** | |
| * | |
| * The schema encoded side must be a `Uint8Array`. | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export function asUint8Array(options) { | |
| return self => asNonMultipartEncoding(self, { | |
| _tag: "Uint8Array", | |
| ...options | |
| }); | |
| } | |
| /** | |
| * Returns `true` when a schema AST represents a no-content response. | |
| * | |
| * **Details** | |
| * | |
| * The check succeeds for direct `void` schemas and schemas whose encoded or | |
| * transformation target is `void`. | |
| * | |
| * @category predicates | |
| * @since 4.0.0 | |
| */ | |
| export const isNoContent = ast => { | |
| if (SchemaAST.isVoid(ast)) return true; | |
| const encoded = SchemaAST.toEncoded(ast); | |
| if (SchemaAST.isVoid(encoded)) return true; | |
| const target = ast.encoding?.[0].to; | |
| if (target === undefined) return false; | |
| return SchemaAST.isVoid(target); | |
| }; | |
| const resolveHttpApiEncoding = /*#__PURE__*/SchemaAST.resolveAt("~httpApiEncoding"); | |
| const resolveHttpApiStatus = /*#__PURE__*/SchemaAST.resolveAt("httpApiStatus"); | |
| const defaultJsonEncoding = { | |
| _tag: "Json", | |
| contentType: "application/json" | |
| }; | |
| const defaultUrlEncodedEncoding = { | |
| _tag: "FormUrlEncoded", | |
| contentType: "application/x-www-form-urlencoded" | |
| }; | |
| function getEncoding(ast) { | |
| return resolveHttpApiEncoding(ast) ?? defaultJsonEncoding; | |
| } | |
| /** @internal */ | |
| export function getPayloadEncoding(ast, method) { | |
| const encoding = resolveHttpApiEncoding(ast); | |
| if (encoding) return encoding; | |
| return hasBody(method) ? defaultJsonEncoding : defaultUrlEncodedEncoding; | |
| } | |
| /** @internal */ | |
| export function getResponseEncoding(ast) { | |
| const out = getEncoding(ast); | |
| if (out._tag === "Multipart") { | |
| throw new Error("Multipart is not supported in response"); | |
| } | |
| return out; | |
| } | |
| /** @internal */ | |
| export function getStatusSuccess(self) { | |
| return resolveHttpApiStatus(self) ?? 200; | |
| } | |
| /** @internal */ | |
| export function getStatusStream(self) { | |
| return getStatusSuccess(self.ast); | |
| } | |
| /** @internal */ | |
| export function getStatusError(self) { | |
| return resolveHttpApiStatus(self) ?? 500; | |
| } | |
| //# sourceMappingURL=HttpApiSchema.js.map |
Xet Storage Details
- Size:
- 10.8 kB
- Xet hash:
- 90772346eb54bed1d2541e39647bee22775c79951860fd8eadf9f72dadb716b7
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.