@loopback/metadata 4.0.0-alpha.6 → 4.0.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.
Files changed (97) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +273 -150
  3. package/dist/decorator-factory.d.ts +297 -0
  4. package/{dist6/src → dist}/decorator-factory.js +197 -90
  5. package/dist/decorator-factory.js.map +1 -0
  6. package/dist/index.d.ts +22 -1
  7. package/dist/index.js +24 -7
  8. package/dist/index.js.map +1 -0
  9. package/dist/inspector.d.ts +120 -0
  10. package/dist/inspector.js +186 -0
  11. package/dist/inspector.js.map +1 -0
  12. package/dist/{src/reflect.d.ts → reflect.d.ts} +16 -15
  13. package/dist/{src/reflect.js → reflect.js} +7 -8
  14. package/dist/reflect.js.map +1 -0
  15. package/dist/types.d.ts +70 -0
  16. package/dist/types.js +32 -0
  17. package/dist/types.js.map +1 -0
  18. package/package.json +43 -44
  19. package/src/decorator-factory.ts +289 -140
  20. package/src/index.ts +22 -2
  21. package/src/inspector.ts +118 -126
  22. package/src/reflect.ts +32 -21
  23. package/src/types.ts +94 -0
  24. package/CHANGELOG.md +0 -75
  25. package/api-docs/apple-touch-icon-114x114-precomposed.png +0 -0
  26. package/api-docs/apple-touch-icon-144x144-precomposed.png +0 -0
  27. package/api-docs/apple-touch-icon-57x57-precomposed.png +0 -0
  28. package/api-docs/apple-touch-icon-72x72-precomposed.png +0 -0
  29. package/api-docs/apple-touch-icon-precomposed.png +0 -0
  30. package/api-docs/apple-touch-icon.png +0 -0
  31. package/api-docs/css/bootstrap.min.css +0 -9
  32. package/api-docs/css/code-themes/arta.css +0 -158
  33. package/api-docs/css/code-themes/ascetic.css +0 -50
  34. package/api-docs/css/code-themes/brown_paper.css +0 -104
  35. package/api-docs/css/code-themes/brown_papersq.png +0 -0
  36. package/api-docs/css/code-themes/dark.css +0 -103
  37. package/api-docs/css/code-themes/default.css +0 -135
  38. package/api-docs/css/code-themes/far.css +0 -111
  39. package/api-docs/css/code-themes/github.css +0 -127
  40. package/api-docs/css/code-themes/googlecode.css +0 -144
  41. package/api-docs/css/code-themes/idea.css +0 -121
  42. package/api-docs/css/code-themes/ir_black.css +0 -104
  43. package/api-docs/css/code-themes/magula.css +0 -121
  44. package/api-docs/css/code-themes/monokai.css +0 -114
  45. package/api-docs/css/code-themes/pojoaque.css +0 -104
  46. package/api-docs/css/code-themes/pojoaque.jpg +0 -0
  47. package/api-docs/css/code-themes/rainbow.css +0 -114
  48. package/api-docs/css/code-themes/school_book.css +0 -111
  49. package/api-docs/css/code-themes/school_book.png +0 -0
  50. package/api-docs/css/code-themes/sl-theme.css +0 -45
  51. package/api-docs/css/code-themes/solarized_dark.css +0 -88
  52. package/api-docs/css/code-themes/solarized_light.css +0 -88
  53. package/api-docs/css/code-themes/sunburst.css +0 -158
  54. package/api-docs/css/code-themes/tomorrow-night-blue.css +0 -52
  55. package/api-docs/css/code-themes/tomorrow-night-bright.css +0 -51
  56. package/api-docs/css/code-themes/tomorrow-night-eighties.css +0 -51
  57. package/api-docs/css/code-themes/tomorrow-night.css +0 -52
  58. package/api-docs/css/code-themes/tomorrow.css +0 -49
  59. package/api-docs/css/code-themes/vs.css +0 -86
  60. package/api-docs/css/code-themes/xcode.css +0 -154
  61. package/api-docs/css/code-themes/zenburn.css +0 -115
  62. package/api-docs/css/main.css +0 -139
  63. package/api-docs/favicon.ico +0 -0
  64. package/api-docs/fonts/0ihfXUL2emPh0ROJezvraLO3LdcAZYWl9Si6vvxL-qU.woff +0 -0
  65. package/api-docs/fonts/OsJ2DjdpjqFRVUSto6IffLO3LdcAZYWl9Si6vvxL-qU.woff +0 -0
  66. package/api-docs/fonts/_aijTyevf54tkVDLy-dlnLO3LdcAZYWl9Si6vvxL-qU.woff +0 -0
  67. package/api-docs/index.html +0 -4594
  68. package/api-docs/js/main.js +0 -19
  69. package/api-docs/js/vendor/bootstrap.min.js +0 -6
  70. package/api-docs/js/vendor/jquery-1.10.1.min.js +0 -6
  71. package/api-docs/js/vendor/jquery.scrollTo-1.4.3.1.js +0 -218
  72. package/api-docs/js/vendor/modernizr-2.6.2-respond-1.1.0.min.js +0 -11
  73. package/dist/src/decorator-factory.d.ts +0 -252
  74. package/dist/src/decorator-factory.js +0 -468
  75. package/dist/src/decorator-factory.js.map +0 -1
  76. package/dist/src/index.d.ts +0 -3
  77. package/dist/src/index.js +0 -13
  78. package/dist/src/index.js.map +0 -1
  79. package/dist/src/inspector.d.ts +0 -150
  80. package/dist/src/inspector.js +0 -163
  81. package/dist/src/inspector.js.map +0 -1
  82. package/dist/src/reflect.js.map +0 -1
  83. package/dist6/index.d.ts +0 -1
  84. package/dist6/index.js +0 -13
  85. package/dist6/src/decorator-factory.d.ts +0 -252
  86. package/dist6/src/decorator-factory.js.map +0 -1
  87. package/dist6/src/index.d.ts +0 -3
  88. package/dist6/src/index.js +0 -13
  89. package/dist6/src/index.js.map +0 -1
  90. package/dist6/src/inspector.d.ts +0 -150
  91. package/dist6/src/inspector.js +0 -163
  92. package/dist6/src/inspector.js.map +0 -1
  93. package/dist6/src/reflect.d.ts +0 -38
  94. package/dist6/src/reflect.js +0 -143
  95. package/dist6/src/reflect.js.map +0 -1
  96. package/index.d.ts +0 -6
  97. package/index.js +0 -7
