@atscript/moost-validator 0.1.87 → 0.1.89

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.
Files changed (3) hide show
  1. package/dist/index.cjs +125 -48
  2. package/dist/index.mjs +111 -11
  3. package/package.json +3 -3
package/dist/index.cjs CHANGED
@@ -1,30 +1,7 @@
1
- "use strict";
2
- //#region rolldown:runtime
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
- get: ((k) => from[k]).bind(null, key),
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
- value: mod,
21
- enumerable: true
22
- }) : target, mod));
23
-
24
- //#endregion
25
- const __atscript_typescript_utils = __toESM(require("@atscript/typescript/utils"));
26
- const moost = __toESM(require("moost"));
27
- const __moostjs_event_http = __toESM(require("@moostjs/event-http"));
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ let _atscript_typescript_utils = require("@atscript/typescript/utils");
3
+ let moost = require("moost");
4
+ let _moostjs_event_http = require("@moostjs/event-http");
28
5
 
29
6
  //#region packages/moost-validator/src/as-coercion.pipe.ts
30
7
  /**
@@ -36,20 +13,63 @@ const __moostjs_event_http = __toESM(require("@moostjs/event-http"));
36
13
  "QUERY",
37
14
  "QUERY_ITEM"
38
15
  ];
39
- const coercionPipe = (opts) => {
16
+ /**
17
+ * **coercionPipe** ─ Creates a Moost *pipe* that coerces string-transport
18
+ * input (route params, query strings) toward the parameter's declared type
19
+ * before validation runs.
20
+ *
21
+ * For atscript-annotated types it delegates to `coerceForType` from
22
+ * `@atscript/typescript/utils` (scalars, unions, `@Query()` DTOs, arrays).
23
+ * For plain design types (`offset: number`, `flag: boolean`, `since: Date`)
24
+ * it falls back to direct constructor-based coercion — this also covers
25
+ * scalar `.as` aliases under tsc's `emitDecoratorMetadata`, where the alias
26
+ * identity collapses to `Number`.
27
+ *
28
+ * Coercion never throws and never validates — unparsable input passes
29
+ * through unchanged for {@link validatorPipe} to report. The pipe is
30
+ * registered at {@link TPipePriority.TRANSFORM}, so it composes ahead of
31
+ * `validatorPipe` (VALIDATE):
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const app = new Moost()
36
+ * app.applyGlobalPipes(coercionPipe(), validatorPipe())
37
+ *
38
+ * // KafkaOffset: @expect.int @expect.min 0 → export type KafkaOffset = number
39
+ * ‎@Get('topics/:topic')
40
+ * read(@Param('offset') offset: KafkaOffset) {
41
+ * // offset is a validated number — no manual Number.parseInt
42
+ * }
43
+ * ```
44
+ *
45
+ * @param opts {@link TCoercionOptions}.
46
+ * @returns A ready‑to‑use `PipeFn` instance.
47
+ */ const coercionPipe = (opts) => {
40
48
  const sources = new Set(opts?.sources ?? DEFAULT_SOURCES);
41
49
  return (0, moost.definePipeFn)((value, metas) => {
42
50
  const source = metas?.targetMeta?.paramSource;
43
51
  if (!source || !sources.has(source)) return value;
44
52
  const t = metas?.targetMeta?.type;
45
- if ((0, __atscript_typescript_utils.isAnnotatedType)(t)) return (0, __atscript_typescript_utils.coerceForType)(t, value);
46
- if (t === Number) return (0, __atscript_typescript_utils.coerceScalar)("number", value);
47
- if (t === Boolean) return (0, __atscript_typescript_utils.coerceScalar)("boolean", value);
53
+ if ((0, _atscript_typescript_utils.isAnnotatedType)(t)) return (0, _atscript_typescript_utils.coerceForType)(t, value);
54
+ if (t === Number) return (0, _atscript_typescript_utils.coerceScalar)("number", value);
55
+ if (t === Boolean) return (0, _atscript_typescript_utils.coerceScalar)("boolean", value);
48
56
  if (t === Date) return coerceDate(value);
49
57
  return value;
50
58
  }, moost.TPipePriority.TRANSFORM);
51
59
  };
