yedra 0.19.3 → 0.20.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/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { y };
3
3
  export type { ConnectMiddleware } from './routing/app.js';
4
4
  export { Yedra } from './routing/app.js';
5
5
  export { BadRequestError, ConflictError, ForbiddenError, HttpError, NotFoundError, PaymentRequiredError, UnauthorizedError, } from './routing/errors.js';
6
- export { Delete, Get, Post, Put } from './routing/rest.js';
6
+ export { Delete, Get, Patch, Post, Put } from './routing/rest.js';
7
7
  export type { YedraWebSocket } from './routing/websocket.js';
8
8
  export { Ws } from './routing/websocket.js';
9
9
  export { SecurityScheme } from './util/security.js';
package/dist/index.js CHANGED
@@ -2,6 +2,6 @@ import * as y from './lib.js';
2
2
  export { y };
3
3
  export { Yedra } from './routing/app.js';
4
4
  export { BadRequestError, ConflictError, ForbiddenError, HttpError, NotFoundError, PaymentRequiredError, UnauthorizedError, } from './routing/errors.js';
5
- export { Delete, Get, Post, Put } from './routing/rest.js';
5
+ export { Delete, Get, Patch, Post, Put } from './routing/rest.js';
6
6
  export { Ws } from './routing/websocket.js';
7
7
  export { SecurityScheme } from './util/security.js';
package/dist/lib.d.ts CHANGED
@@ -10,6 +10,7 @@ export { either } from './validation/either.js';
10
10
  export { _enum as enum } from './validation/enum.js';
11
11
  export { ValidationError } from './validation/error.js';
12
12
  export { integer } from './validation/integer.js';
13
+ export { json } from './validation/json.js';
13
14
  export { array } from './validation/modifiable.js';
14
15
  export { _null as null } from './validation/null.js';
15
16
  export { number } from './validation/number.js';
package/dist/lib.js CHANGED
@@ -14,6 +14,7 @@ export { either } from './validation/either.js';
14
14
  export { _enum as enum } from './validation/enum.js';
15
15
  export { ValidationError } from './validation/error.js';
16
16
  export { integer } from './validation/integer.js';
17
+ export { json } from './validation/json.js';
17
18
  // validation
18
19
  export { array } from './validation/modifiable.js';
19
20
  export { _null as null } from './validation/null.js';