@@ -1,21 +1,15 @@
1
- // Copyright IBM Corp. 2017,2018. All Rights Reserved.
1
+ // Copyright IBM Corp. 2017,2020. All Rights Reserved.
2
2
  // Node module: @loopback/metadata
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
+ import debugModule from 'debug';
7
+ import _ from 'lodash';
6
8
  import {Reflector} from './reflect';
7
- import * as _ from 'lodash';
8
- import * as debugModule from 'debug';
9
+ import {DecoratorType, MetadataKey, MetadataMap} from './types';
9
10
  const debug = debugModule('loopback:metadata:decorator');
10
11
 
11
- // tslint:disable:no-any
12
-
13
- /**
14
- * An object mapping keys to corresponding metadata
15
- */
16
- export interface MetadataMap<T> {
17
- [propertyOrMethodName: string]: T;
18
- }
12
+ /* eslint-disable @typescript-eslint/no-explicit-any */
19
13
 
20
14
  /**
21
15
  * Options for a decorator
@@ -34,18 +28,15 @@ export interface DecoratorOptions {
34
28
  * Default to `true`.
35
29
  */
36
30
  cloneInputSpec?: boolean;
31
+
32
+ /**
33
+ * Name of the decorator for debugging purpose, such as `@inject`
34
+ */
35
+ decoratorName?: string;
36
+
37
37
  [name: string]: any;
38
38
  }
39
39
 
40
- /**
41
- * Decorator function types
42
- */
43
- export type DecoratorType =
44
- | ClassDecorator
45
- | PropertyDecorator
46
- | MethodDecorator
47
- | ParameterDecorator;
48
-
49
40
  /**
50
41
  * Base factory class for decorator functions
51
42
  *
@@ -71,8 +62,10 @@ export type DecoratorType =
71
62
  export class DecoratorFactory<
72
63
  T, // Type of the metadata spec for individual class/method/property/parameter
73
64
  M extends T | MetadataMap<T> | MetadataMap<T[]>, // Type of the metadata
74
- D extends DecoratorType // Type of the decorator
65
+ D extends DecoratorType, // Type of the decorator
75
66
  > {
67
+ protected decoratorName: string;
68
+
76
69
  /**
77
70
  * A constant to reference the target of a decoration
78
71
  */
@@ -80,27 +73,32 @@ export class DecoratorFactory<
80
73
 
81
74
  /**
82
75
  * Construct a new class decorator factory
83
- * @param key Metadata key
84
- * @param spec Metadata object from the decorator function
85
- * @param options Options for the decorator. Default to
76
+ * @param key - Metadata key
77
+ * @param spec - Metadata object from the decorator function
78
+ * @param options - Options for the decorator. Default to
86
79
  * `{allowInheritance: true}` if not provided
87
80
  */
88
81
  constructor(
89
82
  protected key: string,
90
83
  protected spec: T,
91
- protected options?: DecoratorOptions,
84
+ protected options: DecoratorOptions = {},
92
85
  ) {
93
86
  this.options = Object.assign(
94
- {allowInheritance: true, cloneInputSpec: true},
87
+ {
88
+ allowInheritance: true,
89
+ cloneInputSpec: true,
90
+ },
95
91
  options,
96
92
  );
93
+ const defaultDecoratorName = this.constructor.name.replace(/Factory$/, '');
94
+ this.decoratorName = this.options.decoratorName ?? defaultDecoratorName;
97
95
  if (this.options.cloneInputSpec) {
98
96
  this.spec = DecoratorFactory.cloneDeep(spec);
99
97
  }
100
98
  }