52
- const UseCoercionPipe = (opts) => (0, moost.Pipe)(coercionPipe(opts));
60
+ /**
61
+ * Syntactic sugar decorator that applies {@link coercionPipe} to a handler or
62
+ * an entire controller class.
63
+ *
64
+ * @param opts {@link TCoercionOptions}.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * ‎@Get('items')
69
+ * ‎@UseCoercionPipe()
70
+ * list(@Query() query: SearchQuery) {}
71
+ * ```
72
+ */ const UseCoercionPipe = (opts) => (0, moost.Pipe)(coercionPipe(opts));
53
73
  function coerceDate(value) {
54
74
  if (typeof value !== "string" || value.trim().length === 0) return value;
55
75
  const parsed = new Date(value);
@@ -58,19 +78,67 @@ function coerceDate(value) {
58
78
 
59
79
  //#endregion
60
80
  //#region packages/moost-validator/src/as-validator.pipe.ts
61
- const validatorPipe = (opts) => (0, moost.definePipeFn)((value, metas, level) => {
62
- if (metas?.targetMeta?.optional && (value === undefined || value === null)) return value;
63
- if ((0, __atscript_typescript_utils.isAnnotatedType)(metas?.targetMeta?.type) && typeof metas.targetMeta.type.validator === "function") {
64
- const validator = metas.targetMeta.type.validator(opts);
65
- validator.validate(value);
66
- }
81
+ /**
82
+ * **validatorPipe** Creates a Moost *pipe* that runs atscript validation on
83
+ * handler parameters (body, params, query, etc.).
84
+ *
85
+ * The pipe inspects the runtime metadata supplied by Moost; when the target
86
+ * parameter type is an atscript‑annotated class or interface it calls
87
+ * `type.validator(opts).validate(value)` to perform synchronous validation.
88
+ *
89
+ * The pipe is registered at {@link TPipePriority.VALIDATE}, ensuring it fires
90
+ * before any transformation pipes and long before business logic executes.
91
+ *
92
+ * @param opts {@link TValidatorOptions}.
93
+ * @returns A ready‑to‑use `PipeFn` instance.
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * // for method:
98
+ * ‎@Post()
99
+ * ‎@Pipe(validatorPipe())
100
+ * async create(@Body() dto: CreateUserDto) {}
101
+ *
102
+ * // or globally:
103
+ * const app = new Moost();
104
+ * app.applyGlobalPipes(validatorPipe());
105
+ * ```
106
+ */ const validatorPipe = (opts) => (0, moost.definePipeFn)((value, metas, level) => {
107
+ if (metas?.targetMeta?.optional && (value === void 0 || value === null)) return value;
108
+ if ((0, _atscript_typescript_utils.isAnnotatedType)(metas?.targetMeta?.type) && typeof metas.targetMeta.type.validator === "function") metas.targetMeta.type.validator(opts).validate(value);
67
109
  return value;
68
110
  }, moost.TPipePriority.VALIDATE);
69
- const UseValidatorPipe = (opts) => (0, moost.Pipe)(validatorPipe(opts));
111
+ /**
112
+ * Syntactic sugar decorator that applies {@link validatorPipe} to a handler or
113
+ * an entire controller class.
114
+ *
115
+ * @param opts {@link TValidatorOptions}.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * // for method:
120
+ * ‎@Post()
121
+ * ‎@UseValidatorPipe()
122
+ * async create(@Body() dto: CreateUserDto) {}
123
+ * ```
124
+ */ const UseValidatorPipe = (opts) => (0, moost.Pipe)(validatorPipe(opts));
70
125
 
71
126
  //#endregion
72
127
  //#region packages/moost-validator/src/error-transform.ts
73
- const validationErrorTransform = () => (0, moost.defineInterceptor)({
128
+ /**
129
+ * **validationErrorTransform** ─ Moost interceptor that catches
130
+ * {@link ValidatorError}s thrown by {@link validatorPipe} (or manually) and
131
+ * converts them into a structured `HttpError(400)`
132
+ *
133
+ * Applied at {@link TInterceptorPriority.CATCH_ERROR}
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * // apply globally
138
+ * const app = new Moost();
139
+ * app.applyGlobalInterceptors(validationErrorTransform());
140
+ * ```
141
+ */ const validationErrorTransform = () => (0, moost.defineInterceptor)({
74
142
  after: transformValidationError,
75
143
  error: transformValidationError
76
144
  }, moost.TInterceptorPriority.CATCH_ERROR);
@@ -78,18 +146,27 @@ const validationErrorTransform = () => (0, moost.defineInterceptor)({
78
146
  * Internal helper that performs the actual conversion: wraps a
79
147
  * `ValidatorError` into {@link HttpError} and passes it to Moost's `reply`.
80
148
  */ function transformValidationError(error, reply) {
81
- if (error instanceof __atscript_typescript_utils.ValidatorError) reply(new __moostjs_event_http.HttpError(400, {
149
+ if (error instanceof _atscript_typescript_utils.ValidatorError) reply(new _moostjs_event_http.HttpError(400, {
82
150
  message: error.message,
83
151
  statusCode: 400,
84
152
  _body: error.errors
85
153
  }));
86
154
  }
87
- const UseValidationErrorTransform = () => (0, moost.Intercept)(validationErrorTransform());
155
+ /**
156
+ * Decorator that registers {@link validationErrorTransform} on a controller or
157
+ * route handler.
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * // for method:
162
+ * ‎@Post()
163
+ * ‎@UseValidationErrorTransform()
164
+ */ const UseValidationErrorTransform = () => (0, moost.Intercept)(validationErrorTransform());
88
165
 
89
166
  //#endregion
90
- exports.UseCoercionPipe = UseCoercionPipe
91
- exports.UseValidationErrorTransform = UseValidationErrorTransform
92
- exports.UseValidatorPipe = UseValidatorPipe
93
- exports.coercionPipe = coercionPipe
94
- exports.validationErrorTransform = validationErrorTransform
95
- exports.validatorPipe = validatorPipe
167
+ exports.UseCoercionPipe = UseCoercionPipe;
168
+ exports.UseValidationErrorTransform = UseValidationErrorTransform;
169
+ exports.UseValidatorPipe = UseValidatorPipe;
170
+ exports.coercionPipe = coercionPipe;
171
+ exports.validationErrorTransform = validationErrorTransform;
172
+ exports.validatorPipe = validatorPipe;
package/dist/index.mjs CHANGED
@@ -12,7 +12,38 @@ import { HttpError } from "@moostjs/event-http";
12
12
  "QUERY",
13
13
  "QUERY_ITEM"
14
14
  ];
15
- const coercionPipe = (opts) => {
15
+ /**
16
+ * **coercionPipe** ─ Creates a Moost *pipe* that coerces string-transport
17
+ * input (route params, query strings) toward the parameter's declared type
18
+ * before validation runs.
19
+ *
20
+ * For atscript-annotated types it delegates to `coerceForType` from
21
+ * `@atscript/typescript/utils` (scalars, unions, `@Query()` DTOs, arrays).
22
+ * For plain design types (`offset: number`, `flag: boolean`, `since: Date`)
23
+ * it falls back to direct constructor-based coercion — this also covers
24
+ * scalar `.as` aliases under tsc's `emitDecoratorMetadata`, where the alias
25
+ * identity collapses to `Number`.
26
+ *
27
+ * Coercion never throws and never validates — unparsable input passes
28
+ * through unchanged for {@link validatorPipe} to report. The pipe is
29
+ * registered at {@link TPipePriority.TRANSFORM}, so it composes ahead of
30
+ * `validatorPipe` (VALIDATE):
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const app = new Moost()
35
+ * app.applyGlobalPipes(coercionPipe(), validatorPipe())
36
+ *
37
+ * // KafkaOffset: @expect.int @expect.min 0 → export type KafkaOffset = number
38
+ * ‎@Get('topics/:topic')
39
+ * read(@Param('offset') offset: KafkaOffset) {
40
+ * // offset is a validated number — no manual Number.parseInt
41
+ * }
42
+ * ```
43
+ *
44
+ * @param opts {@link TCoercionOptions}.
45
+ * @returns A ready‑to‑use `PipeFn` instance.
46
+ */ const coercionPipe = (opts) => {
16
47
  const sources = new Set(opts?.sources ?? DEFAULT_SOURCES);
17
48
  return definePipeFn((value, metas) => {
18
49
  const source = metas?.targetMeta?.paramSource;
@@ -25,7 +56,19 @@ const coercionPipe = (opts) => {
25
56
  return value;
26
57
  }, TPipePriority.TRANSFORM);
27
58
  };
28
- const UseCoercionPipe = (opts) => Pipe(coercionPipe(opts));
59
+ /**
60
+ * Syntactic sugar decorator that applies {@link coercionPipe} to a handler or
61
+ * an entire controller class.
62
+ *
63
+ * @param opts {@link TCoercionOptions}.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * ‎@Get('items')
68
+ * ‎@UseCoercionPipe()
69
+ * list(@Query() query: SearchQuery) {}
70
+ * ```
71
+ */ const UseCoercionPipe = (opts) => Pipe(coercionPipe(opts));
29
72
  function coerceDate(value) {
30
73
  if (typeof value !== "string" || value.trim().length === 0) return value;
31
74
  const parsed = new Date(value);
@@ -34,19 +77,67 @@ function coerceDate(value) {
34
77
 
35
78
  //#endregion
36
79
  //#region packages/moost-validator/src/as-validator.pipe.ts
37
- const validatorPipe = (opts) => definePipeFn((value, metas, level) => {
38
- if (metas?.targetMeta?.optional && (value === undefined || value === null)) return value;
39
- if (isAnnotatedType(metas?.targetMeta?.type) && typeof metas.targetMeta.type.validator === "function") {
40
- const validator = metas.targetMeta.type.validator(opts);
41
- validator.validate(value);
42
- }
80
+ /**
81
+ * **validatorPipe** Creates a Moost *pipe* that runs atscript validation on
82
+ * handler parameters (body, params, query, etc.).
83
+ *
84
+ * The pipe inspects the runtime metadata supplied by Moost; when the target
85
+ * parameter type is an atscript‑annotated class or interface it calls
86
+ * `type.validator(opts).validate(value)` to perform synchronous validation.
87
+ *
88
+ * The pipe is registered at {@link TPipePriority.VALIDATE}, ensuring it fires
89
+ * before any transformation pipes and long before business logic executes.
90
+ *
91
+ * @param opts {@link TValidatorOptions}.
92
+ * @returns A ready‑to‑use `PipeFn` instance.
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * // for method:
97
+ * ‎@Post()
98
+ * ‎@Pipe(validatorPipe())
99
+ * async create(@Body() dto: CreateUserDto) {}
100
+ *
101
+ * // or globally:
102
+ * const app = new Moost();
103
+ * app.applyGlobalPipes(validatorPipe());
104
+ * ```
105
+ */ const validatorPipe = (opts) => definePipeFn((value, metas, level) => {
106
+ if (metas?.targetMeta?.optional && (value === void 0 || value === null)) return value;
107
+ if (isAnnotatedType(metas?.targetMeta?.type) && typeof metas.targetMeta.type.validator === "function") metas.targetMeta.type.validator(opts).validate(value);
43
108
  return value;
44
109
  }, TPipePriority.VALIDATE);
45
- const UseValidatorPipe = (opts) => Pipe(validatorPipe(opts));
110
+ /**
111
+ * Syntactic sugar decorator that applies {@link validatorPipe} to a handler or
112
+ * an entire controller class.
113
+ *
114
+ * @param opts {@link TValidatorOptions}.
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * // for method:
119
+ * ‎@Post()
120
+ * ‎@UseValidatorPipe()
121
+ * async create(@Body() dto: CreateUserDto) {}
122
+ * ```
123
+ */ const UseValidatorPipe = (opts) => Pipe(validatorPipe(opts));
46
124
 
47
125
  //#endregion
48
126
  //#region packages/moost-validator/src/error-transform.ts
49
- const validationErrorTransform = () => defineInterceptor({
127
+ /**
128
+ * **validationErrorTransform** ─ Moost interceptor that catches
129
+ * {@link ValidatorError}s thrown by {@link validatorPipe} (or manually) and
130
+ * converts them into a structured `HttpError(400)`
131
+ *
132
+ * Applied at {@link TInterceptorPriority.CATCH_ERROR}
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * // apply globally
137
+ * const app = new Moost();
138
+ * app.applyGlobalInterceptors(validationErrorTransform());
139
+ * ```
140
+ */ const validationErrorTransform = () => defineInterceptor({
50
141
  after: transformValidationError,
51
142
  error: transformValidationError
52
143
  }, TInterceptorPriority.CATCH_ERROR);
@@ -60,7 +151,16 @@ const validationErrorTransform = () => defineInterceptor({
60
151
  _body: error.errors
61
152
  }));
62
153
  }
63
- const UseValidationErrorTransform = () => Intercept(validationErrorTransform());
154
+ /**
155
+ * Decorator that registers {@link validationErrorTransform} on a controller or
156
+ * route handler.
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * // for method:
161
+ * ‎@Post()
162
+ * ‎@UseValidationErrorTransform()
163
+ */ const UseValidationErrorTransform = () => Intercept(validationErrorTransform());
64
164
 
65
165
  //#endregion
66
166
  export { UseCoercionPipe, UseValidationErrorTransform, UseValidatorPipe, coercionPipe, validationErrorTransform, validatorPipe };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/moost-validator",
3
- "version": "0.1.87",
3
+ "version": "0.1.89",
4
4
  "description": "Validator pipe and utils for Moost.",
5
5
  "keywords": [
6
6
  "annotations",
@@ -43,8 +43,8 @@
43
43
  "peerDependencies": {
44
44
  "@moostjs/event-http": "^0.6.35",
45
45
  "moost": "^0.6.35",
46
- "@atscript/core": "^0.1.87",
47
- "@atscript/typescript": "^0.1.87"
46
+ "@atscript/core": "^0.1.89",
47
+ "@atscript/typescript": "^0.1.89"
48
48
  },
49
49
  "scripts": {
50
50
  "pub": "pnpm publish --access public",