@tahminator/sapling 1.5.27-beta.3d0c6593 → 1.5.28-beta.583b1882
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.cjs +65 -0
- package/dist/index.d.cts +91 -1
- package/dist/index.d.mts +91 -1
- package/dist/index.mjs +61 -1
- package/package.json +5 -1
package/dist/index.cjs
CHANGED
|
@@ -507,6 +507,60 @@ function _resolve(ctor) {
|
|
|
507
507
|
return _InjectableRegistry.get(ctor);
|
|
508
508
|
}
|
|
509
509
|
//#endregion
|
|
510
|
+
//#region src/annotation/request.ts
|
|
511
|
+
const _requestSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
512
|
+
function _getOrCreateDef(ctor, fnName) {
|
|
513
|
+
let byFn = _requestSchemaStore.get(ctor);
|
|
514
|
+
if (!byFn) {
|
|
515
|
+
byFn = /* @__PURE__ */ new Map();
|
|
516
|
+
_requestSchemaStore.set(ctor, byFn);
|
|
517
|
+
}
|
|
518
|
+
const existing = byFn.get(fnName);
|
|
519
|
+
if (existing) return existing;
|
|
520
|
+
const created = {};
|
|
521
|
+
byFn.set(fnName, created);
|
|
522
|
+
return created;
|
|
523
|
+
}
|
|
524
|
+
function _setOnce(def, key, schema, fnName) {
|
|
525
|
+
if (def[key]) throw new Error(`Duplicate request schema for "${String(key)}" on method "${fnName}"`);
|
|
526
|
+
def[key] = schema;
|
|
527
|
+
}
|
|
528
|
+
function RequestBody(schema) {
|
|
529
|
+
return (target, propertyKey) => {
|
|
530
|
+
const ctor = target.constructor;
|
|
531
|
+
const fnName = String(propertyKey);
|
|
532
|
+
_setOnce(_getOrCreateDef(ctor, fnName), "body", schema, fnName);
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function RequestParam(schema) {
|
|
536
|
+
return (target, propertyKey) => {
|
|
537
|
+
const ctor = target.constructor;
|
|
538
|
+
const fnName = String(propertyKey);
|
|
539
|
+
_setOnce(_getOrCreateDef(ctor, fnName), "param", schema, fnName);
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
function RequestQuery(schema) {
|
|
543
|
+
return (target, propertyKey) => {
|
|
544
|
+
const ctor = target.constructor;
|
|
545
|
+
const fnName = String(propertyKey);
|
|
546
|
+
_setOnce(_getOrCreateDef(ctor, fnName), "query", schema, fnName);
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
function _getRequestSchemas(ctor, fnName) {
|
|
550
|
+
return _requestSchemaStore.get(ctor)?.get(fnName);
|
|
551
|
+
}
|
|
552
|
+
function _formatIssues(issues) {
|
|
553
|
+
return issues.map((i) => {
|
|
554
|
+
const path = Array.isArray(i.path) ? i.path.map((seg) => typeof seg === "object" && seg ? String(seg.key) : String(seg)).join(".") : "";
|
|
555
|
+
return path ? `${path}: ${i.message}` : i.message;
|
|
556
|
+
}).join("; ");
|
|
557
|
+
}
|
|
558
|
+
async function _parseOrThrow(schema, input, kind) {
|
|
559
|
+
const result = await schema["~standard"].validate(input);
|
|
560
|
+
if (!result.issues) return result.value;
|
|
561
|
+
throw new ResponseStatusError(400, `${kind === "body" ? "Invalid request body" : kind === "params" ? "Invalid request params" : "Invalid request query"}: ${_formatIssues(result.issues)}`);
|
|
562
|
+
}
|
|
563
|
+
//#endregion
|
|
510
564
|
//#region src/annotation/route.ts
|
|
511
565
|
const _routeStore = /* @__PURE__ */ new WeakMap();
|
|
512
566
|
/**
|
|
@@ -613,6 +667,12 @@ function Controller({ prefix = "", deps = [] } = {}) {
|
|
|
613
667
|
if (method !== "USE") usedRoutes.add(routeKey);
|
|
614
668
|
const methodName = methodResolve[method];
|
|
615
669
|
router[methodName](fp, async (request, response, next) => {
|
|
670
|
+
const schemas = _getRequestSchemas(target, fnName);
|
|
671
|
+
if (schemas) {
|
|
672
|
+
if (schemas.body) request.body = await _parseOrThrow(schemas.body, request.body, "body");
|
|
673
|
+
if (schemas.param) request.params = await _parseOrThrow(schemas.param, request.params, "params");
|
|
674
|
+
if (schemas.query) request.query = await _parseOrThrow(schemas.query, request.query, "query");
|
|
675
|
+
}
|
|
616
676
|
const result = await fn.bind(controllerInstance)(request, response, next);
|
|
617
677
|
if (method === "USE") return;
|
|
618
678
|
if (result instanceof ResponseEntity) {
|
|
@@ -656,6 +716,9 @@ exports.PATCH = PATCH;
|
|
|
656
716
|
exports.POST = POST;
|
|
657
717
|
exports.PUT = PUT;
|
|
658
718
|
exports.RedirectView = RedirectView;
|
|
719
|
+
exports.RequestBody = RequestBody;
|
|
720
|
+
exports.RequestParam = RequestParam;
|
|
721
|
+
exports.RequestQuery = RequestQuery;
|
|
659
722
|
exports.ResponseEntity = ResponseEntity;
|
|
660
723
|
exports.ResponseEntityBuilder = ResponseEntityBuilder;
|
|
661
724
|
exports.ResponseStatusError = ResponseStatusError;
|
|
@@ -664,6 +727,8 @@ exports._ControllerRegistry = _ControllerRegistry;
|
|
|
664
727
|
exports._InjectableDeps = _InjectableDeps;
|
|
665
728
|
exports._InjectableRegistry = _InjectableRegistry;
|
|
666
729
|
exports._Route = _Route;
|
|
730
|
+
exports._getRequestSchemas = _getRequestSchemas;
|
|
667
731
|
exports._getRoutes = _getRoutes;
|
|
732
|
+
exports._parseOrThrow = _parseOrThrow;
|
|
668
733
|
exports._resolve = _resolve;
|
|
669
734
|
exports.methodResolve = methodResolve;
|
package/dist/index.d.cts
CHANGED
|
@@ -153,6 +153,96 @@ declare function _getRoutes(ctor: Function): readonly RouteDefinition[];
|
|
|
153
153
|
*/
|
|
154
154
|
declare function MiddlewareClass(...args: Parameters<typeof Controller>): ClassDecorator;
|
|
155
155
|
//#endregion
|
|
156
|
+
//#region node_modules/.pnpm/@standard-schema+spec@1.1.0/node_modules/@standard-schema/spec/dist/index.d.ts
|
|
157
|
+
/** The Standard Typed interface. This is a base type extended by other specs. */
|
|
158
|
+
interface StandardTypedV1<Input = unknown, Output = Input> {
|
|
159
|
+
/** The Standard properties. */
|
|
160
|
+
readonly "~standard": StandardTypedV1.Props<Input, Output>;
|
|
161
|
+
}
|
|
162
|
+
declare namespace StandardTypedV1 {
|
|
163
|
+
/** The Standard Typed properties interface. */
|
|
164
|
+
interface Props<Input = unknown, Output = Input> {
|
|
165
|
+
/** The version number of the standard. */
|
|
166
|
+
readonly version: 1;
|
|
167
|
+
/** The vendor name of the schema library. */
|
|
168
|
+
readonly vendor: string;
|
|
169
|
+
/** Inferred types associated with the schema. */
|
|
170
|
+
readonly types?: Types<Input, Output> | undefined;
|
|
171
|
+
}
|
|
172
|
+
/** The Standard Typed types interface. */
|
|
173
|
+
interface Types<Input = unknown, Output = Input> {
|
|
174
|
+
/** The input type of the schema. */
|
|
175
|
+
readonly input: Input;
|
|
176
|
+
/** The output type of the schema. */
|
|
177
|
+
readonly output: Output;
|
|
178
|
+
}
|
|
179
|
+
/** Infers the input type of a Standard Typed. */
|
|
180
|
+
type InferInput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["input"];
|
|
181
|
+
/** Infers the output type of a Standard Typed. */
|
|
182
|
+
type InferOutput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["output"];
|
|
183
|
+
}
|
|
184
|
+
/** The Standard Schema interface. */
|
|
185
|
+
interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
186
|
+
/** The Standard Schema properties. */
|
|
187
|
+
readonly "~standard": StandardSchemaV1.Props<Input, Output>;
|
|
188
|
+
}
|
|
189
|
+
declare namespace StandardSchemaV1 {
|
|
190
|
+
/** The Standard Schema properties interface. */
|
|
191
|
+
interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> {
|
|
192
|
+
/** Validates unknown input values. */
|
|
193
|
+
readonly validate: (value: unknown, options?: StandardSchemaV1.Options | undefined) => Result<Output> | Promise<Result<Output>>;
|
|
194
|
+
}
|
|
195
|
+
/** The result interface of the validate function. */
|
|
196
|
+
type Result<Output> = SuccessResult<Output> | FailureResult;
|
|
197
|
+
/** The result interface if validation succeeds. */
|
|
198
|
+
interface SuccessResult<Output> {
|
|
199
|
+
/** The typed output value. */
|
|
200
|
+
readonly value: Output;
|
|
201
|
+
/** A falsy value for `issues` indicates success. */
|
|
202
|
+
readonly issues?: undefined;
|
|
203
|
+
}
|
|
204
|
+
interface Options {
|
|
205
|
+
/** Explicit support for additional vendor-specific parameters, if needed. */
|
|
206
|
+
readonly libraryOptions?: Record<string, unknown> | undefined;
|
|
207
|
+
}
|
|
208
|
+
/** The result interface if validation fails. */
|
|
209
|
+
interface FailureResult {
|
|
210
|
+
/** The issues of failed validation. */
|
|
211
|
+
readonly issues: ReadonlyArray<Issue>;
|
|
212
|
+
}
|
|
213
|
+
/** The issue interface of the failure output. */
|
|
214
|
+
interface Issue {
|
|
215
|
+
/** The error message of the issue. */
|
|
216
|
+
readonly message: string;
|
|
217
|
+
/** The path of the issue, if any. */
|
|
218
|
+
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
|
|
219
|
+
}
|
|
220
|
+
/** The path segment interface of the issue. */
|
|
221
|
+
interface PathSegment {
|
|
222
|
+
/** The key representing a path segment. */
|
|
223
|
+
readonly key: PropertyKey;
|
|
224
|
+
}
|
|
225
|
+
/** The Standard types interface. */
|
|
226
|
+
interface Types<Input = unknown, Output = Input> extends StandardTypedV1.Types<Input, Output> {}
|
|
227
|
+
/** Infers the input type of a Standard. */
|
|
228
|
+
type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
|
|
229
|
+
/** Infers the output type of a Standard. */
|
|
230
|
+
type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
|
|
231
|
+
}
|
|
232
|
+
/** The Standard JSON Schema interface. */
|
|
233
|
+
//#endregion
|
|
234
|
+
//#region src/annotation/request.d.ts
|
|
235
|
+
type RequestSchemaDefinition = {
|
|
236
|
+
body?: StandardSchemaV1;
|
|
237
|
+
param?: StandardSchemaV1;
|
|
238
|
+
query?: StandardSchemaV1;
|
|
239
|
+
};
|
|
240
|
+
declare function RequestBody(schema: StandardSchemaV1): MethodDecorator;
|
|
241
|
+
declare function RequestParam(schema: StandardSchemaV1): MethodDecorator;
|
|
242
|
+
declare function RequestQuery(schema: StandardSchemaV1): MethodDecorator;
|
|
243
|
+
declare function _getRequestSchemas(ctor: Function, fnName: string): RequestSchemaDefinition | undefined;
|
|
244
|
+
declare function _parseOrThrow<TSchema extends StandardSchemaV1>(schema: TSchema, input: unknown, kind: "body" | "params" | "query"): Promise<StandardSchemaV1.InferOutput<TSchema>>;
|
|
245
|
+
//#endregion
|
|
156
246
|
//#region src/helper/redirect.d.ts
|
|
157
247
|
/**
|
|
158
248
|
* Generic HTTP redirect wrapped modeled after Spring's `RedirectView`.
|
|
@@ -418,4 +508,4 @@ declare class Sapling {
|
|
|
418
508
|
static setDeserializeFn(this: void, fn: (value: string) => any): void;
|
|
419
509
|
}
|
|
420
510
|
//#endregion
|
|
421
|
-
export { Class, Controller, DELETE, ExpressMiddlewareFn, ExpressRouterMethodKey, ExpressRouterMethods, GET, HEAD, Html404ErrorPage, HttpHeaders, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, RedirectView, ResponseEntity, ResponseEntityBuilder, ResponseStatusError, RouteDefinition, Sapling, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route, _getRoutes, _resolve, methodResolve };
|
|
511
|
+
export { Class, Controller, DELETE, ExpressMiddlewareFn, ExpressRouterMethodKey, ExpressRouterMethods, GET, HEAD, Html404ErrorPage, HttpHeaders, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, RedirectView, RequestBody, RequestParam, RequestQuery, ResponseEntity, ResponseEntityBuilder, ResponseStatusError, RouteDefinition, Sapling, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route, _getRequestSchemas, _getRoutes, _parseOrThrow, _resolve, methodResolve };
|
package/dist/index.d.mts
CHANGED
|
@@ -153,6 +153,96 @@ declare function _getRoutes(ctor: Function): readonly RouteDefinition[];
|
|
|
153
153
|
*/
|
|
154
154
|
declare function MiddlewareClass(...args: Parameters<typeof Controller>): ClassDecorator;
|
|
155
155
|
//#endregion
|
|
156
|
+
//#region node_modules/.pnpm/@standard-schema+spec@1.1.0/node_modules/@standard-schema/spec/dist/index.d.ts
|
|
157
|
+
/** The Standard Typed interface. This is a base type extended by other specs. */
|
|
158
|
+
interface StandardTypedV1<Input = unknown, Output = Input> {
|
|
159
|
+
/** The Standard properties. */
|
|
160
|
+
readonly "~standard": StandardTypedV1.Props<Input, Output>;
|
|
161
|
+
}
|
|
162
|
+
declare namespace StandardTypedV1 {
|
|
163
|
+
/** The Standard Typed properties interface. */
|
|
164
|
+
interface Props<Input = unknown, Output = Input> {
|
|
165
|
+
/** The version number of the standard. */
|
|
166
|
+
readonly version: 1;
|
|
167
|
+
/** The vendor name of the schema library. */
|
|
168
|
+
readonly vendor: string;
|
|
169
|
+
/** Inferred types associated with the schema. */
|
|
170
|
+
readonly types?: Types<Input, Output> | undefined;
|
|
171
|
+
}
|
|
172
|
+
/** The Standard Typed types interface. */
|
|
173
|
+
interface Types<Input = unknown, Output = Input> {
|
|
174
|
+
/** The input type of the schema. */
|
|
175
|
+
readonly input: Input;
|
|
176
|
+
/** The output type of the schema. */
|
|
177
|
+
readonly output: Output;
|
|
178
|
+
}
|
|
179
|
+
/** Infers the input type of a Standard Typed. */
|
|
180
|
+
type InferInput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["input"];
|
|
181
|
+
/** Infers the output type of a Standard Typed. */
|
|
182
|
+
type InferOutput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["output"];
|
|
183
|
+
}
|
|
184
|
+
/** The Standard Schema interface. */
|
|
185
|
+
interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
186
|
+
/** The Standard Schema properties. */
|
|
187
|
+
readonly "~standard": StandardSchemaV1.Props<Input, Output>;
|
|
188
|
+
}
|
|
189
|
+
declare namespace StandardSchemaV1 {
|
|
190
|
+
/** The Standard Schema properties interface. */
|
|
191
|
+
interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> {
|
|
192
|
+
/** Validates unknown input values. */
|
|
193
|
+
readonly validate: (value: unknown, options?: StandardSchemaV1.Options | undefined) => Result<Output> | Promise<Result<Output>>;
|
|
194
|
+
}
|
|
195
|
+
/** The result interface of the validate function. */
|
|
196
|
+
type Result<Output> = SuccessResult<Output> | FailureResult;
|
|
197
|
+
/** The result interface if validation succeeds. */
|
|
198
|
+
interface SuccessResult<Output> {
|
|
199
|
+
/** The typed output value. */
|
|
200
|
+
readonly value: Output;
|
|
201
|
+
/** A falsy value for `issues` indicates success. */
|
|
202
|
+
readonly issues?: undefined;
|
|
203
|
+
}
|
|
204
|
+
interface Options {
|
|
205
|
+
/** Explicit support for additional vendor-specific parameters, if needed. */
|
|
206
|
+
readonly libraryOptions?: Record<string, unknown> | undefined;
|
|
207
|
+
}
|
|
208
|
+
/** The result interface if validation fails. */
|
|
209
|
+
interface FailureResult {
|
|
210
|
+
/** The issues of failed validation. */
|
|
211
|
+
readonly issues: ReadonlyArray<Issue>;
|
|
212
|
+
}
|
|
213
|
+
/** The issue interface of the failure output. */
|
|
214
|
+
interface Issue {
|
|
215
|
+
/** The error message of the issue. */
|
|
216
|
+
readonly message: string;
|
|
217
|
+
/** The path of the issue, if any. */
|
|
218
|
+
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
|
|
219
|
+
}
|
|
220
|
+
/** The path segment interface of the issue. */
|
|
221
|
+
interface PathSegment {
|
|
222
|
+
/** The key representing a path segment. */
|
|
223
|
+
readonly key: PropertyKey;
|
|
224
|
+
}
|
|
225
|
+
/** The Standard types interface. */
|
|
226
|
+
interface Types<Input = unknown, Output = Input> extends StandardTypedV1.Types<Input, Output> {}
|
|
227
|
+
/** Infers the input type of a Standard. */
|
|
228
|
+
type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
|
|
229
|
+
/** Infers the output type of a Standard. */
|
|
230
|
+
type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
|
|
231
|
+
}
|
|
232
|
+
/** The Standard JSON Schema interface. */
|
|
233
|
+
//#endregion
|
|
234
|
+
//#region src/annotation/request.d.ts
|
|
235
|
+
type RequestSchemaDefinition = {
|
|
236
|
+
body?: StandardSchemaV1;
|
|
237
|
+
param?: StandardSchemaV1;
|
|
238
|
+
query?: StandardSchemaV1;
|
|
239
|
+
};
|
|
240
|
+
declare function RequestBody(schema: StandardSchemaV1): MethodDecorator;
|
|
241
|
+
declare function RequestParam(schema: StandardSchemaV1): MethodDecorator;
|
|
242
|
+
declare function RequestQuery(schema: StandardSchemaV1): MethodDecorator;
|
|
243
|
+
declare function _getRequestSchemas(ctor: Function, fnName: string): RequestSchemaDefinition | undefined;
|
|
244
|
+
declare function _parseOrThrow<TSchema extends StandardSchemaV1>(schema: TSchema, input: unknown, kind: "body" | "params" | "query"): Promise<StandardSchemaV1.InferOutput<TSchema>>;
|
|
245
|
+
//#endregion
|
|
156
246
|
//#region src/helper/redirect.d.ts
|
|
157
247
|
/**
|
|
158
248
|
* Generic HTTP redirect wrapped modeled after Spring's `RedirectView`.
|
|
@@ -418,4 +508,4 @@ declare class Sapling {
|
|
|
418
508
|
static setDeserializeFn(this: void, fn: (value: string) => any): void;
|
|
419
509
|
}
|
|
420
510
|
//#endregion
|
|
421
|
-
export { Class, Controller, DELETE, ExpressMiddlewareFn, ExpressRouterMethodKey, ExpressRouterMethods, GET, HEAD, Html404ErrorPage, HttpHeaders, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, RedirectView, ResponseEntity, ResponseEntityBuilder, ResponseStatusError, RouteDefinition, Sapling, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route, _getRoutes, _resolve, methodResolve };
|
|
511
|
+
export { Class, Controller, DELETE, ExpressMiddlewareFn, ExpressRouterMethodKey, ExpressRouterMethods, GET, HEAD, Html404ErrorPage, HttpHeaders, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, RedirectView, RequestBody, RequestParam, RequestQuery, ResponseEntity, ResponseEntityBuilder, ResponseStatusError, RouteDefinition, Sapling, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route, _getRequestSchemas, _getRoutes, _parseOrThrow, _resolve, methodResolve };
|
package/dist/index.mjs
CHANGED
|
@@ -483,6 +483,60 @@ function _resolve(ctor) {
|
|
|
483
483
|
return _InjectableRegistry.get(ctor);
|
|
484
484
|
}
|
|
485
485
|
//#endregion
|
|
486
|
+
//#region src/annotation/request.ts
|
|
487
|
+
const _requestSchemaStore = /* @__PURE__ */ new WeakMap();
|
|
488
|
+
function _getOrCreateDef(ctor, fnName) {
|
|
489
|
+
let byFn = _requestSchemaStore.get(ctor);
|
|
490
|
+
if (!byFn) {
|
|
491
|
+
byFn = /* @__PURE__ */ new Map();
|
|
492
|
+
_requestSchemaStore.set(ctor, byFn);
|
|
493
|
+
}
|
|
494
|
+
const existing = byFn.get(fnName);
|
|
495
|
+
if (existing) return existing;
|
|
496
|
+
const created = {};
|
|
497
|
+
byFn.set(fnName, created);
|
|
498
|
+
return created;
|
|
499
|
+
}
|
|
500
|
+
function _setOnce(def, key, schema, fnName) {
|
|
501
|
+
if (def[key]) throw new Error(`Duplicate request schema for "${String(key)}" on method "${fnName}"`);
|
|
502
|
+
def[key] = schema;
|
|
503
|
+
}
|
|
504
|
+
function RequestBody(schema) {
|
|
505
|
+
return (target, propertyKey) => {
|
|
506
|
+
const ctor = target.constructor;
|
|
507
|
+
const fnName = String(propertyKey);
|
|
508
|
+
_setOnce(_getOrCreateDef(ctor, fnName), "body", schema, fnName);
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
function RequestParam(schema) {
|
|
512
|
+
return (target, propertyKey) => {
|
|
513
|
+
const ctor = target.constructor;
|
|
514
|
+
const fnName = String(propertyKey);
|
|
515
|
+
_setOnce(_getOrCreateDef(ctor, fnName), "param", schema, fnName);
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
function RequestQuery(schema) {
|
|
519
|
+
return (target, propertyKey) => {
|
|
520
|
+
const ctor = target.constructor;
|
|
521
|
+
const fnName = String(propertyKey);
|
|
522
|
+
_setOnce(_getOrCreateDef(ctor, fnName), "query", schema, fnName);
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
function _getRequestSchemas(ctor, fnName) {
|
|
526
|
+
return _requestSchemaStore.get(ctor)?.get(fnName);
|
|
527
|
+
}
|
|
528
|
+
function _formatIssues(issues) {
|
|
529
|
+
return issues.map((i) => {
|
|
530
|
+
const path = Array.isArray(i.path) ? i.path.map((seg) => typeof seg === "object" && seg ? String(seg.key) : String(seg)).join(".") : "";
|
|
531
|
+
return path ? `${path}: ${i.message}` : i.message;
|
|
532
|
+
}).join("; ");
|
|
533
|
+
}
|
|
534
|
+
async function _parseOrThrow(schema, input, kind) {
|
|
535
|
+
const result = await schema["~standard"].validate(input);
|
|
536
|
+
if (!result.issues) return result.value;
|
|
537
|
+
throw new ResponseStatusError(400, `${kind === "body" ? "Invalid request body" : kind === "params" ? "Invalid request params" : "Invalid request query"}: ${_formatIssues(result.issues)}`);
|
|
538
|
+
}
|
|
539
|
+
//#endregion
|
|
486
540
|
//#region src/annotation/route.ts
|
|
487
541
|
const _routeStore = /* @__PURE__ */ new WeakMap();
|
|
488
542
|
/**
|
|
@@ -589,6 +643,12 @@ function Controller({ prefix = "", deps = [] } = {}) {
|
|
|
589
643
|
if (method !== "USE") usedRoutes.add(routeKey);
|
|
590
644
|
const methodName = methodResolve[method];
|
|
591
645
|
router[methodName](fp, async (request, response, next) => {
|
|
646
|
+
const schemas = _getRequestSchemas(target, fnName);
|
|
647
|
+
if (schemas) {
|
|
648
|
+
if (schemas.body) request.body = await _parseOrThrow(schemas.body, request.body, "body");
|
|
649
|
+
if (schemas.param) request.params = await _parseOrThrow(schemas.param, request.params, "params");
|
|
650
|
+
if (schemas.query) request.query = await _parseOrThrow(schemas.query, request.query, "query");
|
|
651
|
+
}
|
|
592
652
|
const result = await fn.bind(controllerInstance)(request, response, next);
|
|
593
653
|
if (method === "USE") return;
|
|
594
654
|
if (result instanceof ResponseEntity) {
|
|
@@ -618,4 +678,4 @@ function MiddlewareClass(...args) {
|
|
|
618
678
|
return Controller(...args);
|
|
619
679
|
}
|
|
620
680
|
//#endregion
|
|
621
|
-
export { Controller, DELETE, GET, HEAD, Html404ErrorPage, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, RedirectView, ResponseEntity, ResponseEntityBuilder, ResponseStatusError, Sapling, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route, _getRoutes, _resolve, methodResolve };
|
|
681
|
+
export { Controller, DELETE, GET, HEAD, Html404ErrorPage, HttpStatus, Injectable, Middleware, MiddlewareClass, OPTIONS, PATCH, POST, PUT, RedirectView, RequestBody, RequestParam, RequestQuery, ResponseEntity, ResponseEntityBuilder, ResponseStatusError, Sapling, _ControllerRegistry, _InjectableDeps, _InjectableRegistry, _Route, _getRequestSchemas, _getRoutes, _parseOrThrow, _resolve, methodResolve };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tahminator/sapling",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.28-beta.583b1882",
|
|
4
4
|
"author": "Tahmid Ahmed",
|
|
5
5
|
"description": "A library to help you write cleaner Express.js code",
|
|
6
6
|
"repository": {
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@eslint/js": "^10.0.1",
|
|
44
|
+
"@standard-schema/spec": "^1.1.0",
|
|
44
45
|
"@types/express": "^5",
|
|
45
46
|
"@types/supertest": "^7.2.0",
|
|
46
47
|
"@vitest/coverage-istanbul": "^4.1.2",
|
|
@@ -56,5 +57,8 @@
|
|
|
56
57
|
"typescript-eslint": "^8.57.2",
|
|
57
58
|
"vite-tsconfig-paths": "^6.1.1",
|
|
58
59
|
"vitest": "^4.1.2"
|
|
60
|
+
},
|
|
61
|
+
"inlinedDependencies": {
|
|
62
|
+
"@standard-schema/spec": "1.1.0"
|
|
59
63
|
}
|
|
60
64
|
}
|