@dereekb/nestjs 13.6.12 → 13.6.14

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.
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@dereekb/nestjs/discord",
3
- "version": "13.6.12",
3
+ "version": "13.6.14",
4
4
  "peerDependencies": {
5
- "@dereekb/nestjs": "13.6.12",
6
- "@dereekb/rxjs": "13.6.12",
7
- "@dereekb/util": "13.6.12",
5
+ "@dereekb/nestjs": "13.6.14",
6
+ "@dereekb/rxjs": "13.6.14",
7
+ "@dereekb/util": "13.6.14",
8
8
  "@nestjs/common": "^11.1.17",
9
9
  "@nestjs/config": "^4.0.3",
10
10
  "discord.js": "^14.25.1",
@@ -0,0 +1 @@
1
+ exports._default = require('./index.cjs.js').default;
@@ -0,0 +1,382 @@
1
+ 'use strict';
2
+
3
+ function _array_like_to_array(arr, len) {
4
+ if (len == null || len > arr.length) len = arr.length;
5
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
6
+ return arr2;
7
+ }
8
+ function _array_without_holes(arr) {
9
+ if (Array.isArray(arr)) return _array_like_to_array(arr);
10
+ }
11
+ function _iterable_to_array(iter) {
12
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
13
+ }
14
+ function _non_iterable_spread() {
15
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
16
+ }
17
+ function _to_consumable_array(arr) {
18
+ return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
19
+ }
20
+ function _unsupported_iterable_to_array(o, minLen) {
21
+ if (!o) return;
22
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
23
+ var n = Object.prototype.toString.call(o).slice(8, -1);
24
+ if (n === "Object" && o.constructor) n = o.constructor.name;
25
+ if (n === "Map" || n === "Set") return Array.from(n);
26
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
27
+ }
28
+ /**
29
+ * The import source that NestJS decorators come from.
30
+ */ var NESTJS_COMMON_MODULE = '@nestjs/common';
31
+ /**
32
+ * NestJS class decorators that make a class eligible for dependency injection.
33
+ */ var NESTJS_CLASS_DECORATORS = new Set([
34
+ 'Injectable',
35
+ 'Controller',
36
+ 'Resolver'
37
+ ]);
38
+ /**
39
+ * Decorators that count as valid parameter injection markers.
40
+ * Any parameter with at least one of these is considered properly annotated.
41
+ */ var VALID_PARAM_DECORATORS = new Set([
42
+ 'Inject',
43
+ 'Optional',
44
+ 'Self',
45
+ 'SkipSelf',
46
+ 'Host'
47
+ ]);
48
+ /**
49
+ * Extracts the decorator name from a decorator node.
50
+ *
51
+ * @example
52
+ * ```
53
+ * // Returns 'Injectable' for both:
54
+ * // @Injectable()
55
+ * // @Injectable
56
+ * ```
57
+ *
58
+ * @param decorator - The decorator AST node
59
+ * @returns The decorator name, or empty string if unrecognized
60
+ */ function getDecoratorName(decorator) {
61
+ var expression = decorator.expression;
62
+ if (expression.type === 'CallExpression') {
63
+ if (expression.callee.type === 'Identifier') {
64
+ return expression.callee.name;
65
+ }
66
+ if (expression.callee.type === 'MemberExpression' && expression.callee.property.type === 'Identifier') {
67
+ return expression.callee.property.name;
68
+ }
69
+ }
70
+ if (expression.type === 'Identifier') {
71
+ return expression.name;
72
+ }
73
+ return '';
74
+ }
75
+ /**
76
+ * Extracts the injection token name from a decorator like @Inject(TokenName).
77
+ *
78
+ * @param decorator - The decorator AST node
79
+ * @returns The token identifier name, or null if not a simple identifier
80
+ */ function getInjectTokenFromDecorator(decorator) {
81
+ var expression = decorator.expression;
82
+ if (expression.type === 'CallExpression' && expression.callee.type === 'Identifier' && expression.callee.name === 'Inject') {
83
+ var firstArg = expression.arguments[0];
84
+ if ((firstArg === null || firstArg === void 0 ? void 0 : firstArg.type) === 'Identifier') {
85
+ return firstArg.name;
86
+ }
87
+ }
88
+ return null;
89
+ }
90
+ /**
91
+ * Extracts the parameter name from a constructor parameter node.
92
+ *
93
+ * @param param - The parameter AST node
94
+ * @returns The parameter name for error reporting
95
+ */ function getParamName(param) {
96
+ if (param.type === 'TSParameterProperty') {
97
+ var inner = param.parameter;
98
+ if (inner.type === 'Identifier') {
99
+ return inner.name;
100
+ }
101
+ if (inner.type === 'AssignmentPattern' && inner.left.type === 'Identifier') {
102
+ return inner.left.name;
103
+ }
104
+ }
105
+ if (param.type === 'Identifier') {
106
+ return param.name;
107
+ }
108
+ if (param.type === 'AssignmentPattern' && param.left.type === 'Identifier') {
109
+ return param.left.name;
110
+ }
111
+ return '(unknown)';
112
+ }
113
+ /**
114
+ * Gets the class-typed injection token name from a parameter's type annotation, if available.
115
+ *
116
+ * Returns the type name only for simple TSTypeReference identifiers (i.e. class names like `FooApi`).
117
+ * Returns null for primitives, union types, generics, or any non-simple type reference.
118
+ *
119
+ * @param param - The parameter AST node
120
+ * @returns The type name to use as injection token, or null if not auto-fixable
121
+ */ function getInjectTokenName(param) {
122
+ var _target_typeAnnotation, _typeAnnotation_typeName;
123
+ var target = param.type === 'TSParameterProperty' ? param.parameter : param;
124
+ var typeAnnotation = (_target_typeAnnotation = target.typeAnnotation) === null || _target_typeAnnotation === void 0 ? void 0 : _target_typeAnnotation.typeAnnotation;
125
+ if ((typeAnnotation === null || typeAnnotation === void 0 ? void 0 : typeAnnotation.type) === 'TSTypeReference' && ((_typeAnnotation_typeName = typeAnnotation.typeName) === null || _typeAnnotation_typeName === void 0 ? void 0 : _typeAnnotation_typeName.type) === 'Identifier' && !typeAnnotation.typeArguments) {
126
+ return typeAnnotation.typeName.name;
127
+ }
128
+ return null;
129
+ }
130
+ /**
131
+ * ESLint rule requiring @Inject() on all constructor parameters in NestJS injectable classes.
132
+ *
133
+ * Required when emitDecoratorMetadata is disabled — without it, NestJS cannot infer
134
+ * constructor parameter types and will throw at runtime if @Inject() is missing.
135
+ *
136
+ * Only applies to decorators imported from '@nestjs/common', so Angular @Injectable()
137
+ * classes are not affected.
138
+ *
139
+ * Also flags injection tokens that are type-only imports (`import type { X }` or
140
+ * `import { type X }`), since those cannot be used as values at runtime.
141
+ *
142
+ * Auto-fixes:
143
+ * - Missing @Inject(): adds `@Inject(ClassName)` for simple class-typed parameters
144
+ * - Type-only imports: removes the `type` modifier from the import specifier
145
+ *
146
+ * Register as a plugin in your flat ESLint config, then enable individual rules
147
+ * under the chosen plugin prefix (e.g. 'dereekb-nestjs/require-nest-inject').
148
+ */ var nestjsRequireInjectRule = {
149
+ meta: {
150
+ type: 'problem',
151
+ fixable: 'code',
152
+ docs: {
153
+ description: 'Require @Inject() decorator on constructor parameters in NestJS injectable classes',
154
+ recommended: true
155
+ },
156
+ messages: {
157
+ missingInject: 'Constructor parameter "{{name}}" in @{{classDecorator}}() class must have an @Inject() decorator. Without emitDecoratorMetadata, NestJS cannot infer the injection token and will throw at runtime.',
158
+ typeOnlyInjectToken: '@Inject({{token}}) uses "{{token}}" which is a type-only import. It must be a value import to be used as an injection token at runtime.'
159
+ },
160
+ schema: [
161
+ {
162
+ type: 'object',
163
+ properties: {
164
+ additionalClassDecorators: {
165
+ type: 'array',
166
+ items: {
167
+ type: 'string'
168
+ },
169
+ description: 'Additional class decorator names (beyond Injectable, Controller, Resolver) that should trigger this rule.'
170
+ },
171
+ additionalParamDecorators: {
172
+ type: 'array',
173
+ items: {
174
+ type: 'string'
175
+ },
176
+ description: 'Additional parameter decorator names (beyond Inject, Optional, Self, SkipSelf, Host) to treat as valid injection markers.'
177
+ }
178
+ },
179
+ additionalProperties: false
180
+ }
181
+ ]
182
+ },
183
+ create: function create(context) {
184
+ var options = context.options[0] || {};
185
+ var classDecorators = new Set(_to_consumable_array(NESTJS_CLASS_DECORATORS).concat(_to_consumable_array(options.additionalClassDecorators || [])));
186
+ var paramDecorators = new Set(_to_consumable_array(VALID_PARAM_DECORATORS).concat(_to_consumable_array(options.additionalParamDecorators || [])));
187
+ /**
188
+ * Tracks identifiers imported from '@nestjs/common'.
189
+ * Only decorators whose local name appears in this set will trigger the rule.
190
+ */ var nestjsImports = new Set();
191
+ /**
192
+ * Tracks all type-only imports across the file, keyed by local identifier name.
193
+ * Used to detect when @Inject(Token) uses a type-only import.
194
+ */ var typeOnlyImports = new Map();
195
+ /**
196
+ * Reference to the '@nestjs/common' ImportDeclaration node, used for adding
197
+ * the Inject import if it's missing during auto-fix.
198
+ */ var nestjsImportNode = null;
199
+ return {
200
+ ImportDeclaration: function ImportDeclaration(node) {
201
+ var isDeclarationTypeOnly = node.importKind === 'type';
202
+ if (node.source.value === NESTJS_COMMON_MODULE) {
203
+ nestjsImportNode = node;
204
+ }
205
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
206
+ try {
207
+ for(var _iterator = node.specifiers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
208
+ var specifier = _step.value;
209
+ if (specifier.type !== 'ImportSpecifier') {
210
+ continue;
211
+ }
212
+ var localName = specifier.local.name;
213
+ // Track @nestjs/common imports
214
+ if (node.source.value === NESTJS_COMMON_MODULE) {
215
+ nestjsImports.add(localName);
216
+ }
217
+ // Track type-only imports from any source
218
+ var isTypeOnly = isDeclarationTypeOnly || specifier.importKind === 'type';
219
+ if (isTypeOnly) {
220
+ typeOnlyImports.set(localName, {
221
+ specifier: specifier,
222
+ declaration: node,
223
+ isDeclarationLevel: isDeclarationTypeOnly
224
+ });
225
+ }
226
+ }
227
+ } catch (err) {
228
+ _didIteratorError = true;
229
+ _iteratorError = err;
230
+ } finally{
231
+ try {
232
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
233
+ _iterator.return();
234
+ }
235
+ } finally{
236
+ if (_didIteratorError) {
237
+ throw _iteratorError;
238
+ }
239
+ }
240
+ }
241
+ },
242
+ ClassDeclaration: function ClassDeclaration(classNode) {
243
+ var decorators = classNode.decorators;
244
+ if (!decorators || decorators.length === 0) {
245
+ return;
246
+ }
247
+ var matchedClassDecorator = decorators.find(function(d) {
248
+ var name = getDecoratorName(d);
249
+ return classDecorators.has(name) && nestjsImports.has(name);
250
+ });
251
+ if (!matchedClassDecorator) {
252
+ return;
253
+ }
254
+ var classDecoratorName = getDecoratorName(matchedClassDecorator);
255
+ var constructor = classNode.body.body.find(function(member) {
256
+ return member.type === 'MethodDefinition' && member.kind === 'constructor';
257
+ });
258
+ if (!(constructor === null || constructor === void 0 ? void 0 : constructor.value.params) || constructor.value.params.length === 0) {
259
+ return;
260
+ }
261
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
262
+ try {
263
+ var _loop = function() {
264
+ var param = _step.value;
265
+ var paramDecoratorsOnNode = param.decorators;
266
+ var hasValidDecorator = paramDecoratorsOnNode && paramDecoratorsOnNode.length > 0 && paramDecoratorsOnNode.some(function(d) {
267
+ return paramDecorators.has(getDecoratorName(d));
268
+ });
269
+ if (!hasValidDecorator) {
270
+ // Missing @Inject() entirely
271
+ var tokenName = getInjectTokenName(param);
272
+ context.report({
273
+ node: param,
274
+ messageId: 'missingInject',
275
+ data: {
276
+ name: getParamName(param),
277
+ classDecorator: classDecoratorName
278
+ },
279
+ fix: tokenName ? function(fixer) {
280
+ var fixes = [];
281
+ fixes.push(fixer.insertTextBefore(param, "@Inject(".concat(tokenName, ") ")));
282
+ if (!nestjsImports.has('Inject') && nestjsImportNode) {
283
+ var lastSpecifier = nestjsImportNode.specifiers[nestjsImportNode.specifiers.length - 1];
284
+ if (lastSpecifier) {
285
+ fixes.push(fixer.insertTextAfter(lastSpecifier, ', Inject'));
286
+ }
287
+ nestjsImports.add('Inject');
288
+ }
289
+ return fixes;
290
+ } : undefined
291
+ });
292
+ } else {
293
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
294
+ try {
295
+ var _loop = function() {
296
+ var decorator = _step1.value;
297
+ var tokenName = getInjectTokenFromDecorator(decorator);
298
+ if (tokenName) {
299
+ var typeImportInfo = typeOnlyImports.get(tokenName);
300
+ if (typeImportInfo) {
301
+ context.report({
302
+ node: decorator,
303
+ messageId: 'typeOnlyInjectToken',
304
+ data: {
305
+ token: tokenName
306
+ },
307
+ fix: function fix(fixer) {
308
+ if (typeImportInfo.isDeclarationLevel) {
309
+ // `import type { X } from '...'` → `import { X } from '...'`
310
+ // Remove 'type ' after 'import '
311
+ var importKeywordEnd = typeImportInfo.declaration.range[0] + 'import '.length;
312
+ return fixer.removeRange([
313
+ importKeywordEnd,
314
+ importKeywordEnd + 'type '.length
315
+ ]);
316
+ }
317
+ // `import { type X }` → `import { X }`
318
+ // The specifier range includes 'type X', so remove 'type ' prefix
319
+ var specRange = typeImportInfo.specifier.range;
320
+ var importedRange = typeImportInfo.specifier.imported.range;
321
+ return fixer.removeRange([
322
+ specRange[0],
323
+ importedRange[0]
324
+ ]);
325
+ }
326
+ });
327
+ // Remove from tracking so we don't report the same import twice
328
+ typeOnlyImports.delete(tokenName);
329
+ }
330
+ }
331
+ };
332
+ // Has @Inject() — check if the injection token is a type-only import
333
+ for(var _iterator = paramDecoratorsOnNode[Symbol.iterator](), _step1; !(_iteratorNormalCompletion = (_step1 = _iterator.next()).done); _iteratorNormalCompletion = true)_loop();
334
+ } catch (err) {
335
+ _didIteratorError = true;
336
+ _iteratorError = err;
337
+ } finally{
338
+ try {
339
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
340
+ _iterator.return();
341
+ }
342
+ } finally{
343
+ if (_didIteratorError) {
344
+ throw _iteratorError;
345
+ }
346
+ }
347
+ }
348
+ }
349
+ };
350
+ for(var _iterator = constructor.value.params[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true)_loop();
351
+ } catch (err) {
352
+ _didIteratorError = true;
353
+ _iteratorError = err;
354
+ } finally{
355
+ try {
356
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
357
+ _iterator.return();
358
+ }
359
+ } finally{
360
+ if (_didIteratorError) {
361
+ throw _iteratorError;
362
+ }
363
+ }
364
+ }
365
+ }
366
+ };
367
+ }
368
+ };
369
+
370
+ /**
371
+ * ESLint plugin for NestJS rules.
372
+ *
373
+ * Register as a plugin in your flat ESLint config, then enable individual rules
374
+ * under the chosen plugin prefix (e.g. 'dereekb-nestjs/require-nest-inject').
375
+ */ var nestjsEslintPlugin = {
376
+ rules: {
377
+ 'require-nest-inject': nestjsRequireInjectRule
378
+ }
379
+ };
380
+
381
+ exports.nestjsEslintPlugin = nestjsEslintPlugin;
382
+ exports.nestjsRequireInjectRule = nestjsRequireInjectRule;
@@ -0,0 +1,2 @@
1
+ export * from './index.cjs.js';
2
+ export { _default as default } from './index.cjs.default.js';
@@ -0,0 +1 @@
1
+ export * from "./src/index";
@@ -0,0 +1,379 @@
1
+ function _array_like_to_array(arr, len) {
2
+ if (len == null || len > arr.length) len = arr.length;
3
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
4
+ return arr2;
5
+ }
6
+ function _array_without_holes(arr) {
7
+ if (Array.isArray(arr)) return _array_like_to_array(arr);
8
+ }
9
+ function _iterable_to_array(iter) {
10
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
11
+ }
12
+ function _non_iterable_spread() {
13
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
14
+ }
15
+ function _to_consumable_array(arr) {
16
+ return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
17
+ }
18
+ function _unsupported_iterable_to_array(o, minLen) {
19
+ if (!o) return;
20
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
21
+ var n = Object.prototype.toString.call(o).slice(8, -1);
22
+ if (n === "Object" && o.constructor) n = o.constructor.name;
23
+ if (n === "Map" || n === "Set") return Array.from(n);
24
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
25
+ }
26
+ /**
27
+ * The import source that NestJS decorators come from.
28
+ */ var NESTJS_COMMON_MODULE = '@nestjs/common';
29
+ /**
30
+ * NestJS class decorators that make a class eligible for dependency injection.
31
+ */ var NESTJS_CLASS_DECORATORS = new Set([
32
+ 'Injectable',
33
+ 'Controller',
34
+ 'Resolver'
35
+ ]);
36
+ /**
37
+ * Decorators that count as valid parameter injection markers.
38
+ * Any parameter with at least one of these is considered properly annotated.
39
+ */ var VALID_PARAM_DECORATORS = new Set([
40
+ 'Inject',
41
+ 'Optional',
42
+ 'Self',
43
+ 'SkipSelf',
44
+ 'Host'
45
+ ]);
46
+ /**
47
+ * Extracts the decorator name from a decorator node.
48
+ *
49
+ * @example
50
+ * ```
51
+ * // Returns 'Injectable' for both:
52
+ * // @Injectable()
53
+ * // @Injectable
54
+ * ```
55
+ *
56
+ * @param decorator - The decorator AST node
57
+ * @returns The decorator name, or empty string if unrecognized
58
+ */ function getDecoratorName(decorator) {
59
+ var expression = decorator.expression;
60
+ if (expression.type === 'CallExpression') {
61
+ if (expression.callee.type === 'Identifier') {
62
+ return expression.callee.name;
63
+ }
64
+ if (expression.callee.type === 'MemberExpression' && expression.callee.property.type === 'Identifier') {
65
+ return expression.callee.property.name;
66
+ }
67
+ }
68
+ if (expression.type === 'Identifier') {
69
+ return expression.name;
70
+ }
71
+ return '';
72
+ }
73
+ /**
74
+ * Extracts the injection token name from a decorator like @Inject(TokenName).
75
+ *
76
+ * @param decorator - The decorator AST node
77
+ * @returns The token identifier name, or null if not a simple identifier
78
+ */ function getInjectTokenFromDecorator(decorator) {
79
+ var expression = decorator.expression;
80
+ if (expression.type === 'CallExpression' && expression.callee.type === 'Identifier' && expression.callee.name === 'Inject') {
81
+ var firstArg = expression.arguments[0];
82
+ if ((firstArg === null || firstArg === void 0 ? void 0 : firstArg.type) === 'Identifier') {
83
+ return firstArg.name;
84
+ }
85
+ }
86
+ return null;
87
+ }
88
+ /**
89
+ * Extracts the parameter name from a constructor parameter node.
90
+ *
91
+ * @param param - The parameter AST node
92
+ * @returns The parameter name for error reporting
93
+ */ function getParamName(param) {
94
+ if (param.type === 'TSParameterProperty') {
95
+ var inner = param.parameter;
96
+ if (inner.type === 'Identifier') {
97
+ return inner.name;
98
+ }
99
+ if (inner.type === 'AssignmentPattern' && inner.left.type === 'Identifier') {
100
+ return inner.left.name;
101
+ }
102
+ }
103
+ if (param.type === 'Identifier') {
104
+ return param.name;
105
+ }
106
+ if (param.type === 'AssignmentPattern' && param.left.type === 'Identifier') {
107
+ return param.left.name;
108
+ }
109
+ return '(unknown)';
110
+ }
111
+ /**
112
+ * Gets the class-typed injection token name from a parameter's type annotation, if available.
113
+ *
114
+ * Returns the type name only for simple TSTypeReference identifiers (i.e. class names like `FooApi`).
115
+ * Returns null for primitives, union types, generics, or any non-simple type reference.
116
+ *
117
+ * @param param - The parameter AST node
118
+ * @returns The type name to use as injection token, or null if not auto-fixable
119
+ */ function getInjectTokenName(param) {
120
+ var _target_typeAnnotation, _typeAnnotation_typeName;
121
+ var target = param.type === 'TSParameterProperty' ? param.parameter : param;
122
+ var typeAnnotation = (_target_typeAnnotation = target.typeAnnotation) === null || _target_typeAnnotation === void 0 ? void 0 : _target_typeAnnotation.typeAnnotation;
123
+ if ((typeAnnotation === null || typeAnnotation === void 0 ? void 0 : typeAnnotation.type) === 'TSTypeReference' && ((_typeAnnotation_typeName = typeAnnotation.typeName) === null || _typeAnnotation_typeName === void 0 ? void 0 : _typeAnnotation_typeName.type) === 'Identifier' && !typeAnnotation.typeArguments) {
124
+ return typeAnnotation.typeName.name;
125
+ }
126
+ return null;
127
+ }
128
+ /**
129
+ * ESLint rule requiring @Inject() on all constructor parameters in NestJS injectable classes.
130
+ *
131
+ * Required when emitDecoratorMetadata is disabled — without it, NestJS cannot infer
132
+ * constructor parameter types and will throw at runtime if @Inject() is missing.
133
+ *
134
+ * Only applies to decorators imported from '@nestjs/common', so Angular @Injectable()
135
+ * classes are not affected.
136
+ *
137
+ * Also flags injection tokens that are type-only imports (`import type { X }` or
138
+ * `import { type X }`), since those cannot be used as values at runtime.
139
+ *
140
+ * Auto-fixes:
141
+ * - Missing @Inject(): adds `@Inject(ClassName)` for simple class-typed parameters
142
+ * - Type-only imports: removes the `type` modifier from the import specifier
143
+ *
144
+ * Register as a plugin in your flat ESLint config, then enable individual rules
145
+ * under the chosen plugin prefix (e.g. 'dereekb-nestjs/require-nest-inject').
146
+ */ var nestjsRequireInjectRule = {
147
+ meta: {
148
+ type: 'problem',
149
+ fixable: 'code',
150
+ docs: {
151
+ description: 'Require @Inject() decorator on constructor parameters in NestJS injectable classes',
152
+ recommended: true
153
+ },
154
+ messages: {
155
+ missingInject: 'Constructor parameter "{{name}}" in @{{classDecorator}}() class must have an @Inject() decorator. Without emitDecoratorMetadata, NestJS cannot infer the injection token and will throw at runtime.',
156
+ typeOnlyInjectToken: '@Inject({{token}}) uses "{{token}}" which is a type-only import. It must be a value import to be used as an injection token at runtime.'
157
+ },
158
+ schema: [
159
+ {
160
+ type: 'object',
161
+ properties: {
162
+ additionalClassDecorators: {
163
+ type: 'array',
164
+ items: {
165
+ type: 'string'
166
+ },
167
+ description: 'Additional class decorator names (beyond Injectable, Controller, Resolver) that should trigger this rule.'
168
+ },
169
+ additionalParamDecorators: {
170
+ type: 'array',
171
+ items: {
172
+ type: 'string'
173
+ },
174
+ description: 'Additional parameter decorator names (beyond Inject, Optional, Self, SkipSelf, Host) to treat as valid injection markers.'
175
+ }
176
+ },
177
+ additionalProperties: false
178
+ }
179
+ ]
180
+ },
181
+ create: function create(context) {
182
+ var options = context.options[0] || {};
183
+ var classDecorators = new Set(_to_consumable_array(NESTJS_CLASS_DECORATORS).concat(_to_consumable_array(options.additionalClassDecorators || [])));
184
+ var paramDecorators = new Set(_to_consumable_array(VALID_PARAM_DECORATORS).concat(_to_consumable_array(options.additionalParamDecorators || [])));
185
+ /**
186
+ * Tracks identifiers imported from '@nestjs/common'.
187
+ * Only decorators whose local name appears in this set will trigger the rule.
188
+ */ var nestjsImports = new Set();
189
+ /**
190
+ * Tracks all type-only imports across the file, keyed by local identifier name.
191
+ * Used to detect when @Inject(Token) uses a type-only import.
192
+ */ var typeOnlyImports = new Map();
193
+ /**
194
+ * Reference to the '@nestjs/common' ImportDeclaration node, used for adding
195
+ * the Inject import if it's missing during auto-fix.
196
+ */ var nestjsImportNode = null;
197
+ return {
198
+ ImportDeclaration: function ImportDeclaration(node) {
199
+ var isDeclarationTypeOnly = node.importKind === 'type';
200
+ if (node.source.value === NESTJS_COMMON_MODULE) {
201
+ nestjsImportNode = node;
202
+ }
203
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
204
+ try {
205
+ for(var _iterator = node.specifiers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
206
+ var specifier = _step.value;
207
+ if (specifier.type !== 'ImportSpecifier') {
208
+ continue;
209
+ }
210
+ var localName = specifier.local.name;
211
+ // Track @nestjs/common imports
212
+ if (node.source.value === NESTJS_COMMON_MODULE) {
213
+ nestjsImports.add(localName);
214
+ }
215
+ // Track type-only imports from any source
216
+ var isTypeOnly = isDeclarationTypeOnly || specifier.importKind === 'type';
217
+ if (isTypeOnly) {
218
+ typeOnlyImports.set(localName, {
219
+ specifier: specifier,
220
+ declaration: node,
221
+ isDeclarationLevel: isDeclarationTypeOnly
222
+ });
223
+ }
224
+ }
225
+ } catch (err) {
226
+ _didIteratorError = true;
227
+ _iteratorError = err;
228
+ } finally{
229
+ try {
230
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
231
+ _iterator.return();
232
+ }
233
+ } finally{
234
+ if (_didIteratorError) {
235
+ throw _iteratorError;
236
+ }
237
+ }
238
+ }
239
+ },
240
+ ClassDeclaration: function ClassDeclaration(classNode) {
241
+ var decorators = classNode.decorators;
242
+ if (!decorators || decorators.length === 0) {
243
+ return;
244
+ }
245
+ var matchedClassDecorator = decorators.find(function(d) {
246
+ var name = getDecoratorName(d);
247
+ return classDecorators.has(name) && nestjsImports.has(name);
248
+ });
249
+ if (!matchedClassDecorator) {
250
+ return;
251
+ }
252
+ var classDecoratorName = getDecoratorName(matchedClassDecorator);
253
+ var constructor = classNode.body.body.find(function(member) {
254
+ return member.type === 'MethodDefinition' && member.kind === 'constructor';
255
+ });
256
+ if (!(constructor === null || constructor === void 0 ? void 0 : constructor.value.params) || constructor.value.params.length === 0) {
257
+ return;
258
+ }
259
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
260
+ try {
261
+ var _loop = function() {
262
+ var param = _step.value;
263
+ var paramDecoratorsOnNode = param.decorators;
264
+ var hasValidDecorator = paramDecoratorsOnNode && paramDecoratorsOnNode.length > 0 && paramDecoratorsOnNode.some(function(d) {
265
+ return paramDecorators.has(getDecoratorName(d));
266
+ });
267
+ if (!hasValidDecorator) {
268
+ // Missing @Inject() entirely
269
+ var tokenName = getInjectTokenName(param);
270
+ context.report({
271
+ node: param,
272
+ messageId: 'missingInject',
273
+ data: {
274
+ name: getParamName(param),
275
+ classDecorator: classDecoratorName
276
+ },
277
+ fix: tokenName ? function(fixer) {
278
+ var fixes = [];
279
+ fixes.push(fixer.insertTextBefore(param, "@Inject(".concat(tokenName, ") ")));
280
+ if (!nestjsImports.has('Inject') && nestjsImportNode) {
281
+ var lastSpecifier = nestjsImportNode.specifiers[nestjsImportNode.specifiers.length - 1];
282
+ if (lastSpecifier) {
283
+ fixes.push(fixer.insertTextAfter(lastSpecifier, ', Inject'));
284
+ }
285
+ nestjsImports.add('Inject');
286
+ }
287
+ return fixes;
288
+ } : undefined
289
+ });
290
+ } else {
291
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
292
+ try {
293
+ var _loop = function() {
294
+ var decorator = _step1.value;
295
+ var tokenName = getInjectTokenFromDecorator(decorator);
296
+ if (tokenName) {
297
+ var typeImportInfo = typeOnlyImports.get(tokenName);
298
+ if (typeImportInfo) {
299
+ context.report({
300
+ node: decorator,
301
+ messageId: 'typeOnlyInjectToken',
302
+ data: {
303
+ token: tokenName
304
+ },
305
+ fix: function fix(fixer) {
306
+ if (typeImportInfo.isDeclarationLevel) {
307
+ // `import type { X } from '...'` → `import { X } from '...'`
308
+ // Remove 'type ' after 'import '
309
+ var importKeywordEnd = typeImportInfo.declaration.range[0] + 'import '.length;
310
+ return fixer.removeRange([
311
+ importKeywordEnd,
312
+ importKeywordEnd + 'type '.length
313
+ ]);
314
+ }
315
+ // `import { type X }` → `import { X }`
316
+ // The specifier range includes 'type X', so remove 'type ' prefix
317
+ var specRange = typeImportInfo.specifier.range;
318
+ var importedRange = typeImportInfo.specifier.imported.range;
319
+ return fixer.removeRange([
320
+ specRange[0],
321
+ importedRange[0]
322
+ ]);
323
+ }
324
+ });
325
+ // Remove from tracking so we don't report the same import twice
326
+ typeOnlyImports.delete(tokenName);
327
+ }
328
+ }
329
+ };
330
+ // Has @Inject() — check if the injection token is a type-only import
331
+ for(var _iterator = paramDecoratorsOnNode[Symbol.iterator](), _step1; !(_iteratorNormalCompletion = (_step1 = _iterator.next()).done); _iteratorNormalCompletion = true)_loop();
332
+ } catch (err) {
333
+ _didIteratorError = true;
334
+ _iteratorError = err;
335
+ } finally{
336
+ try {
337
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
338
+ _iterator.return();
339
+ }
340
+ } finally{
341
+ if (_didIteratorError) {
342
+ throw _iteratorError;
343
+ }
344
+ }
345
+ }
346
+ }
347
+ };
348
+ for(var _iterator = constructor.value.params[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true)_loop();
349
+ } catch (err) {
350
+ _didIteratorError = true;
351
+ _iteratorError = err;
352
+ } finally{
353
+ try {
354
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
355
+ _iterator.return();
356
+ }
357
+ } finally{
358
+ if (_didIteratorError) {
359
+ throw _iteratorError;
360
+ }
361
+ }
362
+ }
363
+ }
364
+ };
365
+ }
366
+ };
367
+
368
+ /**
369
+ * ESLint plugin for NestJS rules.
370
+ *
371
+ * Register as a plugin in your flat ESLint config, then enable individual rules
372
+ * under the chosen plugin prefix (e.g. 'dereekb-nestjs/require-nest-inject').
373
+ */ var nestjsEslintPlugin = {
374
+ rules: {
375
+ 'require-nest-inject': nestjsRequireInjectRule
376
+ }
377
+ };
378
+
379
+ export { nestjsEslintPlugin, nestjsRequireInjectRule };
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@dereekb/nestjs/eslint",
3
+ "version": "13.6.14",
4
+ "peerDependencies": {
5
+ "@typescript-eslint/utils": ">=8.0.0"
6
+ },
7
+ "exports": {
8
+ "./package.json": "./package.json",
9
+ ".": {
10
+ "module": "./index.esm.js",
11
+ "types": "./index.d.ts",
12
+ "import": "./index.cjs.mjs",
13
+ "default": "./index.cjs.js"
14
+ }
15
+ },
16
+ "module": "./index.esm.js",
17
+ "main": "./index.cjs.js",
18
+ "types": "./index.d.ts"
19
+ }
@@ -0,0 +1 @@
1
+ export * from './lib';
@@ -0,0 +1,2 @@
1
+ export { nestjsRequireInjectRule, type NestjsRequireInjectRuleOptions } from './require-inject.rule';
2
+ export { nestjsEslintPlugin } from './plugin';
@@ -0,0 +1,16 @@
1
+ import { type NestjsRequireInjectRuleDefinition } from './require-inject.rule';
2
+ /**
3
+ * ESLint plugin interface for NestJS rules.
4
+ */
5
+ export interface NestjsEslintPlugin {
6
+ readonly rules: {
7
+ readonly 'require-nest-inject': NestjsRequireInjectRuleDefinition;
8
+ };
9
+ }
10
+ /**
11
+ * ESLint plugin for NestJS rules.
12
+ *
13
+ * Register as a plugin in your flat ESLint config, then enable individual rules
14
+ * under the chosen plugin prefix (e.g. 'dereekb-nestjs/require-nest-inject').
15
+ */
16
+ export declare const nestjsEslintPlugin: NestjsEslintPlugin;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Options for the require-nest-inject rule.
3
+ */
4
+ export interface NestjsRequireInjectRuleOptions {
5
+ /**
6
+ * Additional class decorator names (beyond Injectable, Controller, Resolver) that should trigger this rule.
7
+ */
8
+ readonly additionalClassDecorators?: string[];
9
+ /**
10
+ * Additional parameter decorator names (beyond Inject, Optional, Self, SkipSelf, Host) to treat as valid injection markers.
11
+ */
12
+ readonly additionalParamDecorators?: string[];
13
+ }
14
+ type AstNode = any;
15
+ /**
16
+ * ESLint rule definition for require-nest-inject.
17
+ */
18
+ export interface NestjsRequireInjectRuleDefinition {
19
+ readonly meta: {
20
+ readonly type: 'problem';
21
+ readonly fixable: 'code';
22
+ readonly docs: {
23
+ readonly description: string;
24
+ readonly recommended: boolean;
25
+ };
26
+ readonly messages: {
27
+ readonly missingInject: string;
28
+ readonly typeOnlyInjectToken: string;
29
+ };
30
+ readonly schema: readonly object[];
31
+ };
32
+ create(context: {
33
+ options: NestjsRequireInjectRuleOptions[];
34
+ report: (descriptor: {
35
+ node: AstNode;
36
+ messageId: string;
37
+ data: Record<string, string>;
38
+ fix?: (fixer: AstNode) => AstNode;
39
+ }) => void;
40
+ sourceCode: {
41
+ getText: (node: AstNode) => string;
42
+ };
43
+ }): Record<string, (node: AstNode) => void>;
44
+ }
45
+ /**
46
+ * ESLint rule requiring @Inject() on all constructor parameters in NestJS injectable classes.
47
+ *
48
+ * Required when emitDecoratorMetadata is disabled — without it, NestJS cannot infer
49
+ * constructor parameter types and will throw at runtime if @Inject() is missing.
50
+ *
51
+ * Only applies to decorators imported from '@nestjs/common', so Angular @Injectable()
52
+ * classes are not affected.
53
+ *
54
+ * Also flags injection tokens that are type-only imports (`import type { X }` or
55
+ * `import { type X }`), since those cannot be used as values at runtime.
56
+ *
57
+ * Auto-fixes:
58
+ * - Missing @Inject(): adds `@Inject(ClassName)` for simple class-typed parameters
59
+ * - Type-only imports: removes the `type` modifier from the import specifier
60
+ *
61
+ * Register as a plugin in your flat ESLint config, then enable individual rules
62
+ * under the chosen plugin prefix (e.g. 'dereekb-nestjs/require-nest-inject').
63
+ */
64
+ export declare const nestjsRequireInjectRule: NestjsRequireInjectRuleDefinition;
65
+ export {};
package/index.cjs.js CHANGED
@@ -155,6 +155,7 @@ function _ts_generator$1(thisArg, body) {
155
155
  * ```
156
156
  *
157
157
  * @param config - Filesystem configuration with base path.
158
+ * @returns An {@link AssetLoader} backed by the local filesystem.
158
159
  */ function nodeJsLocalAssetLoader(config) {
159
160
  var basePath = config.basePath;
160
161
  var getFn = function getFn(ref) {
@@ -198,6 +199,7 @@ function _ts_generator$1(thisArg, body) {
198
199
  * ```
199
200
  *
200
201
  * @param config - Local filesystem config and optional remote fetch config.
202
+ * @returns NestJS {@link ModuleMetadata} that provides an {@link AssetLoader}.
201
203
  */ function appAssetLoaderModuleMetadata(config) {
202
204
  var local = nodeJsLocalAssetLoader(config.local);
203
205
  var remote = rxjs.fetchAssetLoader(config.remote);
package/index.esm.js CHANGED
@@ -153,6 +153,7 @@ function _ts_generator$1(thisArg, body) {
153
153
  * ```
154
154
  *
155
155
  * @param config - Filesystem configuration with base path.
156
+ * @returns An {@link AssetLoader} backed by the local filesystem.
156
157
  */ function nodeJsLocalAssetLoader(config) {
157
158
  var basePath = config.basePath;
158
159
  var getFn = function getFn(ref) {
@@ -196,6 +197,7 @@ function _ts_generator$1(thisArg, body) {
196
197
  * ```
197
198
  *
198
199
  * @param config - Local filesystem config and optional remote fetch config.
200
+ * @returns NestJS {@link ModuleMetadata} that provides an {@link AssetLoader}.
199
201
  */ function appAssetLoaderModuleMetadata(config) {
200
202
  var local = nodeJsLocalAssetLoader(config.local);
201
203
  var remote = fetchAssetLoader(config.remote);
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@dereekb/nestjs/mailgun",
3
- "version": "13.6.12",
3
+ "version": "13.6.14",
4
4
  "peerDependencies": {
5
- "@dereekb/date": "13.6.12",
6
- "@dereekb/model": "13.6.12",
7
- "@dereekb/nestjs": "13.6.12",
8
- "@dereekb/rxjs": "13.6.12",
9
- "@dereekb/util": "13.6.12",
5
+ "@dereekb/date": "13.6.14",
6
+ "@dereekb/model": "13.6.14",
7
+ "@dereekb/nestjs": "13.6.14",
8
+ "@dereekb/rxjs": "13.6.14",
9
+ "@dereekb/util": "13.6.14",
10
10
  "@nestjs/common": "^11.1.17",
11
11
  "@nestjs/config": "^4.0.3",
12
12
  "form-data": "^4.0.0",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@dereekb/nestjs/openai",
3
- "version": "13.6.12",
3
+ "version": "13.6.14",
4
4
  "peerDependencies": {
5
- "@dereekb/date": "13.6.12",
6
- "@dereekb/model": "13.6.12",
7
- "@dereekb/nestjs": "13.6.12",
8
- "@dereekb/rxjs": "13.6.12",
9
- "@dereekb/util": "13.6.12",
5
+ "@dereekb/date": "13.6.14",
6
+ "@dereekb/model": "13.6.14",
7
+ "@dereekb/nestjs": "13.6.14",
8
+ "@dereekb/rxjs": "13.6.14",
9
+ "@dereekb/util": "13.6.14",
10
10
  "@nestjs/common": "^11.1.17",
11
11
  "@nestjs/config": "^4.0.3",
12
12
  "express": "^5.0.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/nestjs",
3
- "version": "13.6.12",
3
+ "version": "13.6.14",
4
4
  "types": "./src/index.d.ts",
5
5
  "module": "./index.esm.js",
6
6
  "main": "./index.cjs.js",
@@ -41,6 +41,12 @@
41
41
  "import": "./discord/index.cjs.mjs",
42
42
  "default": "./discord/index.cjs.js"
43
43
  },
44
+ "./eslint": {
45
+ "module": "./eslint/index.esm.js",
46
+ "types": "./eslint/index.d.ts",
47
+ "import": "./eslint/index.cjs.mjs",
48
+ "default": "./eslint/index.cjs.js"
49
+ },
44
50
  "./package.json": "./package.json",
45
51
  ".": {
46
52
  "module": "./index.esm.js",
@@ -50,8 +56,8 @@
50
56
  }
51
57
  },
52
58
  "peerDependencies": {
53
- "@dereekb/rxjs": "13.6.12",
54
- "@dereekb/util": "13.6.12",
59
+ "@dereekb/rxjs": "13.6.14",
60
+ "@dereekb/util": "13.6.14",
55
61
  "discord.js": "^14.25.1",
56
62
  "@nestjs/common": "^11.1.17",
57
63
  "@nestjs/config": "^4.0.3",
@@ -27,5 +27,6 @@ export interface NodeJsLocalAssetLoaderConfig {
27
27
  * ```
28
28
  *
29
29
  * @param config - Filesystem configuration with base path.
30
+ * @returns An {@link AssetLoader} backed by the local filesystem.
30
31
  */
31
32
  export declare function nodeJsLocalAssetLoader(config: NodeJsLocalAssetLoaderConfig): AssetLoader;
@@ -33,5 +33,6 @@ export interface AppAssetLoaderModuleMetadataConfig {
33
33
  * ```
34
34
  *
35
35
  * @param config - Local filesystem config and optional remote fetch config.
36
+ * @returns NestJS {@link ModuleMetadata} that provides an {@link AssetLoader}.
36
37
  */
37
38
  export declare function appAssetLoaderModuleMetadata(config: AppAssetLoaderModuleMetadataConfig): ModuleMetadata;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@dereekb/nestjs/stripe",
3
- "version": "13.6.12",
3
+ "version": "13.6.14",
4
4
  "peerDependencies": {
5
- "@dereekb/date": "13.6.12",
6
- "@dereekb/model": "13.6.12",
7
- "@dereekb/nestjs": "13.6.12",
8
- "@dereekb/rxjs": "13.6.12",
9
- "@dereekb/util": "13.6.12",
5
+ "@dereekb/date": "13.6.14",
6
+ "@dereekb/model": "13.6.14",
7
+ "@dereekb/nestjs": "13.6.14",
8
+ "@dereekb/rxjs": "13.6.14",
9
+ "@dereekb/util": "13.6.14",
10
10
  "@nestjs/common": "^11.1.17",
11
11
  "@nestjs/config": "^4.0.3",
12
12
  "express": "^5.0.0",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@dereekb/nestjs/typeform",
3
- "version": "13.6.12",
3
+ "version": "13.6.14",
4
4
  "peerDependencies": {
5
- "@dereekb/date": "13.6.12",
6
- "@dereekb/model": "13.6.12",
7
- "@dereekb/nestjs": "13.6.12",
8
- "@dereekb/rxjs": "13.6.12",
9
- "@dereekb/util": "13.6.12",
5
+ "@dereekb/date": "13.6.14",
6
+ "@dereekb/model": "13.6.14",
7
+ "@dereekb/nestjs": "13.6.14",
8
+ "@dereekb/rxjs": "13.6.14",
9
+ "@dereekb/util": "13.6.14",
10
10
  "@nestjs/common": "^11.1.17",
11
11
  "@nestjs/config": "^4.0.3",
12
12
  "@typeform/api-client": "^2.5.1",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@dereekb/nestjs/vapiai",
3
- "version": "13.6.12",
3
+ "version": "13.6.14",
4
4
  "peerDependencies": {
5
- "@dereekb/date": "13.6.12",
6
- "@dereekb/model": "13.6.12",
7
- "@dereekb/nestjs": "13.6.12",
8
- "@dereekb/rxjs": "13.6.12",
9
- "@dereekb/util": "13.6.12",
5
+ "@dereekb/date": "13.6.14",
6
+ "@dereekb/model": "13.6.14",
7
+ "@dereekb/nestjs": "13.6.14",
8
+ "@dereekb/rxjs": "13.6.14",
9
+ "@dereekb/util": "13.6.14",
10
10
  "@nestjs/common": "^11.1.17",
11
11
  "@nestjs/config": "^4.0.3",
12
12
  "@vapi-ai/server-sdk": "^0.11.0",