@epsilon-asi/foundation 0.0.1-rc3 → 0.0.2
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/build/decorators/environment/index.d.ts +164 -0
- package/build/decorators/environment/index.js +223 -0
- package/build/decorators/factory/examples/authorization.d.ts +48 -0
- package/build/decorators/factory/examples/authorization.js +113 -0
- package/build/decorators/factory/examples/runExamples.js +4 -0
- package/build/decorators/factory/examples/validation.d.ts +49 -0
- package/build/decorators/factory/examples/validation.js +109 -0
- package/build/decorators/factory/type-tests/decoratorRegistry.type-test.d.ts +1 -0
- package/build/decorators/factory/type-tests/decoratorRegistry.type-test.js +458 -0
- package/build/decorators/requests/mapping.d.ts +234 -0
- package/build/decorators/requests/mapping.js +460 -0
- package/build/decorators/validation/ValidatableClass.d.ts +0 -0
- package/build/decorators/validation/ValidatableClass.js +1 -0
- package/build/decorators/validation/validatable.d.ts +2 -0
- package/build/decorators/validation/validatable.js +8 -0
- package/package.json +5 -2
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import type { ClassDecoratorDefinition, Constructor, DecorationRecord, DecoratorRegistry, DecoratorSchema, PropertyDecoratorDefinition } from '../factory';
|
|
2
|
+
export type RequestSection = 'params' | 'body' | 'query' | 'headers';
|
|
3
|
+
export type AutoMapSection = 'param' | 'body' | 'query' | 'header';
|
|
4
|
+
export type DataType = 'string' | 'number' | 'boolean' | 'date';
|
|
5
|
+
export type RequestPrimitive = string | number | boolean | Date;
|
|
6
|
+
export interface RequestContents<Body extends Record<string, unknown> | undefined = Record<string, unknown>, Query extends Record<string, unknown> | undefined = Record<string, unknown>, Params extends Record<string, unknown> | undefined = Record<string, unknown>, Headers extends Record<string, unknown> | undefined = Record<string, unknown>> {
|
|
7
|
+
body?: Body;
|
|
8
|
+
query?: Query;
|
|
9
|
+
params?: Params;
|
|
10
|
+
headers?: Headers;
|
|
11
|
+
}
|
|
12
|
+
export interface RequestObjectMetadata {
|
|
13
|
+
description?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface RequestFieldBaseMetadata {
|
|
16
|
+
section: RequestSection;
|
|
17
|
+
/**
|
|
18
|
+
* Source key inside request.params/request.body/request.query/request.headers.
|
|
19
|
+
* Defaults to the decorated property name.
|
|
20
|
+
*/
|
|
21
|
+
name?: string;
|
|
22
|
+
required?: boolean;
|
|
23
|
+
description?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface StringRequestFieldMetadata extends RequestFieldBaseMetadata {
|
|
26
|
+
type: 'string';
|
|
27
|
+
default?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface NumberRequestFieldMetadata extends RequestFieldBaseMetadata {
|
|
30
|
+
type: 'number';
|
|
31
|
+
default?: number;
|
|
32
|
+
integer?: boolean;
|
|
33
|
+
min?: number;
|
|
34
|
+
max?: number;
|
|
35
|
+
}
|
|
36
|
+
export interface BooleanRequestFieldMetadata extends RequestFieldBaseMetadata {
|
|
37
|
+
type: 'boolean';
|
|
38
|
+
default?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface DateRequestFieldMetadata extends RequestFieldBaseMetadata {
|
|
41
|
+
type: 'date';
|
|
42
|
+
default?: Date;
|
|
43
|
+
}
|
|
44
|
+
export interface UnknownRequestFieldMetadata extends RequestFieldBaseMetadata {
|
|
45
|
+
type?: undefined;
|
|
46
|
+
default?: unknown;
|
|
47
|
+
}
|
|
48
|
+
export type RequestFieldMetadata = StringRequestFieldMetadata | NumberRequestFieldMetadata | BooleanRequestFieldMetadata | DateRequestFieldMetadata | UnknownRequestFieldMetadata;
|
|
49
|
+
export type AutoMapOpts = {
|
|
50
|
+
/** Exactly one of these should be true. */
|
|
51
|
+
param?: boolean;
|
|
52
|
+
body?: boolean;
|
|
53
|
+
query?: boolean;
|
|
54
|
+
header?: boolean;
|
|
55
|
+
name?: string;
|
|
56
|
+
key?: string;
|
|
57
|
+
type?: DataType;
|
|
58
|
+
required?: boolean;
|
|
59
|
+
default?: unknown;
|
|
60
|
+
description?: string;
|
|
61
|
+
};
|
|
62
|
+
export interface RequestMappingSchema extends DecoratorSchema {
|
|
63
|
+
class: {
|
|
64
|
+
requestObject: ClassDecoratorDefinition<RequestObjectMetadata | undefined>;
|
|
65
|
+
};
|
|
66
|
+
property: {
|
|
67
|
+
requestField: PropertyDecoratorDefinition<RequestFieldMetadata, unknown>;
|
|
68
|
+
};
|
|
69
|
+
method: {};
|
|
70
|
+
accessor: {};
|
|
71
|
+
}
|
|
72
|
+
export type FieldDecoratorFor<TValue> = <TThis, TActual extends TValue | undefined>(value: undefined, context: ClassFieldDecoratorContext<TThis, TActual>) => void;
|
|
73
|
+
export type AnyFieldDecorator = <TThis, TActual>(value: undefined, context: ClassFieldDecoratorContext<TThis, TActual>) => void;
|
|
74
|
+
export type StringFieldOptions = Omit<StringRequestFieldMetadata, 'section' | 'type'>;
|
|
75
|
+
export type NumberFieldOptions = Omit<NumberRequestFieldMetadata, 'section' | 'type'>;
|
|
76
|
+
export type BooleanFieldOptions = Omit<BooleanRequestFieldMetadata, 'section' | 'type'>;
|
|
77
|
+
export type DateFieldOptions = Omit<DateRequestFieldMetadata, 'section' | 'type'>;
|
|
78
|
+
export type UnknownFieldOptions = Omit<UnknownRequestFieldMetadata, 'section' | 'type'>;
|
|
79
|
+
export interface RequestSectionDecorator<TDefaultValue> {
|
|
80
|
+
(): FieldDecoratorFor<TDefaultValue>;
|
|
81
|
+
(name: string): FieldDecoratorFor<TDefaultValue>;
|
|
82
|
+
(options: StringFieldOptions & {
|
|
83
|
+
type: 'string';
|
|
84
|
+
}): FieldDecoratorFor<string>;
|
|
85
|
+
(options: NumberFieldOptions & {
|
|
86
|
+
type: 'number';
|
|
87
|
+
}): FieldDecoratorFor<number>;
|
|
88
|
+
(options: BooleanFieldOptions & {
|
|
89
|
+
type: 'boolean';
|
|
90
|
+
}): FieldDecoratorFor<boolean>;
|
|
91
|
+
(options: DateFieldOptions & {
|
|
92
|
+
type: 'date';
|
|
93
|
+
}): FieldDecoratorFor<Date>;
|
|
94
|
+
(options: UnknownFieldOptions): FieldDecoratorFor<TDefaultValue>;
|
|
95
|
+
(name: string, options: StringFieldOptions & {
|
|
96
|
+
type: 'string';
|
|
97
|
+
}): FieldDecoratorFor<string>;
|
|
98
|
+
(name: string, options: NumberFieldOptions & {
|
|
99
|
+
type: 'number';
|
|
100
|
+
}): FieldDecoratorFor<number>;
|
|
101
|
+
(name: string, options: BooleanFieldOptions & {
|
|
102
|
+
type: 'boolean';
|
|
103
|
+
}): FieldDecoratorFor<boolean>;
|
|
104
|
+
(name: string, options: DateFieldOptions & {
|
|
105
|
+
type: 'date';
|
|
106
|
+
}): FieldDecoratorFor<Date>;
|
|
107
|
+
(name: string, options: UnknownFieldOptions): FieldDecoratorFor<TDefaultValue>;
|
|
108
|
+
string(name?: string, options?: StringFieldOptions): FieldDecoratorFor<string>;
|
|
109
|
+
number(name?: string, options?: NumberFieldOptions): FieldDecoratorFor<number>;
|
|
110
|
+
boolean(name?: string, options?: BooleanFieldOptions): FieldDecoratorFor<boolean>;
|
|
111
|
+
date(name?: string, options?: DateFieldOptions): FieldDecoratorFor<Date>;
|
|
112
|
+
}
|
|
113
|
+
export interface RequestMappingDefinition {
|
|
114
|
+
propertyName: string;
|
|
115
|
+
sourceKey: string;
|
|
116
|
+
section: RequestSection;
|
|
117
|
+
type?: DataType;
|
|
118
|
+
required: boolean;
|
|
119
|
+
static: boolean;
|
|
120
|
+
private: boolean;
|
|
121
|
+
metadata: RequestFieldMetadata;
|
|
122
|
+
record: DecorationRecord;
|
|
123
|
+
}
|
|
124
|
+
export interface LegacyMetaEntry {
|
|
125
|
+
key: string;
|
|
126
|
+
sourceKey: string;
|
|
127
|
+
dataType?: DataType;
|
|
128
|
+
required?: boolean;
|
|
129
|
+
}
|
|
130
|
+
export interface LegacyMetaContainer {
|
|
131
|
+
params: Record<string, LegacyMetaEntry>;
|
|
132
|
+
body: Record<string, LegacyMetaEntry>;
|
|
133
|
+
query: Record<string, LegacyMetaEntry>;
|
|
134
|
+
headers: Record<string, LegacyMetaEntry>;
|
|
135
|
+
}
|
|
136
|
+
export interface RequestMapperOptions {
|
|
137
|
+
namespace?: string;
|
|
138
|
+
allowPrivateMembers?: boolean;
|
|
139
|
+
}
|
|
140
|
+
export interface RequestReadOptions {
|
|
141
|
+
includeInherited?: boolean;
|
|
142
|
+
}
|
|
143
|
+
export interface AssignRequestOptions extends RequestReadOptions {
|
|
144
|
+
/**
|
|
145
|
+
* If true, throws when @Param/@Body/@Query/@Header is applied to a static field.
|
|
146
|
+
* Defaults to true because request objects are normally instance DTOs.
|
|
147
|
+
*/
|
|
148
|
+
rejectStatic?: boolean;
|
|
149
|
+
}
|
|
150
|
+
export interface ToRequestContentsOptions extends RequestReadOptions {
|
|
151
|
+
includeUndefined?: boolean;
|
|
152
|
+
/**
|
|
153
|
+
* By default values are preserved. Use 'http' to stringify params/query/headers.
|
|
154
|
+
*/
|
|
155
|
+
serialize?: 'preserve' | 'http';
|
|
156
|
+
rejectStatic?: boolean;
|
|
157
|
+
}
|
|
158
|
+
export declare class RequestMappingError extends Error {
|
|
159
|
+
readonly section?: RequestSection;
|
|
160
|
+
readonly propertyName?: string;
|
|
161
|
+
readonly sourceKey?: string;
|
|
162
|
+
constructor(message: string, details?: {
|
|
163
|
+
section?: RequestSection;
|
|
164
|
+
propertyName?: string;
|
|
165
|
+
sourceKey?: string;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
type ConcreteConstructor<T> = new (...args: never[]) => T;
|
|
169
|
+
type RequestMapperApi = {
|
|
170
|
+
registry: DecoratorRegistry<RequestMappingSchema>;
|
|
171
|
+
RequestObject(metadata?: RequestObjectMetadata): <TClass extends Constructor>(value: TClass, context: ClassDecoratorContext<TClass>) => void;
|
|
172
|
+
AutoMap(opts: AutoMapOpts & {
|
|
173
|
+
type: 'string';
|
|
174
|
+
}): FieldDecoratorFor<string>;
|
|
175
|
+
AutoMap(opts: AutoMapOpts & {
|
|
176
|
+
type: 'number';
|
|
177
|
+
}): FieldDecoratorFor<number>;
|
|
178
|
+
AutoMap(opts: AutoMapOpts & {
|
|
179
|
+
type: 'boolean';
|
|
180
|
+
}): FieldDecoratorFor<boolean>;
|
|
181
|
+
AutoMap(opts: AutoMapOpts & {
|
|
182
|
+
type: 'date';
|
|
183
|
+
}): FieldDecoratorFor<Date>;
|
|
184
|
+
AutoMap(opts: AutoMapOpts): AnyFieldDecorator;
|
|
185
|
+
Param: RequestSectionDecorator<string>;
|
|
186
|
+
Query: RequestSectionDecorator<string>;
|
|
187
|
+
Header: RequestSectionDecorator<string>;
|
|
188
|
+
Body: RequestSectionDecorator<unknown>;
|
|
189
|
+
getRequestMappingDefinitions(target: Constructor | object, options?: RequestReadOptions): readonly RequestMappingDefinition[];
|
|
190
|
+
getAutoMapMeta(target: Constructor | object, options?: RequestReadOptions): LegacyMetaContainer;
|
|
191
|
+
assignRequestContents<T extends object>(target: T, request: RequestContents, options?: AssignRequestOptions): T;
|
|
192
|
+
requestContentsToObject<T extends object>(ctor: ConcreteConstructor<T>, request: RequestContents, options?: AssignRequestOptions): T;
|
|
193
|
+
objectToRequestContents<T extends object>(source: T, options?: ToRequestContentsOptions): RequestContents;
|
|
194
|
+
};
|
|
195
|
+
export declare function createRequestMapper(options?: RequestMapperOptions): RequestMapperApi;
|
|
196
|
+
export declare const requestMappingRegistry: DecoratorRegistry<RequestMappingSchema>;
|
|
197
|
+
export declare const RequestObject: (metadata?: RequestObjectMetadata) => <TClass extends Constructor>(value: TClass, context: ClassDecoratorContext<TClass>) => void;
|
|
198
|
+
export declare const AutoMap: {
|
|
199
|
+
(opts: AutoMapOpts & {
|
|
200
|
+
type: "string";
|
|
201
|
+
}): FieldDecoratorFor<string>;
|
|
202
|
+
(opts: AutoMapOpts & {
|
|
203
|
+
type: "number";
|
|
204
|
+
}): FieldDecoratorFor<number>;
|
|
205
|
+
(opts: AutoMapOpts & {
|
|
206
|
+
type: "boolean";
|
|
207
|
+
}): FieldDecoratorFor<boolean>;
|
|
208
|
+
(opts: AutoMapOpts & {
|
|
209
|
+
type: "date";
|
|
210
|
+
}): FieldDecoratorFor<Date>;
|
|
211
|
+
(opts: AutoMapOpts): AnyFieldDecorator;
|
|
212
|
+
};
|
|
213
|
+
export declare const Param: RequestSectionDecorator<string>;
|
|
214
|
+
export declare const Query: RequestSectionDecorator<string>;
|
|
215
|
+
export declare const Header: RequestSectionDecorator<string>;
|
|
216
|
+
export declare const Body: RequestSectionDecorator<unknown>;
|
|
217
|
+
export declare const getRequestMappingDefinitions: (target: Constructor | object, options?: RequestReadOptions) => readonly RequestMappingDefinition[];
|
|
218
|
+
export declare const getAutoMapMeta: (target: Constructor | object, options?: RequestReadOptions) => LegacyMetaContainer;
|
|
219
|
+
export declare const assignRequestContents: <T extends object>(target: T, request: RequestContents, options?: AssignRequestOptions) => T;
|
|
220
|
+
export declare const requestContentsToObject: <T extends object>(ctor: ConcreteConstructor<T>, request: RequestContents, options?: AssignRequestOptions) => T;
|
|
221
|
+
export declare const objectToRequestContents: <T extends object>(source: T, options?: ToRequestContentsOptions) => RequestContents;
|
|
222
|
+
export declare class MappedRequest<TRequest extends RequestContents = RequestContents> {
|
|
223
|
+
defaultConstruct(request: TRequest): void;
|
|
224
|
+
construct(_request: TRequest): void;
|
|
225
|
+
destruct(options?: ToRequestContentsOptions): TRequest;
|
|
226
|
+
toRequestContents(options?: ToRequestContentsOptions): TRequest;
|
|
227
|
+
validateWith<TError>(validate: (value: this) => readonly TError[] | Promise<readonly TError[]>): Promise<readonly TError[]>;
|
|
228
|
+
constructor(request?: TRequest);
|
|
229
|
+
static create<T extends object>(this: ConcreteConstructor<T>, data: Partial<CleanRequestObject<T>>): T;
|
|
230
|
+
static fromRequest<T extends object>(this: ConcreteConstructor<T>, request: RequestContents, options?: AssignRequestOptions): T;
|
|
231
|
+
}
|
|
232
|
+
export type CleanRequestObject<T> = Omit<T, 'defaultConstruct' | 'construct' | 'destruct' | 'toRequestContents' | 'validateWith'>;
|
|
233
|
+
export declare function CleanRequestObject<T extends object>(request: T): CleanRequestObject<T>;
|
|
234
|
+
export default MappedRequest;
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.MappedRequest = exports.objectToRequestContents = exports.requestContentsToObject = exports.assignRequestContents = exports.getAutoMapMeta = exports.getRequestMappingDefinitions = exports.Body = exports.Header = exports.Query = exports.Param = exports.AutoMap = exports.RequestObject = exports.requestMappingRegistry = exports.RequestMappingError = void 0;
|
|
15
|
+
exports.createRequestMapper = createRequestMapper;
|
|
16
|
+
exports.CleanRequestObject = CleanRequestObject;
|
|
17
|
+
const factory_1 = require("../factory");
|
|
18
|
+
class RequestMappingError extends Error {
|
|
19
|
+
constructor(message, details = {}) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = 'RequestMappingError';
|
|
22
|
+
this.section = details.section;
|
|
23
|
+
this.propertyName = details.propertyName;
|
|
24
|
+
this.sourceKey = details.sourceKey;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.RequestMappingError = RequestMappingError;
|
|
28
|
+
const pendingRequestSymbol = Symbol('requestMapping.pendingRequest');
|
|
29
|
+
function setPendingRequest(target, request) {
|
|
30
|
+
Object.defineProperty(target, pendingRequestSymbol, {
|
|
31
|
+
configurable: true,
|
|
32
|
+
enumerable: false,
|
|
33
|
+
writable: true,
|
|
34
|
+
value: request,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function getPendingRequest(target) {
|
|
38
|
+
return target[pendingRequestSymbol];
|
|
39
|
+
}
|
|
40
|
+
function createRequestMapper(options = {}) {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
const registry = (0, factory_1.createDecoratorRegistry)({
|
|
43
|
+
namespace: (_a = options.namespace) !== null && _a !== void 0 ? _a : 'request-mapping',
|
|
44
|
+
allowPrivateMembers: (_b = options.allowPrivateMembers) !== null && _b !== void 0 ? _b : false,
|
|
45
|
+
});
|
|
46
|
+
const RequestObject = registry.class('requestObject');
|
|
47
|
+
const Scoped = registry.for();
|
|
48
|
+
const RawRequestField = Scoped.property('requestField');
|
|
49
|
+
function makeFieldDecorator(metadata) {
|
|
50
|
+
const recordDecorator = RawRequestField(metadata);
|
|
51
|
+
return function requestFieldDecorator(value, context) {
|
|
52
|
+
var _a;
|
|
53
|
+
recordDecorator(value, context);
|
|
54
|
+
if (context.static || typeof context.name !== 'string') {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
const propertyName = context.name;
|
|
58
|
+
const sourceKey = (_a = metadata.name) !== null && _a !== void 0 ? _a : propertyName;
|
|
59
|
+
return function requestFieldInitializer(initialValue) {
|
|
60
|
+
var _a;
|
|
61
|
+
if (typeof this !== 'object' || this === null)
|
|
62
|
+
return initialValue;
|
|
63
|
+
const request = getPendingRequest(this);
|
|
64
|
+
if (request === undefined)
|
|
65
|
+
return initialValue;
|
|
66
|
+
const definition = createInitializerDefinition({
|
|
67
|
+
propertyName,
|
|
68
|
+
sourceKey,
|
|
69
|
+
metadata,
|
|
70
|
+
static: context.static,
|
|
71
|
+
private: context.private,
|
|
72
|
+
});
|
|
73
|
+
const rawValue = (_a = getSectionSource(request, metadata.section)) === null || _a === void 0 ? void 0 : _a[sourceKey];
|
|
74
|
+
return coerceRequestValue(rawValue, definition);
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function AutoMap(opts) {
|
|
79
|
+
var _a;
|
|
80
|
+
const section = sectionFromAutoMapOptions(opts);
|
|
81
|
+
const metadata = normalizeMetadata(section, undefined, (_a = opts.name) !== null && _a !== void 0 ? _a : opts.key, opts);
|
|
82
|
+
return makeFieldDecorator(metadata);
|
|
83
|
+
}
|
|
84
|
+
const Param = createSectionDecorator('params', 'string', makeFieldDecorator);
|
|
85
|
+
const Query = createSectionDecorator('query', 'string', makeFieldDecorator);
|
|
86
|
+
const Header = createSectionDecorator('headers', 'string', makeFieldDecorator);
|
|
87
|
+
const Body = createSectionDecorator('body', undefined, makeFieldDecorator);
|
|
88
|
+
function getRequestMappingDefinitions(target, readOptions = {}) {
|
|
89
|
+
const snapshot = registry.read(target, {
|
|
90
|
+
includeInherited: readOptions.includeInherited,
|
|
91
|
+
});
|
|
92
|
+
return Object.freeze(snapshot.propertyRecords('requestField').map((record) => {
|
|
93
|
+
var _a, _b;
|
|
94
|
+
if (record.memberName === undefined) {
|
|
95
|
+
throw new RequestMappingError('Request mapping record is missing memberName.');
|
|
96
|
+
}
|
|
97
|
+
if (typeof record.memberName === 'symbol') {
|
|
98
|
+
throw new RequestMappingError(`Request mapping does not support symbol member keys: ${String(record.memberName)}.`);
|
|
99
|
+
}
|
|
100
|
+
const metadata = record.metadata;
|
|
101
|
+
const propertyName = record.memberName;
|
|
102
|
+
const sourceKey = (_a = metadata.name) !== null && _a !== void 0 ? _a : propertyName;
|
|
103
|
+
return Object.freeze({
|
|
104
|
+
propertyName,
|
|
105
|
+
sourceKey,
|
|
106
|
+
section: metadata.section,
|
|
107
|
+
type: metadata.type,
|
|
108
|
+
required: (_b = metadata.required) !== null && _b !== void 0 ? _b : false,
|
|
109
|
+
static: record.static,
|
|
110
|
+
private: record.private,
|
|
111
|
+
metadata,
|
|
112
|
+
record,
|
|
113
|
+
});
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
function getAutoMapMeta(target, readOptions = {}) {
|
|
117
|
+
const result = {
|
|
118
|
+
params: {},
|
|
119
|
+
body: {},
|
|
120
|
+
query: {},
|
|
121
|
+
headers: {},
|
|
122
|
+
};
|
|
123
|
+
for (const definition of getRequestMappingDefinitions(target, readOptions)) {
|
|
124
|
+
const entry = {
|
|
125
|
+
key: definition.propertyName,
|
|
126
|
+
sourceKey: definition.sourceKey,
|
|
127
|
+
dataType: definition.type,
|
|
128
|
+
required: definition.required,
|
|
129
|
+
};
|
|
130
|
+
result[definition.section][definition.propertyName] = entry;
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
function assignRequestContents(target, request, assignOptions = {}) {
|
|
135
|
+
var _a;
|
|
136
|
+
const rejectStatic = (_a = assignOptions.rejectStatic) !== null && _a !== void 0 ? _a : true;
|
|
137
|
+
for (const definition of getRequestMappingDefinitions(target, assignOptions)) {
|
|
138
|
+
assertUsableDefinition(definition, rejectStatic);
|
|
139
|
+
const sectionSource = getSectionSource(request, definition.section);
|
|
140
|
+
const rawValue = sectionSource === null || sectionSource === void 0 ? void 0 : sectionSource[definition.sourceKey];
|
|
141
|
+
const value = coerceRequestValue(rawValue, definition);
|
|
142
|
+
setObjectProperty(target, definition.propertyName, value);
|
|
143
|
+
}
|
|
144
|
+
return target;
|
|
145
|
+
}
|
|
146
|
+
function requestContentsToObject(ctor, request, assignOptions = {}) {
|
|
147
|
+
const instance = new ctor();
|
|
148
|
+
return assignRequestContents(instance, request, assignOptions);
|
|
149
|
+
}
|
|
150
|
+
function objectToRequestContents(source, toOptions = {}) {
|
|
151
|
+
var _a, _b, _c;
|
|
152
|
+
const rejectStatic = (_a = toOptions.rejectStatic) !== null && _a !== void 0 ? _a : true;
|
|
153
|
+
const includeUndefined = (_b = toOptions.includeUndefined) !== null && _b !== void 0 ? _b : true;
|
|
154
|
+
const serialize = (_c = toOptions.serialize) !== null && _c !== void 0 ? _c : 'preserve';
|
|
155
|
+
const out = {
|
|
156
|
+
params: {},
|
|
157
|
+
body: {},
|
|
158
|
+
query: {},
|
|
159
|
+
headers: {},
|
|
160
|
+
};
|
|
161
|
+
for (const definition of getRequestMappingDefinitions(source, toOptions)) {
|
|
162
|
+
assertUsableDefinition(definition, rejectStatic);
|
|
163
|
+
const value = getObjectProperty(source, definition.propertyName);
|
|
164
|
+
if (value === undefined && !includeUndefined)
|
|
165
|
+
continue;
|
|
166
|
+
const section = ensureSection(out, definition.section);
|
|
167
|
+
section[definition.sourceKey] = serializeValueForRequest(value, definition, serialize);
|
|
168
|
+
}
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
return Object.freeze({
|
|
172
|
+
registry,
|
|
173
|
+
RequestObject,
|
|
174
|
+
AutoMap: AutoMap,
|
|
175
|
+
Param,
|
|
176
|
+
Query,
|
|
177
|
+
Header,
|
|
178
|
+
Body,
|
|
179
|
+
getRequestMappingDefinitions,
|
|
180
|
+
getAutoMapMeta,
|
|
181
|
+
assignRequestContents,
|
|
182
|
+
requestContentsToObject,
|
|
183
|
+
objectToRequestContents,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
function createSectionDecorator(section, defaultType, makeFieldDecorator) {
|
|
187
|
+
const decorator = ((first, second) => {
|
|
188
|
+
const metadata = normalizeSectionDecoratorMetadata(section, defaultType, first, second);
|
|
189
|
+
return makeFieldDecorator(metadata);
|
|
190
|
+
});
|
|
191
|
+
decorator.string = (name, options = {}) => makeFieldDecorator(normalizeMetadata(section, 'string', name, options));
|
|
192
|
+
decorator.number = (name, options = {}) => makeFieldDecorator(normalizeMetadata(section, 'number', name, options));
|
|
193
|
+
decorator.boolean = (name, options = {}) => makeFieldDecorator(normalizeMetadata(section, 'boolean', name, options));
|
|
194
|
+
decorator.date = (name, options = {}) => makeFieldDecorator(normalizeMetadata(section, 'date', name, options));
|
|
195
|
+
return decorator;
|
|
196
|
+
}
|
|
197
|
+
function normalizeSectionDecoratorMetadata(section, defaultType, first, second) {
|
|
198
|
+
if (typeof first === 'string') {
|
|
199
|
+
return normalizeMetadata(section, defaultType, first, second !== null && second !== void 0 ? second : {});
|
|
200
|
+
}
|
|
201
|
+
return normalizeMetadata(section, defaultType, first === null || first === void 0 ? void 0 : first.name, first !== null && first !== void 0 ? first : {});
|
|
202
|
+
}
|
|
203
|
+
function normalizeMetadata(section, defaultType, explicitName, options) {
|
|
204
|
+
const _a = (options !== null && options !== void 0 ? options : {}), { name: _name, key: _key, param: _param, body: _body, query: _query, header: _header, type: optionType } = _a, rest = __rest(_a, ["name", "key", "param", "body", "query", "header", "type"]);
|
|
205
|
+
return Object.assign(Object.assign({}, rest), { section, name: explicitName, type: optionType !== null && optionType !== void 0 ? optionType : defaultType });
|
|
206
|
+
}
|
|
207
|
+
function sectionFromAutoMapOptions(opts) {
|
|
208
|
+
const sections = [];
|
|
209
|
+
if (opts.param)
|
|
210
|
+
sections.push('params');
|
|
211
|
+
if (opts.body)
|
|
212
|
+
sections.push('body');
|
|
213
|
+
if (opts.query)
|
|
214
|
+
sections.push('query');
|
|
215
|
+
if (opts.header)
|
|
216
|
+
sections.push('headers');
|
|
217
|
+
if (sections.length !== 1) {
|
|
218
|
+
throw new RequestMappingError('@AutoMap requires exactly one of { param, body, query, header }.');
|
|
219
|
+
}
|
|
220
|
+
return sections[0];
|
|
221
|
+
}
|
|
222
|
+
function createInitializerDefinition(args) {
|
|
223
|
+
var _a;
|
|
224
|
+
return {
|
|
225
|
+
propertyName: args.propertyName,
|
|
226
|
+
sourceKey: args.sourceKey,
|
|
227
|
+
section: args.metadata.section,
|
|
228
|
+
type: args.metadata.type,
|
|
229
|
+
required: (_a = args.metadata.required) !== null && _a !== void 0 ? _a : false,
|
|
230
|
+
static: args.static,
|
|
231
|
+
private: args.private,
|
|
232
|
+
metadata: args.metadata,
|
|
233
|
+
record: undefined,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function assertUsableDefinition(definition, rejectStatic) {
|
|
237
|
+
if (definition.private) {
|
|
238
|
+
throw new RequestMappingError(`Request mapping field "${definition.propertyName}" cannot be private.`, definition);
|
|
239
|
+
}
|
|
240
|
+
if (rejectStatic && definition.static) {
|
|
241
|
+
throw new RequestMappingError(`Request mapping field "${definition.propertyName}" must be an instance field, not static.`, definition);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function getSectionSource(request, section) {
|
|
245
|
+
return request[section];
|
|
246
|
+
}
|
|
247
|
+
function ensureSection(request, section) {
|
|
248
|
+
const existing = request[section];
|
|
249
|
+
if (existing !== undefined)
|
|
250
|
+
return existing;
|
|
251
|
+
const created = {};
|
|
252
|
+
request[section] = created;
|
|
253
|
+
return created;
|
|
254
|
+
}
|
|
255
|
+
function coerceRequestValue(rawValue, definition) {
|
|
256
|
+
const metadata = definition.metadata;
|
|
257
|
+
const missing = rawValue === undefined || rawValue === null;
|
|
258
|
+
if (missing) {
|
|
259
|
+
if (Object.prototype.hasOwnProperty.call(metadata, 'default')) {
|
|
260
|
+
return cloneSimpleDefault(metadata.default);
|
|
261
|
+
}
|
|
262
|
+
if (definition.required) {
|
|
263
|
+
throw new RequestMappingError(`Missing required ${definition.section} value "${definition.sourceKey}" for property "${definition.propertyName}".`, definition);
|
|
264
|
+
}
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
switch (metadata.type) {
|
|
268
|
+
case undefined:
|
|
269
|
+
return rawValue;
|
|
270
|
+
case 'string':
|
|
271
|
+
return coerceString(rawValue);
|
|
272
|
+
case 'number':
|
|
273
|
+
return coerceNumber(rawValue, definition);
|
|
274
|
+
case 'boolean':
|
|
275
|
+
return coerceBoolean(rawValue, definition);
|
|
276
|
+
case 'date':
|
|
277
|
+
return coerceDate(rawValue, definition);
|
|
278
|
+
default:
|
|
279
|
+
return assertNever(metadata);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function coerceString(value) {
|
|
283
|
+
if (typeof value === 'string')
|
|
284
|
+
return value;
|
|
285
|
+
if (value instanceof Date)
|
|
286
|
+
return value.toISOString();
|
|
287
|
+
return String(value);
|
|
288
|
+
}
|
|
289
|
+
function coerceNumber(value, definition) {
|
|
290
|
+
if (typeof value === 'number') {
|
|
291
|
+
validateNumber(value, definition);
|
|
292
|
+
return value;
|
|
293
|
+
}
|
|
294
|
+
if (typeof value === 'boolean') {
|
|
295
|
+
const numberValue = value ? 1 : 0;
|
|
296
|
+
validateNumber(numberValue, definition);
|
|
297
|
+
return numberValue;
|
|
298
|
+
}
|
|
299
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
300
|
+
throw new RequestMappingError(`Expected ${definition.section} value "${definition.sourceKey}" to be a number-compatible value.`, definition);
|
|
301
|
+
}
|
|
302
|
+
const numberValue = Number(value);
|
|
303
|
+
validateNumber(numberValue, definition);
|
|
304
|
+
return numberValue;
|
|
305
|
+
}
|
|
306
|
+
function validateNumber(value, definition) {
|
|
307
|
+
const metadata = definition.metadata;
|
|
308
|
+
if (!Number.isFinite(value)) {
|
|
309
|
+
throw new RequestMappingError(`Expected ${definition.section} value "${definition.sourceKey}" to be a finite number.`, definition);
|
|
310
|
+
}
|
|
311
|
+
if (metadata.integer === true && !Number.isInteger(value)) {
|
|
312
|
+
throw new RequestMappingError(`Expected ${definition.section} value "${definition.sourceKey}" to be an integer.`, definition);
|
|
313
|
+
}
|
|
314
|
+
if (metadata.min !== undefined && value < metadata.min) {
|
|
315
|
+
throw new RequestMappingError(`Expected ${definition.section} value "${definition.sourceKey}" to be >= ${metadata.min}.`, definition);
|
|
316
|
+
}
|
|
317
|
+
if (metadata.max !== undefined && value > metadata.max) {
|
|
318
|
+
throw new RequestMappingError(`Expected ${definition.section} value "${definition.sourceKey}" to be <= ${metadata.max}.`, definition);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function coerceBoolean(value, definition) {
|
|
322
|
+
if (typeof value === 'boolean')
|
|
323
|
+
return value;
|
|
324
|
+
if (typeof value === 'number') {
|
|
325
|
+
if (value === 1)
|
|
326
|
+
return true;
|
|
327
|
+
if (value === 0)
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
if (typeof value === 'string') {
|
|
331
|
+
const normalized = value.trim().toLowerCase();
|
|
332
|
+
if (['true', '1', 'yes', 'y', 'on'].includes(normalized))
|
|
333
|
+
return true;
|
|
334
|
+
if (['false', '0', 'no', 'n', 'off'].includes(normalized))
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
throw new RequestMappingError(`Expected ${definition.section} value "${definition.sourceKey}" to be boolean-compatible.`, definition);
|
|
338
|
+
}
|
|
339
|
+
function coerceDate(value, definition) {
|
|
340
|
+
if (value instanceof Date) {
|
|
341
|
+
assertValidDate(value, definition);
|
|
342
|
+
return value;
|
|
343
|
+
}
|
|
344
|
+
let date;
|
|
345
|
+
if (typeof value === 'number') {
|
|
346
|
+
date = new Date(value);
|
|
347
|
+
}
|
|
348
|
+
else if (typeof value === 'string') {
|
|
349
|
+
const trimmed = value.trim();
|
|
350
|
+
const numeric = trimmed !== '' ? Number(trimmed) : Number.NaN;
|
|
351
|
+
date = Number.isFinite(numeric) ? new Date(numeric) : new Date(trimmed);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
throw new RequestMappingError(`Expected ${definition.section} value "${definition.sourceKey}" to be date-compatible.`, definition);
|
|
355
|
+
}
|
|
356
|
+
assertValidDate(date, definition);
|
|
357
|
+
return date;
|
|
358
|
+
}
|
|
359
|
+
function assertValidDate(value, definition) {
|
|
360
|
+
if (Number.isNaN(value.getTime())) {
|
|
361
|
+
throw new RequestMappingError(`Expected ${definition.section} value "${definition.sourceKey}" to be a valid date.`, definition);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function serializeValueForRequest(value, definition, mode) {
|
|
365
|
+
if (mode === 'preserve')
|
|
366
|
+
return value;
|
|
367
|
+
if (value === undefined || value === null)
|
|
368
|
+
return value;
|
|
369
|
+
if (definition.section === 'body') {
|
|
370
|
+
return value;
|
|
371
|
+
}
|
|
372
|
+
if (value instanceof Date) {
|
|
373
|
+
return value.toISOString();
|
|
374
|
+
}
|
|
375
|
+
return String(value);
|
|
376
|
+
}
|
|
377
|
+
function setObjectProperty(target, propertyName, value) {
|
|
378
|
+
target[propertyName] = value;
|
|
379
|
+
}
|
|
380
|
+
function getObjectProperty(target, propertyName) {
|
|
381
|
+
return target[propertyName];
|
|
382
|
+
}
|
|
383
|
+
function cloneSimpleDefault(value) {
|
|
384
|
+
if (value instanceof Date)
|
|
385
|
+
return new Date(value.getTime());
|
|
386
|
+
if (Array.isArray(value))
|
|
387
|
+
return value.slice();
|
|
388
|
+
if (isPlainObject(value))
|
|
389
|
+
return Object.assign({}, value);
|
|
390
|
+
return value;
|
|
391
|
+
}
|
|
392
|
+
function isPlainObject(value) {
|
|
393
|
+
return typeof value === 'object'
|
|
394
|
+
&& value !== null
|
|
395
|
+
&& Object.getPrototypeOf(value) === Object.prototype;
|
|
396
|
+
}
|
|
397
|
+
function assertNever(value) {
|
|
398
|
+
throw new RequestMappingError(`Unsupported request mapping metadata: ${JSON.stringify(value)}.`);
|
|
399
|
+
}
|
|
400
|
+
const defaultMapper = createRequestMapper();
|
|
401
|
+
exports.requestMappingRegistry = defaultMapper.registry;
|
|
402
|
+
exports.RequestObject = defaultMapper.RequestObject;
|
|
403
|
+
exports.AutoMap = defaultMapper.AutoMap;
|
|
404
|
+
exports.Param = defaultMapper.Param;
|
|
405
|
+
exports.Query = defaultMapper.Query;
|
|
406
|
+
exports.Header = defaultMapper.Header;
|
|
407
|
+
exports.Body = defaultMapper.Body;
|
|
408
|
+
exports.getRequestMappingDefinitions = defaultMapper.getRequestMappingDefinitions;
|
|
409
|
+
exports.getAutoMapMeta = defaultMapper.getAutoMapMeta;
|
|
410
|
+
exports.assignRequestContents = defaultMapper.assignRequestContents;
|
|
411
|
+
exports.requestContentsToObject = defaultMapper.requestContentsToObject;
|
|
412
|
+
exports.objectToRequestContents = defaultMapper.objectToRequestContents;
|
|
413
|
+
class MappedRequest {
|
|
414
|
+
defaultConstruct(request) {
|
|
415
|
+
(0, exports.assignRequestContents)(this, request);
|
|
416
|
+
}
|
|
417
|
+
construct(_request) {
|
|
418
|
+
// Extension hook for subclasses.
|
|
419
|
+
}
|
|
420
|
+
destruct(options) {
|
|
421
|
+
return (0, exports.objectToRequestContents)(this, options);
|
|
422
|
+
}
|
|
423
|
+
toRequestContents(options) {
|
|
424
|
+
return this.destruct(options);
|
|
425
|
+
}
|
|
426
|
+
validateWith(validate) {
|
|
427
|
+
return Promise.resolve(validate(this));
|
|
428
|
+
}
|
|
429
|
+
constructor(request) {
|
|
430
|
+
if (request !== undefined) {
|
|
431
|
+
setPendingRequest(this, request);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
static create(data) {
|
|
435
|
+
const instance = new this();
|
|
436
|
+
for (const key of Object.keys(data)) {
|
|
437
|
+
instance[key] = data[key];
|
|
438
|
+
}
|
|
439
|
+
return instance;
|
|
440
|
+
}
|
|
441
|
+
static fromRequest(request, options) {
|
|
442
|
+
const instance = new this();
|
|
443
|
+
(0, exports.assignRequestContents)(instance, request, options);
|
|
444
|
+
const maybeConstruct = instance.construct;
|
|
445
|
+
if (typeof maybeConstruct === 'function')
|
|
446
|
+
maybeConstruct.call(instance, request);
|
|
447
|
+
return instance;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
exports.MappedRequest = MappedRequest;
|
|
451
|
+
function CleanRequestObject(request) {
|
|
452
|
+
const out = {};
|
|
453
|
+
for (const key of Object.keys(request)) {
|
|
454
|
+
const value = request[key];
|
|
455
|
+
if (typeof value !== 'function')
|
|
456
|
+
out[key] = value;
|
|
457
|
+
}
|
|
458
|
+
return out;
|
|
459
|
+
}
|
|
460
|
+
exports.default = MappedRequest;
|