101
99
 
102
100
  protected allowInheritance(): boolean {
103
- return !!(this.options && this.options.allowInheritance);
101
+ return !!this.options?.allowInheritance;
104
102
  }
105
103
 
106
104
  /**
@@ -108,12 +106,12 @@ export class DecoratorFactory<
108
106
  * metadata into the spec if `allowInheritance` is set to `true`. To customize
109
107
  * the behavior, this method can be overridden by sub classes.
110
108
  *
111
- * @param inheritedMetadata Metadata from base classes for the member
109
+ * @param inheritedMetadata - Metadata from base classes for the member
112
110
  */
113
111
  protected inherit(inheritedMetadata: T | undefined | null): T {
114
112
  if (!this.allowInheritance()) return this.spec;
115
113
  if (inheritedMetadata == null) return this.spec;
116
- if (this.spec == undefined) return inheritedMetadata;
114
+ if (this.spec == null) return inheritedMetadata;
117
115
  if (typeof inheritedMetadata !== 'object') return this.spec;
118
116
  if (Array.isArray(inheritedMetadata) || Array.isArray(this.spec)) {
119
117
  // For arrays, we don't merge
@@ -123,20 +121,24 @@ export class DecoratorFactory<
123
121
  }
124
122
 
125
123
  /**
126
- * Get the qualified name of a decoration target. For example:
127
- * ```
128
- * class MyClass
129
- * MyClass.constructor[0] // First parameter of the constructor
130
- * MyClass.myStaticProperty
131
- * MyClass.myStaticMethod()
132
- * MyClass.myStaticMethod[0] // First parameter of the myStaticMethod
133
- * MyClass.prototype.myProperty
134
- * MyClass.prototype.myMethod()
135
- * MyClass.prototype.myMethod[1] // Second parameter of myMethod
136
- * ```
137
- * @param target Class or prototype of a class
138
- * @param member Optional property/method name
139
- * @param descriptorOrIndex Optional method descriptor or parameter index
124
+ * Get the qualified name of a decoration target.
125
+ *
126
+ * @remarks
127
+ *
128
+ * Example of target names:
129
+ *
130
+ * - class MyClass
131
+ * - MyClass.constructor[0] // First parameter of the constructor
132
+ * - MyClass.myStaticProperty
133
+ * - MyClass.myStaticMethod()
134
+ * - MyClass.myStaticMethod[0] // First parameter of the myStaticMethod
135
+ * - MyClass.prototype.myProperty
136
+ * - MyClass.prototype.myMethod()
137
+ * - MyClass.prototype.myMethod[1] // Second parameter of myMethod
138
+ *
139
+ * @param target - Class or prototype of a class
140
+ * @param member - Optional property/method name
141
+ * @param descriptorOrIndex - Optional method descriptor or parameter index
140
142
  */
141
143
  static getTargetName(
142
144
  target: Object,
@@ -150,38 +152,43 @@ export class DecoratorFactory<
150
152
  if (member == null && descriptorOrIndex == null) {
151
153
  return `class ${name}`;
152
154
  }
153
- if (member == null) member = 'constructor';
155
+ if (member == null || member === '') member = 'constructor';
156
+
157
+ const memberAccessor =
158
+ typeof member === 'symbol' ? '[' + member.toString() + ']' : '.' + member;
159
+
154
160
  if (typeof descriptorOrIndex === 'number') {
155
161
  // Parameter
156
- name = `${name}.${member}[${descriptorOrIndex}]`;
162
+ name = `${name}${memberAccessor}[${descriptorOrIndex}]`;
157
163
  } else if (descriptorOrIndex != null) {
158
- name = `${name}.${member}()`;
164
+ name = `${name}${memberAccessor}()`;
159
165
  } else {
160
- name = `${name}.${member}`;
166
+ name = `${name}${memberAccessor}`;
161
167
  }
162
168
  return name;
163
169
  }
164
170
 
165
171
  /**
166
172
  * Get the number of parameters for a given constructor or method
167
- * @param target Class or the prototype
168
- * @param member Method name
173
+ * @param target - Class or the prototype
174
+ * @param member - Method name
169
175
  */
170
- static getNumberOfParameters(target: Object, member?: string | symbol) {
171
- if (target instanceof Function && !member) {
176
+ static getNumberOfParameters(target: Object, member?: string) {
177
+ if (typeof target === 'function' && !member) {
172
178
  // constructor
173
179
  return target.length;
174
180
  } else {
175
181
  // target[member] is a function
176
- return (<{[methodName: string]: Function}>target)[member!].length;
182
+ const method = (<{[methodName: string]: Function}>target)[member!];
183
+ return method.length;
177
184
  }
178
185
  }
179
186
 
180
187
  /**
181
188
  * Set a reference to the target class or prototype for a given spec if
182
189
  * it's an object
183
- * @param spec Metadata spec
184
- * @param target Target of the decoration. It is a class or the prototype of
190
+ * @param spec - Metadata spec
191
+ * @param target - Target of the decoration. It is a class or the prototype of
185
192
  * a class.
186
193
  */
187
194
  withTarget(spec: T, target: Object) {
@@ -190,6 +197,8 @@ export class DecoratorFactory<
190
197
  Object.defineProperty(spec, DecoratorFactory.TARGET, {
191
198
  value: target,
192
199
  enumerable: false,
200
+ // Make sure it won't be redefined on the same object
201
+ configurable: false,
193
202
  });
194
203
  }
195
204
  return spec;
@@ -197,7 +206,7 @@ export class DecoratorFactory<
197
206
 
198
207
  /**
199
208
  * Get the optional decoration target of a given spec
200
- * @param spec Metadata spec
209
+ * @param spec - Metadata spec
201
210
  */
202
211
  getTarget(spec: T) {
203
212
  if (typeof spec === 'object' && spec != null) {
@@ -216,10 +225,10 @@ export class DecoratorFactory<
216
225
  *
217
226
  * It MUST be overridden by subclasses to process inherited metadata.
218
227
  *
219
- * @param inheritedMetadata Metadata inherited from the base classes
220
- * @param target Decoration target
221
- * @param member Optional property or method
222
- * @param descriptorOrIndex Optional parameter index or method descriptor
228
+ * @param inheritedMetadata - Metadata inherited from the base classes
229
+ * @param target - Decoration target
230
+ * @param member - Optional property or method
231
+ * @param descriptorOrIndex - Optional parameter index or method descriptor
223
232
  */
224
233
  protected mergeWithInherited(
225
234
  inheritedMetadata: M,
@@ -227,7 +236,9 @@ export class DecoratorFactory<
227
236
  member?: string | symbol,
228
237
  descriptorOrIndex?: TypedPropertyDescriptor<any> | number,
229
238
  ): M {
230
- throw new Error('mergeWithInherited() is not implemented');
239
+ throw new Error(
240
+ `mergeWithInherited() is not implemented for ${this.decoratorName}`,
241
+ );
231
242
  }
232
243
 
233
244
  /**
@@ -238,10 +249,10 @@ export class DecoratorFactory<
238
249
  *
239
250
  * It MUST be overridden by subclasses to process own metadata.
240
251
  *
241
- * @param ownMetadata Own Metadata exists locally on the target
242
- * @param target Decoration target
243
- * @param member Optional property or method
244
- * @param descriptorOrIndex Optional parameter index or method descriptor
252
+ * @param ownMetadata - Own Metadata exists locally on the target
253
+ * @param target - Decoration target
254
+ * @param member - Optional property or method
255
+ * @param descriptorOrIndex - Optional parameter index or method descriptor
245
256
  */
246
257
  protected mergeWithOwn(
247
258
  ownMetadata: M,
@@ -249,7 +260,31 @@ export class DecoratorFactory<
249
260
  member?: string | symbol,
250
261
  descriptorOrIndex?: TypedPropertyDescriptor<any> | number,
251
262
  ): M {
252
- throw new Error('mergeWithOwn() is not implemented');
263
+ throw new Error(
264
+ `mergeWithOwn() is not implemented for ${this.decoratorName}`,
265
+ );
266
+ }
267
+
268
+ /**
269
+ * Create an error to report if the decorator is applied to the target more
270
+ * than once
271
+ * @param target - Decoration target
272
+ * @param member - Optional property or method
273
+ * @param descriptorOrIndex - Optional parameter index or method descriptor
274
+ */
275
+ protected duplicateDecorationError(
276
+ target: Object,
277
+ member?: string | symbol,
278
+ descriptorOrIndex?: TypedPropertyDescriptor<any> | number,
279
+ ) {
280
+ const targetName = DecoratorFactory.getTargetName(
281
+ target,
282
+ member,
283
+ descriptorOrIndex,
284
+ );
285
+ return new Error(
286
+ `${this.decoratorName} cannot be applied more than once on ${targetName}`,
287
+ );
253
288
  }
254
289
 
255
290
  /**
@@ -257,14 +292,14 @@ export class DecoratorFactory<
257
292
  * implement this method.
258
293
  */
259
294
  create(): D {
260
- throw new Error('create() is not implemented');
295
+ throw new Error(`create() is not implemented for ${this.decoratorName}`);
261
296
  }
262
297
 
263
298
  /**
264
299
  * Base implementation of the decorator function
265
- * @param target Decorator target
266
- * @param member Optional property or method
267
- * @param descriptorOrIndex Optional method descriptor or parameter index
300
+ * @param target - Decorator target
301
+ * @param member - Optional property or method
302
+ * @param descriptorOrIndex - Optional method descriptor or parameter index
268
303
  */
269
304
  protected decorate(
270
305
  target: Object,
@@ -284,12 +319,14 @@ export class DecoratorFactory<
284
319
  Reflector.getMetadata(this.key, target),
285
320
  );
286
321
  meta = this.mergeWithInherited(meta, target, member, descriptorOrIndex);
322
+ /* istanbul ignore if */
287
323
  if (debug.enabled) {
288
324
  debug('%s: %j', targetName, meta);
289
325
  }
290
326
  Reflector.defineMetadata(this.key, meta, target);
291
327
  } else {
292
328
  meta = this.mergeWithOwn(meta, target, member, descriptorOrIndex);
329
+ /* istanbul ignore if */
293
330
  if (debug.enabled) {
294
331
  debug('%s: %j', targetName, meta);
295
332
  }
@@ -299,30 +336,50 @@ export class DecoratorFactory<
299
336
 
300
337
  /**
301
338
  * Create a decorator function
302
- * @param key Metadata key
303
- * @param spec Metadata object from the decorator function
304
- * @param options Options for the decorator
339
+ * @param key - Metadata key
340
+ * @param spec - Metadata object from the decorator function
341
+ * @param options - Options for the decorator
305
342
  */
306
343
  protected static _createDecorator<
307
- T,
308
- M extends T | MetadataMap<T> | MetadataMap<T[]>,
309
- D extends DecoratorType
310
- >(key: string, spec: T, options?: DecoratorOptions): D {
311
- const inst = new this<T, M, D>(key, spec, options);
344
+ S,
345
+ MT extends S | MetadataMap<S> | MetadataMap<S[]>,
346
+ DT extends DecoratorType,
347
+ >(key: MetadataKey<S, DT>, spec: S, options?: DecoratorOptions): DT {
348
+ const inst = new this<S, MT, DT>(key.toString(), spec, options);
312
349
  return inst.create();
313
350
  }
314
351
 
315
- static cloneDeep<T>(val: T): T {
352
+ // See https://github.com/lodash/lodash/blob/master/.internal/baseClone.js
353
+ private static _cloneableTypes = [
354
+ Object,
355
+ Array,
356
+ Set,
357
+ Map,
358
+ RegExp,
359
+ Date,
360
+ Buffer,
361
+ ArrayBuffer,
362
+ Float32Array,
363
+ Float64Array,
364
+ Int8Array,
365
+ Int16Array,
366
+ Int32Array,
367
+ Uint8Array,
368
+ Uint8ClampedArray,
369
+ Uint16Array,
370
+ Uint32Array,
371
+ ];
372
+
373
+ static cloneDeep<V>(val: Readonly<V>): V {
316
374
  if (typeof val !== 'object') return val;
317
375
  return _.cloneDeepWith(val, v => {
318
- // Do not clone functions
319
- if (typeof v === 'function') return v;
376
+ if (typeof v !== 'object') return v;
377
+ if (v == null) return v;
320
378
  if (
321
- v &&
322
- typeof v.constructor === 'function' &&
323
- v.constructor.prototype === v
379
+ v.constructor != null &&
380
+ !DecoratorFactory._cloneableTypes.includes(v.constructor)
324
381
  ) {
325
- // Do not clone class prototype
382
+ // Do not clone instances of classes/constructors, such as Date
326
383
  return v;
327
384
  }
328
385
  return undefined;
@@ -341,7 +398,7 @@ export class ClassDecoratorFactory<T> extends DecoratorFactory<
341
398
  protected mergeWithInherited(
342
399
  inheritedMetadata: T,
343
400
  target: Object,
344
- member?: string | symbol,
401
+ member?: string,
345
402
  descriptorOrIndex?: TypedPropertyDescriptor<any> | number,
346
403
  ) {
347
404
  return this.withTarget(<T>this.inherit(inheritedMetadata), target);
@@ -350,14 +407,11 @@ export class ClassDecoratorFactory<T> extends DecoratorFactory<
350
407
  protected mergeWithOwn(
351
408
  ownMetadata: T,
352
409
  target: Object,
353
- member?: string | symbol,
410
+ member?: string,
354
411
  descriptorOrIndex?: TypedPropertyDescriptor<any> | number,
355
412
  ) {
356
413
  if (ownMetadata != null) {
357
- throw new Error(
358
- 'Decorator cannot be applied more than once on ' +
359
- DecoratorFactory.getTargetName(target),
360
- );
414
+ throw this.duplicateDecorationError(target, member, descriptorOrIndex);
361
415
  }
362
416
  return this.withTarget(this.spec, target);
363
417
  }
@@ -368,12 +422,16 @@ export class ClassDecoratorFactory<T> extends DecoratorFactory<
368
422
 
369
423
  /**
370
424
  * Create a class decorator function
371
- * @param key Metadata key
372
- * @param spec Metadata object from the decorator function
373
- * @param options Options for the decorator
425
+ * @param key - Metadata key
426
+ * @param spec - Metadata object from the decorator function
427
+ * @param options - Options for the decorator
374
428
  */
375
- static createDecorator<T>(key: string, spec: T, options?: DecoratorOptions) {
376
- return super._createDecorator<T, T, ClassDecorator>(key, spec, options);
429
+ static createDecorator<S>(
430
+ key: MetadataKey<S, ClassDecorator>,
431
+ spec: S,
432
+ options?: DecoratorOptions,
433
+ ) {
434
+ return super._createDecorator<S, S, ClassDecorator>(key, spec, options);
377
435
  }
378
436
  }
379
437
 
@@ -388,7 +446,7 @@ export class PropertyDecoratorFactory<T> extends DecoratorFactory<
388
446
  protected mergeWithInherited(
389
447
  inheritedMetadata: MetadataMap<T>,
390
448
  target: Object,
391
- propertyName?: string | symbol,
449
+ propertyName?: string,
392
450
  descriptorOrIndex?: TypedPropertyDescriptor<any> | number,
393
451
  ) {
394
452
  inheritedMetadata = inheritedMetadata || {};
@@ -403,14 +461,15 @@ export class PropertyDecoratorFactory<T> extends DecoratorFactory<
403
461
  protected mergeWithOwn(
404
462
  ownMetadata: MetadataMap<T>,
405
463
  target: Object,
406
- propertyName?: string | symbol,
464
+ propertyName?: string,
407
465
  descriptorOrParameterIndex?: TypedPropertyDescriptor<any> | number,
408
466
  ) {
409
467
  ownMetadata = ownMetadata || {};
410
468
  if (ownMetadata[propertyName!] != null) {
411
- const targetName = DecoratorFactory.getTargetName(target, propertyName);
412
- throw new Error(
413
- 'Decorator cannot be applied more than once on ' + targetName,
469
+ throw this.duplicateDecorationError(
470
+ target,
471
+ propertyName,
472
+ descriptorOrParameterIndex,
414
473
  );
415
474
  }
416
475
  ownMetadata[propertyName!] = this.withTarget(this.spec, target);
@@ -424,12 +483,16 @@ export class PropertyDecoratorFactory<T> extends DecoratorFactory<
424
483
 
425
484
  /**
426
485
  * Create a property decorator function
427
- * @param key Metadata key
428
- * @param spec Metadata object from the decorator function
429
- * @param options Options for the decorator
486
+ * @param key - Metadata key
487
+ * @param spec - Metadata object from the decorator function
488
+ * @param options - Options for the decorator
430
489
  */
431
- static createDecorator<T>(key: string, spec: T, options?: DecoratorOptions) {
432
- return super._createDecorator<T, MetadataMap<T>, PropertyDecorator>(
490
+ static createDecorator<S>(
491
+ key: MetadataKey<S, PropertyDecorator>,
492
+ spec: S,
493
+ options?: DecoratorOptions,
494
+ ) {
495
+ return super._createDecorator<S, MetadataMap<S>, PropertyDecorator>(
433
496
  key,
434
497
  spec,
435
498
  options,
@@ -448,7 +511,7 @@ export class MethodDecoratorFactory<T> extends DecoratorFactory<
448
511
  protected mergeWithInherited(
449
512
  inheritedMetadata: MetadataMap<T>,
450
513
  target: Object,
451
- methodName?: string | symbol,
514
+ methodName?: string,
452
515
  methodDescriptor?: TypedPropertyDescriptor<any> | number,
453
516
  ) {
454
517
  inheritedMetadata = inheritedMetadata || {};
@@ -463,16 +526,13 @@ export class MethodDecoratorFactory<T> extends DecoratorFactory<
463
526
  protected mergeWithOwn(
464
527
  ownMetadata: MetadataMap<T>,
465
528
  target: Object,
466
- methodName?: string | symbol,
529
+ methodName?: string,
467
530
  methodDescriptor?: TypedPropertyDescriptor<any> | number,
468
531
  ) {
469
532
  ownMetadata = ownMetadata || {};
470
533
  const methodMeta = ownMetadata[methodName!];
471
534
  if (this.getTarget(methodMeta) === target) {
472
- throw new Error(
473
- 'Decorator cannot be applied more than once on ' +
474
- DecoratorFactory.getTargetName(target, methodName, methodDescriptor),
475
- );
535
+ throw this.duplicateDecorationError(target, methodName, methodDescriptor);
476
536
  }
477
537
  // Set the method metadata
478
538
  ownMetadata[methodName!] = this.withTarget(this.spec, target);
@@ -489,12 +549,16 @@ export class MethodDecoratorFactory<T> extends DecoratorFactory<
489
549
 
490
550
  /**
491
551
  * Create a method decorator function
492
- * @param key Metadata key
493
- * @param spec Metadata object from the decorator function
494
- * @param options Options for the decorator
552
+ * @param key - Metadata key
553
+ * @param spec - Metadata object from the decorator function
554
+ * @param options - Options for the decorator
495
555
  */
496
- static createDecorator<T>(key: string, spec: T, options?: DecoratorOptions) {
497
- return super._createDecorator<T, MetadataMap<T>, MethodDecorator>(
556
+ static createDecorator<S>(
557
+ key: MetadataKey<S, MethodDecorator>,
558
+ spec: S,
559
+ options?: DecoratorOptions,
560
+ ) {
561
+ return super._createDecorator<S, MetadataMap<S>, MethodDecorator>(
498
562
  key,
499
563
  spec,
500
564
  options,
@@ -513,7 +577,7 @@ export class ParameterDecoratorFactory<T> extends DecoratorFactory<
513
577
  private getOrInitMetadata(
514
578
  meta: MetadataMap<T[]>,
515
579
  target: Object,
516
- methodName?: string | symbol,
580
+ methodName?: string,
517
581
  ) {
518
582
  const method = methodName ? methodName : '';
519
583
  let methodMeta = meta[method];
@@ -530,7 +594,7 @@ export class ParameterDecoratorFactory<T> extends DecoratorFactory<
530
594
  protected mergeWithInherited(
531
595
  inheritedMetadata: MetadataMap<T[]>,
532
596
  target: Object,
533
- methodName?: string | symbol,
597
+ methodName?: string,
534
598
  parameterIndex?: TypedPropertyDescriptor<any> | number,
535
599
  ) {
536
600
  inheritedMetadata = inheritedMetadata || {};
@@ -550,7 +614,7 @@ export class ParameterDecoratorFactory<T> extends DecoratorFactory<
550
614
  protected mergeWithOwn(
551
615
  ownMetadata: MetadataMap<T[]>,
552
616
  target: Object,
553
- methodName?: string | symbol,
617
+ methodName?: string,
554
618
  parameterIndex?: TypedPropertyDescriptor<any> | number,
555
619
  ) {
556
620
  ownMetadata = ownMetadata || {};
@@ -558,10 +622,7 @@ export class ParameterDecoratorFactory<T> extends DecoratorFactory<
558
622
  const methodMeta = this.getOrInitMetadata(ownMetadata, target, methodName);
559
623
  const index = parameterIndex as number;
560
624
  if (this.getTarget(methodMeta[index]) === target) {
561
- throw new Error(
562
- 'Decorator cannot be applied more than once on ' +
563
- DecoratorFactory.getTargetName(target, methodName, parameterIndex),
564
- );
625
+ throw this.duplicateDecorationError(target, methodName, parameterIndex);
565
626
  }
566
627
  // Set the parameter metadata
567
628
  methodMeta[index] = this.withTarget(
@@ -581,12 +642,16 @@ export class ParameterDecoratorFactory<T> extends DecoratorFactory<
581
642
 
582
643
  /**
583
644
  * Create a parameter decorator function
584
- * @param key Metadata key
585
- * @param spec Metadata object from the decorator function
586
- * @param options Options for the decorator
645
+ * @param key - Metadata key
646
+ * @param spec - Metadata object from the decorator function
647
+ * @param options - Options for the decorator
587
648
  */
588
- static createDecorator<T>(key: string, spec: T, options?: DecoratorOptions) {
589
- return super._createDecorator<T, MetadataMap<T[]>, ParameterDecorator>(
649
+ static createDecorator<S>(
650
+ key: MetadataKey<S, ParameterDecorator>,
651
+ spec: S,
652
+ options?: DecoratorOptions,
653
+ ) {
654
+ return super._createDecorator<S, MetadataMap<S[]>, ParameterDecorator>(
590
655
  key,
591
656
  spec,
592
657
  options,
@@ -595,8 +660,11 @@ export class ParameterDecoratorFactory<T> extends DecoratorFactory<
595
660
  }
596
661
 
597
662
  /**
598
- * Factory for method level parameter decorator. For example, the following
599
- * code uses `@param` to declare two parameters for `greet()`.
663
+ * Factory for method level parameter decorator.
664
+ *
665
+ * @example
666
+ * For example, the following code uses `@param` to declare two parameters for
667
+ * `greet()`.
600
668
  * ```ts
601
669
  * class MyController {
602
670
  * @param('name') // Parameter 0
@@ -618,7 +686,7 @@ export class MethodParameterDecoratorFactory<T> extends DecoratorFactory<
618
686
  */
619
687
  private getParameterIndex(
620
688
  target: Object,
621
- methodName?: string | symbol,
689
+ methodName?: string,
622
690
  methodDescriptor?: TypedPropertyDescriptor<any> | number,
623
691
  ) {
624
692
  const numOfParams = DecoratorFactory.getNumberOfParameters(
@@ -641,7 +709,7 @@ export class MethodParameterDecoratorFactory<T> extends DecoratorFactory<
641
709
  methodDescriptor,
642
710
  );
643
711
  throw new Error(
644
- `The decorator is used more than ${numOfParams} time(s) on ${method}`,
712
+ `${this.decoratorName} is used more than ${numOfParams} time(s) on ${method}`,
645
713
  );
646
714
  }
647
715
  return index;
@@ -650,7 +718,7 @@ export class MethodParameterDecoratorFactory<T> extends DecoratorFactory<
650
718
  protected mergeWithInherited(
651
719
  inheritedMetadata: MetadataMap<T[]>,
652
720
  target: Object,
653
- methodName?: string | symbol,
721
+ methodName?: string,
654
722
  methodDescriptor?: TypedPropertyDescriptor<any> | number,
655
723
  ) {
656
724
  inheritedMetadata = inheritedMetadata || {};
@@ -679,13 +747,13 @@ export class MethodParameterDecoratorFactory<T> extends DecoratorFactory<
679
747
  protected mergeWithOwn(
680
748
  ownMetadata: MetadataMap<T[]>,
681
749
  target: Object,
682
- methodName?: string | symbol,
750
+ methodName?: string,
683
751
  methodDescriptor?: TypedPropertyDescriptor<any> | number,
684
752
  ) {
685
753
  ownMetadata = ownMetadata || {};
686
754
  const index = this.getParameterIndex(target, methodName, methodDescriptor);
687
755
 
688
- let params =
756
+ const params =
689
757
  ownMetadata[methodName!] || new Array(index + 1).fill(undefined);
690
758
  params[index] = this.withTarget(<T>this.inherit(params[index]), target);
691
759
  ownMetadata[methodName!] = params;
@@ -709,15 +777,96 @@ export class MethodParameterDecoratorFactory<T> extends DecoratorFactory<
709
777
 
710
778
  /**
711
779
  * Create a method decorator function
712
- * @param key Metadata key
713
- * @param spec Metadata object from the decorator function
714
- * @param options Options for the decorator
780
+ * @param key - Metadata key
781
+ * @param spec - Metadata object from the decorator function
782
+ * @param options - Options for the decorator
715
783
  */
716
- static createDecorator<T>(key: string, spec: T, options?: DecoratorOptions) {
717
- return super._createDecorator<T, MetadataMap<T[]>, MethodDecorator>(
784
+ static createDecorator<S>(
785
+ key: MetadataKey<S, MethodDecorator>,
786
+ spec: S,
787
+ options?: DecoratorOptions,
788
+ ) {
789
+ return super._createDecorator<S, MetadataMap<S[]>, MethodDecorator>(
718
790
  key,
719
791
  spec,
720
792
  options,
721
793
  );
722
794
  }
723
795
  }
796
+
797
+ /**
798
+ * Factory for an append-array of method-level decorators
799
+ * The `@response` metadata for a method is an array.
800
+ * Each item in the array should be a single value, containing
801
+ * a response code and a single spec or Model. This should allow:
802
+ *
803
+ * @example
804
+ * ```ts
805
+ * @response(200, MyFirstModel)
806
+ * @response(403, [NotAuthorizedReasonOne, NotAuthorizedReasonTwo])
807
+ * @response(404, NotFoundOne)
808
+ * @response(404, NotFoundTwo)
809
+ * @response(409, {schema: {}})
810
+ * public async myMethod() {}
811
+ * ```
812
+ *
813
+ * In the case that a ResponseObject is passed, it becomes the
814
+ * default for description/content, and if possible, further Models are
815
+ * incorporated as a `oneOf: []` array.
816
+ *
817
+ * In the case that a ReferenceObject is passed, it and it alone is used, since
818
+ * references can be external and we cannot `oneOf` their content.
819
+ *
820
+ * The factory creates and updates an array of items T[], and the getter
821
+ * provides the values as that array.
822
+ */
823
+ export class MethodMultiDecoratorFactory<T> extends MethodDecoratorFactory<
824
+ T[]
825
+ > {
826
+ protected mergeWithInherited(
827
+ inheritedMetadata: MetadataMap<T[]>,
828
+ target: Object,
829
+ methodName?: string,
830
+ ) {
831
+ inheritedMetadata = inheritedMetadata || {};
832
+
833
+ inheritedMetadata[methodName!] = this._mergeArray(
834
+ inheritedMetadata[methodName!],
835
+ this.withTarget(this.spec, target),
836
+ );
837
+
838
+ return inheritedMetadata;
839
+ }
840
+
841
+ protected mergeWithOwn(
842
+ ownMetadata: MetadataMap<T[]>,
843
+ target: Object,
844
+ methodName?: string,
845
+ methodDescriptor?: TypedPropertyDescriptor<any> | number,
846
+ ) {
847
+ ownMetadata = ownMetadata || {};
848
+ ownMetadata[methodName!] = this._mergeArray(
849
+ ownMetadata[methodName!],
850
+ this.withTarget(this.spec, target),
851
+ );
852
+ return ownMetadata;
853
+ }
854
+
855
+ private _mergeArray(result: T[], methodMeta: T | T[]) {
856
+ if (!result) {
857
+ if (Array.isArray(methodMeta)) {
858
+ result = methodMeta;
859
+ } else {
860
+ result = [methodMeta];
861
+ }
862
+ } else {
863
+ if (Array.isArray(methodMeta)) {
864
+ result.push(...methodMeta);
865
+ } else {
866
+ result.push(methodMeta);
867
+ }
868
+ }
869
+
870
+ return result;
871
+ }
872
+ }