@@ -100,6 +100,7 @@ class BuiltApp {
100
100
  if (req.method !== 'GET' &&
101
101
  req.method !== 'POST' &&
102
102
  req.method !== 'PUT' &&
103
+ req.method !== 'PATCH' &&
103
104
  req.method !== 'DELETE') {
104
105
  return BuiltApp.errorResponse(405, `Method \`${req.method}\` not allowed.`);
105
106
  }
@@ -10,11 +10,6 @@ type ReqObject<Params, Query, Headers, Body> = {
10
10
  query: Query;
11
11
  headers: Headers;
12
12
  body: Body;
13
- /**
14
- * The raw request data. If the request body is
15
- * streamed, this will be an empty buffer.
16
- */
17
- raw: Buffer<ArrayBuffer>;
18
13
  };
19
14
  type ResObject<Body> = Promise<{
20
15
  status?: number;
@@ -46,7 +41,7 @@ type EndpointOptions<Params extends Record<string, Schema<unknown>>, Query exten
46
41
  do: (req: ReqObject<Typeof<ObjectSchema<Params>>, Typeof<ObjectSchema<Query>>, Typeof<ObjectSchema<Headers>>, Typeof<Req>>) => ResObject<TypeofAccepts<Res>>;
47
42
  };
48
43
  export declare abstract class RestEndpoint {
49
- abstract get method(): 'GET' | 'POST' | 'PUT' | 'DELETE';
44
+ abstract get method(): 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
50
45
  abstract handle(req: {
51
46
  url: string;
52
47
  body: Readable;
@@ -72,8 +67,8 @@ declare class ConcreteRestEndpoint<Params extends Record<string, Schema<unknown>
72
67
  private paramsSchema;
73
68
  private querySchema;
74
69
  private headersSchema;
75
- constructor(method: 'GET' | 'POST' | 'PUT' | 'DELETE', options: EndpointOptions<Params, Query, Headers, Req, Res>);
76
- get method(): 'GET' | 'POST' | 'PUT' | 'DELETE';
70
+ constructor(method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', options: EndpointOptions<Params, Query, Headers, Req, Res>);
71
+ get method(): 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
77
72
  handle(req: {
78
73
  url: string;
79
74
  body: Readable;
@@ -97,6 +92,9 @@ export declare class Post<Params extends Record<string, Schema<unknown>>, Query
97
92
  export declare class Put<Params extends Record<string, Schema<unknown>>, Query extends Record<string, Schema<unknown>>, Headers extends Record<string, Schema<unknown>>, Req extends BodyType<unknown, unknown>, Res extends BodyType<unknown, unknown>> extends ConcreteRestEndpoint<Params, Query, Headers, Req, Res> {
98
93
  constructor(options: EndpointOptions<Params, Query, Headers, Req, Res>);
99
94
  }
95
+ export declare class Patch<Params extends Record<string, Schema<unknown>>, Query extends Record<string, Schema<unknown>>, Headers extends Record<string, Schema<unknown>>, Req extends BodyType<unknown, unknown>, Res extends BodyType<unknown, unknown>> extends ConcreteRestEndpoint<Params, Query, Headers, Req, Res> {
96
+ constructor(options: EndpointOptions<Params, Query, Headers, Req, Res>);
97
+ }
100
98
  export declare class Delete<Params extends Record<string, Schema<unknown>>, Query extends Record<string, Schema<unknown>>, Headers extends Record<string, Schema<unknown>>, Res extends BodyType<unknown, unknown>> extends ConcreteRestEndpoint<Params, Query, Headers, NoneBody, Res> {
101
99
  constructor(options: Omit<EndpointOptions<Params, Query, Headers, NoneBody, Res>, 'req'>);
102
100
  }
@@ -26,15 +26,13 @@ class ConcreteRestEndpoint extends RestEndpoint {
26
26
  }
27
27
  async handle(req) {
28
28
  let parsedBody;
29
- let rawBody;
30
29
  let parsedParams;
31
30
  let parsedQuery;
32
31
  let parsedHeaders;
33
32
  const issues = [];
34
33
  try {
35
34
  const result = await this.options.req.deserialize(req.body, req.headers['content-type'] ?? 'application/octet-stream');
36
- parsedBody = result.parsed;
37
- rawBody = result.raw;
35
+ parsedBody = result;
38
36
  }
39
37
  catch (error) {
40
38
  if (error instanceof SyntaxError) {
@@ -94,8 +92,6 @@ class ConcreteRestEndpoint extends RestEndpoint {
94
92
  headers: parsedHeaders,
95
93
  // biome-ignore lint/style/noNonNullAssertion: this is required to convince TypeScript that this is initialized
96
94
  body: parsedBody,
97
- // biome-ignore lint/style/noNonNullAssertion: this is required to convince TypeScript that this is initialized
98
- raw: rawBody,
99
95
  });
100
96
  }
101
97
  isHidden() {
@@ -171,6 +167,11 @@ export class Put extends ConcreteRestEndpoint {
171
167
  super('PUT', options);
172
168
  }
173
169
  }
170
+ export class Patch extends ConcreteRestEndpoint {
171
+ constructor(options) {
172
+ super('PATCH', options);
173
+ }
174
+ }
174
175
  export class Delete extends ConcreteRestEndpoint {
175
176
  constructor(options) {
176
177
  super('DELETE', { req: none(), ...options });
@@ -17,10 +17,7 @@ export declare abstract class BodyType<Provides, Accepts> {
17
17
  * @param stream - The raw stream.
18
18
  * @param contentType - The content type.
19
19
  */
20
- abstract deserialize(stream: Readable, contentType: string): Promise<{
21
- parsed: Provides;
22
- raw: Buffer<ArrayBuffer>;
23
- }>;
20
+ abstract deserialize(stream: Readable, contentType: string): Promise<Provides>;
24
21
  /**
25
22
  * Generate OpenAPI docs for this body.
26
23
  */
@@ -3,10 +3,7 @@ import { BodyType, type Typeof, type TypeofAccepts, type TypeofProvides } from '
3
3
  declare class EitherBody<T extends [...BodyType<unknown, unknown>[]]> extends BodyType<TypeofProvides<T[number]>, TypeofAccepts<T[number]>> {
4
4
  private options;
5
5
  constructor(options: T);
6
- deserialize(stream: Readable, contentType: string): Promise<{
7
- parsed: Typeof<T[number]>;
8
- raw: Buffer<ArrayBuffer>;
9
- }>;
6
+ deserialize(stream: Readable, contentType: string): Promise<Typeof<T[number]>>;
10
7
  bodyDocs(): object;
11
8
  }
12
9
  /**
@@ -0,0 +1,12 @@
1
+ import type { Readable } from 'node:stream';
2
+ import { BodyType } from './body.js';
3
+ import type { Schema } from './schema.js';
4
+ declare class JsonBody<T> extends BodyType<T, T> {
5
+ private contentType;
6
+ private schema;
7
+ constructor(contentType: string, schema: Schema<T>);
8
+ deserialize(stream: Readable, contentType: string): Promise<T>;
9
+ bodyDocs(): object;
10
+ }
11
+ export declare const json: <T>(schema: Schema<T>, contentType: string) => JsonBody<T>;
12
+ export {};
@@ -0,0 +1,33 @@
1
+ import { readableToBuffer } from '../util/stream.js';
2
+ import { BodyType } from './body.js';
3
+ import { Issue, ValidationError } from './error.js';
4
+ class JsonBody extends BodyType {
5
+ constructor(contentType, schema) {
6
+ super();
7
+ this.contentType = contentType;
8
+ this.schema = schema;
9
+ }
10
+ async deserialize(stream, contentType) {
11
+ const buffer = await readableToBuffer(stream);
12
+ if (buffer.length === 0) {
13
+ return this.schema.parse({});
14
+ }
15
+ if (contentType !== this.contentType) {
16
+ throw new ValidationError([
17
+ new Issue([], `Expected content type \`${this.contentType}\`, but got \`${contentType}\``),
18
+ ]);
19
+ }
20
+ const obj = JSON.parse(buffer.toString('utf-8'));
21
+ return this.schema.parse(obj);
22
+ }
23
+ bodyDocs() {
24
+ return {
25
+ [this.contentType]: {
26
+ schema: this.schema.documentation(),
27
+ },
28
+ };
29
+ }
30
+ }
31
+ export const json = (schema, contentType) => {
32
+ return new JsonBody(contentType, schema);
33
+ };
@@ -1,10 +1,7 @@
1
1
  import type { Readable } from 'node:stream';
2
2
  import { BodyType } from './body.js';
3
3
  export declare class NoneBody extends BodyType<undefined, undefined> {
4
- deserialize(_stream: Readable, _contentType: string): Promise<{
5
- parsed: undefined;
6
- raw: Buffer<ArrayBuffer>;
7
- }>;
4
+ deserialize(_stream: Readable, _contentType: string): Promise<undefined>;
8
5
  bodyDocs(): object;
9
6
  }
10
7
  /**
@@ -1,7 +1,7 @@
1
1
  import { BodyType } from './body.js';
2
2
  export class NoneBody extends BodyType {
3
3
  deserialize(_stream, _contentType) {
4
- return Promise.resolve({ parsed: undefined, raw: Buffer.from('') });
4
+ return Promise.resolve(undefined);
5
5
  }
6
6
  bodyDocs() {
7
7
  // TODO
@@ -3,10 +3,7 @@ import { BodyType } from './body.js';
3
3
  declare class RawBody extends BodyType<Buffer<ArrayBuffer>, Buffer<ArrayBufferLike>> {
4
4
  private contentType;
5
5
  constructor(contentType: string);
6
- deserialize(stream: Readable, _contentType: string): Promise<{
7
- parsed: Buffer<ArrayBuffer>;
8
- raw: Buffer<ArrayBuffer>;
9
- }>;
6
+ deserialize(stream: Readable, _contentType: string): Promise<Buffer<ArrayBuffer>>;
10
7
  bodyDocs(): object;
11
8
  }
12
9
  /**
@@ -7,10 +7,7 @@ class RawBody extends BodyType {
7
7
  }
8
8
  async deserialize(stream, _contentType) {
9
9
  const buffer = await readableToBuffer(stream);
10
- return {
11
- parsed: buffer,
12
- raw: buffer,
13
- };
10
+ return buffer;
14
11
  }
15
12
  bodyDocs() {
16
13
  return {
@@ -4,10 +4,7 @@ import { BodyType } from './body.js';
4
4
  * The base class for all schemas.
5
5
  */
6
6
  export declare abstract class Schema<T> extends BodyType<T, T> {
7
- deserialize(stream: Readable, contentType: string): Promise<{
8
- parsed: T;
9
- raw: Buffer<ArrayBuffer>;
10
- }>;
7
+ deserialize(stream: Readable, contentType: string): Promise<T>;
11
8
  bodyDocs(): object;
12
9
  /**
13
10
  * Parse the object with this schema. This throws a
@@ -8,7 +8,7 @@ export class Schema extends BodyType {
8
8
  async deserialize(stream, contentType) {
9
9
  const buffer = await readableToBuffer(stream);
10
10
  if (buffer.length === 0) {
11
- return { parsed: this.parse({}), raw: buffer };
11
+ return this.parse({});
12
12
  }
13
13
  if (contentType !== 'application/json') {
14
14
  throw new ValidationError([
@@ -16,7 +16,7 @@ export class Schema extends BodyType {
16
16
  ]);
17
17
  }
18
18
  const data = JSON.parse(Buffer.from(buffer).toString('utf8'));
19
- return { parsed: this.parse(data), raw: buffer };
19
+ return this.parse(data);
20
20
  }
21
21
  bodyDocs() {
22
22
  return {
@@ -3,10 +3,7 @@ import { BodyType } from './body.js';
3
3
  declare class StreamBody extends BodyType<ReadableStream, ReadableStream> {
4
4
  private readonly contentType;
5
5
  constructor(contentType: string);
6
- deserialize(stream: Readable, _contentType: string): Promise<{
7
- parsed: ReadableStream;
8
- raw: Buffer<ArrayBuffer>;
9
- }>;
6
+ deserialize(stream: Readable, _contentType: string): Promise<ReadableStream>;
10
7
  bodyDocs(): object;
11
8
  }
12
9
  /**
@@ -5,17 +5,14 @@ class StreamBody extends BodyType {
5
5
  this.contentType = contentType;
6
6
  }
7
7
  deserialize(stream, _contentType) {
8
- return Promise.resolve({
9
- parsed: new ReadableStream({
10
- async start(controller) {
11
- for await (const chunk of stream) {
12
- controller.enqueue(chunk);
13
- }
14
- controller.close();
15
- },
16
- }),
17
- raw: Buffer.from(''),
18
- });
8
+ return Promise.resolve(new ReadableStream({
9
+ async start(controller) {
10
+ for await (const chunk of stream) {
11
+ controller.enqueue(chunk);
12
+ }
13
+ controller.close();
14
+ },
15
+ }));
19
16
  }
20
17
  bodyDocs() {
21
18
  return {
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "yedra",
3
- "version": "0.19.3",
3
+ "version": "0.20.0",
4
4
  "repository": "github:0codekit/yedra",
5
5
  "main": "dist/index.js",
6
6
  "devDependencies": {
7
- "@biomejs/biome": "^2.3.8",
8
- "@types/bun": "^1.3.4",
9
- "@types/node": "^25.0.1",
7
+ "@biomejs/biome": "^2.3.12",
8
+ "@types/bun": "^1.3.6",
9
+ "@types/node": "^25.0.10",
10
10
  "@types/uuid": "^11.0.0",
11
11
  "@types/ws": "^8.18.1",
12
12
  "typescript": "^5.9.3"
@@ -37,6 +37,6 @@
37
37
  "@opentelemetry/api": "^1.9.0",
38
38
  "mime": "^4.1.0",
39
39
  "uuid": "^13.0.0",
40
- "ws": "^8.18.3"
40
+ "ws": "^8.19.0"
41
41
  }
42
42
  }