te.js 2.1.0 → 2.1.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/README.md +197 -196
- package/auto-docs/analysis/handler-analyzer.js +58 -58
- package/auto-docs/analysis/source-resolver.js +101 -101
- package/auto-docs/constants.js +37 -37
- package/auto-docs/docs-llm/index.js +7 -7
- package/auto-docs/docs-llm/prompts.js +222 -222
- package/auto-docs/docs-llm/provider.js +132 -132
- package/auto-docs/index.js +146 -146
- package/auto-docs/openapi/endpoint-processor.js +277 -277
- package/auto-docs/openapi/generator.js +107 -107
- package/auto-docs/openapi/level3.js +131 -131
- package/auto-docs/openapi/spec-builders.js +244 -244
- package/auto-docs/ui/docs-ui.js +186 -186
- package/auto-docs/utils/logger.js +17 -17
- package/auto-docs/utils/strip-usage.js +10 -10
- package/cli/docs-command.js +315 -315
- package/cli/fly-command.js +71 -71
- package/cli/index.js +56 -56
- package/cors/index.js +71 -0
- package/database/index.js +165 -165
- package/database/mongodb.js +146 -146
- package/database/redis.js +201 -201
- package/docs/README.md +36 -36
- package/docs/ammo.md +362 -362
- package/docs/api-reference.md +490 -490
- package/docs/auto-docs.md +216 -216
- package/docs/cli.md +152 -152
- package/docs/configuration.md +275 -275
- package/docs/database.md +390 -390
- package/docs/error-handling.md +438 -438
- package/docs/file-uploads.md +333 -333
- package/docs/getting-started.md +214 -214
- package/docs/middleware.md +355 -355
- package/docs/rate-limiting.md +393 -393
- package/docs/routing.md +302 -302
- package/lib/llm/client.js +73 -0
- package/lib/llm/index.js +7 -0
- package/lib/llm/parse.js +89 -0
- package/package.json +64 -62
- package/rate-limit/algorithms/fixed-window.js +141 -141
- package/rate-limit/algorithms/sliding-window.js +147 -147
- package/rate-limit/algorithms/token-bucket.js +115 -115
- package/rate-limit/base.js +165 -165
- package/rate-limit/index.js +147 -147
- package/rate-limit/storage/base.js +104 -104
- package/rate-limit/storage/memory.js +101 -101
- package/rate-limit/storage/redis.js +88 -88
- package/server/ammo/body-parser.js +220 -220
- package/server/ammo/dispatch-helper.js +103 -103
- package/server/ammo/enhancer.js +57 -57
- package/server/ammo.js +454 -415
- package/server/endpoint.js +97 -74
- package/server/error.js +9 -9
- package/server/errors/code-context.js +125 -125
- package/server/errors/llm-error-service.js +140 -140
- package/server/files/helper.js +33 -33
- package/server/files/uploader.js +143 -143
- package/server/handler.js +158 -119
- package/server/target.js +185 -175
- package/server/targets/middleware-validator.js +22 -22
- package/server/targets/path-validator.js +21 -21
- package/server/targets/registry.js +160 -160
- package/server/targets/shoot-validator.js +21 -21
- package/te.js +428 -402
- package/utils/auto-register.js +17 -17
- package/utils/configuration.js +64 -64
- package/utils/errors-llm-config.js +84 -84
- package/utils/request-logger.js +43 -43
- package/utils/status-codes.js +82 -82
- package/utils/tejas-entrypoint-html.js +18 -18
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import TejLogger from 'tej-logger';
|
|
2
|
-
|
|
3
|
-
const logger = new TejLogger('MiddlewareValidator');
|
|
4
|
-
|
|
5
|
-
const isMiddlewareValid = (middleware) => {
|
|
6
|
-
if (typeof middleware !== 'function') {
|
|
7
|
-
logger.error(`Middleware ${middleware} should be a function. Skipping...`);
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const args = middleware.length;
|
|
12
|
-
if (args !== 2 && args !== 3) {
|
|
13
|
-
logger.error(
|
|
14
|
-
`Middleware ${middleware.name} should have 2 arguments (ammo, next) or 3 arguments (req, res, next). Skipping...`,
|
|
15
|
-
);
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return true;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export default isMiddlewareValid;
|
|
1
|
+
import TejLogger from 'tej-logger';
|
|
2
|
+
|
|
3
|
+
const logger = new TejLogger('MiddlewareValidator');
|
|
4
|
+
|
|
5
|
+
const isMiddlewareValid = (middleware) => {
|
|
6
|
+
if (typeof middleware !== 'function') {
|
|
7
|
+
logger.error(`Middleware ${middleware} should be a function. Skipping...`);
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const args = middleware.length;
|
|
12
|
+
if (args !== 2 && args !== 3) {
|
|
13
|
+
logger.error(
|
|
14
|
+
`Middleware ${middleware.name} should have 2 arguments (ammo, next) or 3 arguments (req, res, next). Skipping...`,
|
|
15
|
+
);
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return true;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default isMiddlewareValid;
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import TejLogger from 'tej-logger';
|
|
2
|
-
|
|
3
|
-
const logger = new TejLogger('PathValidator');
|
|
4
|
-
|
|
5
|
-
const standardizePath = (path) => {
|
|
6
|
-
if (!path || path.length === 0) return '';
|
|
7
|
-
|
|
8
|
-
let standardized = path.startsWith('/') ? path : `/${path}`;
|
|
9
|
-
return standardized.endsWith('/') ? standardized.slice(0, -1) : standardized;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const isPathValid = (path) => {
|
|
13
|
-
if (typeof path !== 'string') {
|
|
14
|
-
logger.error(`Path ${path} should be a string. Skipping...`);
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return true;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export { isPathValid, standardizePath };
|
|
1
|
+
import TejLogger from 'tej-logger';
|
|
2
|
+
|
|
3
|
+
const logger = new TejLogger('PathValidator');
|
|
4
|
+
|
|
5
|
+
const standardizePath = (path) => {
|
|
6
|
+
if (!path || path.length === 0) return '';
|
|
7
|
+
|
|
8
|
+
let standardized = path.startsWith('/') ? path : `/${path}`;
|
|
9
|
+
return standardized.endsWith('/') ? standardized.slice(0, -1) : standardized;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const isPathValid = (path) => {
|
|
13
|
+
if (typeof path !== 'string') {
|
|
14
|
+
logger.error(`Path ${path} should be a string. Skipping...`);
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return true;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export { isPathValid, standardizePath };
|
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
import isMiddlewareValid from './middleware-validator.js';
|
|
2
|
-
import { standardizePath } from './path-validator.js';
|
|
3
|
-
|
|
4
|
-
class TargetRegistry {
|
|
5
|
-
constructor() {
|
|
6
|
-
if (TargetRegistry.instance) {
|
|
7
|
-
return TargetRegistry.instance;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
TargetRegistry.instance = this;
|
|
11
|
-
|
|
12
|
-
// TODO - Add a default target
|
|
13
|
-
this.targets = [];
|
|
14
|
-
this.globalMiddlewares = [];
|
|
15
|
-
/** Current source group (target file id) set by loader before importing a target file. */
|
|
16
|
-
this._currentSourceGroup = null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
setCurrentSourceGroup(group) {
|
|
20
|
-
this._currentSourceGroup = group ?? null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
getCurrentSourceGroup() {
|
|
24
|
-
return this._currentSourceGroup;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
addGlobalMiddleware() {
|
|
28
|
-
if (!arguments) return;
|
|
29
|
-
|
|
30
|
-
const middlewares = [...arguments];
|
|
31
|
-
const validMiddlewares = middlewares.filter(isMiddlewareValid);
|
|
32
|
-
this.globalMiddlewares = this.globalMiddlewares.concat(validMiddlewares);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @param {Array || Object} targets
|
|
37
|
-
*/
|
|
38
|
-
register(targets) {
|
|
39
|
-
if (Array.isArray(targets)) {
|
|
40
|
-
this.targets = this.targets.concat(targets);
|
|
41
|
-
} else {
|
|
42
|
-
this.targets.push(targets);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Matches an endpoint URL to a registered target, supporting parameterized routes.
|
|
48
|
-
*
|
|
49
|
-
* @param {string} endpoint - The endpoint URL to match
|
|
50
|
-
* @returns {Object|null} An object with `target` and `params`, or null if no match
|
|
51
|
-
*/
|
|
52
|
-
aim(endpoint) {
|
|
53
|
-
const standardizedEndpoint = standardizePath(endpoint);
|
|
54
|
-
|
|
55
|
-
// First, try exact match (most specific)
|
|
56
|
-
const exactMatch = this.targets.find((target) => {
|
|
57
|
-
return target.getPath() === standardizedEndpoint;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
if (exactMatch) {
|
|
61
|
-
return { target: exactMatch, params: {} };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Then, try parameterized route matching
|
|
65
|
-
for (const target of this.targets) {
|
|
66
|
-
const targetPath = target.getPath();
|
|
67
|
-
const params = this.matchParameterizedRoute(
|
|
68
|
-
targetPath,
|
|
69
|
-
standardizedEndpoint,
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
if (params !== null) {
|
|
73
|
-
return { target, params };
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Matches a parameterized route pattern against an actual URL.
|
|
82
|
-
*
|
|
83
|
-
* @param {string} pattern - The route pattern (e.g., '/api/categories/:id')
|
|
84
|
-
* @param {string} url - The actual URL to match (e.g., '/api/categories/123')
|
|
85
|
-
* @returns {Object|null} An object with extracted parameters, or null if no match
|
|
86
|
-
*/
|
|
87
|
-
matchParameterizedRoute(pattern, url) {
|
|
88
|
-
// Handle root path case
|
|
89
|
-
if (pattern === '/' && url === '/') {
|
|
90
|
-
return {};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Split both pattern and URL into segments
|
|
94
|
-
const patternSegments = pattern.split('/').filter((s) => s.length > 0);
|
|
95
|
-
const urlSegments = url.split('/').filter((s) => s.length > 0);
|
|
96
|
-
|
|
97
|
-
// Must have same number of segments
|
|
98
|
-
if (patternSegments.length !== urlSegments.length) {
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// If both are empty (root paths), they match
|
|
103
|
-
if (patternSegments.length === 0 && urlSegments.length === 0) {
|
|
104
|
-
return {};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const params = {};
|
|
108
|
-
|
|
109
|
-
// Match each segment
|
|
110
|
-
for (let i = 0; i < patternSegments.length; i++) {
|
|
111
|
-
const patternSegment = patternSegments[i];
|
|
112
|
-
const urlSegment = urlSegments[i];
|
|
113
|
-
|
|
114
|
-
// If it's a parameter (starts with :)
|
|
115
|
-
if (patternSegment.startsWith(':')) {
|
|
116
|
-
const paramName = patternSegment.slice(1); // Remove the ':'
|
|
117
|
-
params[paramName] = urlSegment;
|
|
118
|
-
} else if (patternSegment !== urlSegment) {
|
|
119
|
-
// If it's not a parameter and doesn't match, no match
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return params;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Get all registered endpoints.
|
|
129
|
-
*
|
|
130
|
-
* @param {boolean|{ detailed?: boolean, grouped?: boolean }} [options] - If boolean, treated as grouped (backward compat).
|
|
131
|
-
* If object: detailed=true returns full metadata per endpoint; grouped=true returns paths grouped by first segment.
|
|
132
|
-
* @returns {string[]|Object|Array<{ path: string, metadata: object|null, handler: function }>}
|
|
133
|
-
*/
|
|
134
|
-
getAllEndpoints(options = {}) {
|
|
135
|
-
const grouped =
|
|
136
|
-
typeof options === 'boolean' ? options : (options && options.grouped);
|
|
137
|
-
const detailed =
|
|
138
|
-
typeof options === 'object' && options && options.detailed === true;
|
|
139
|
-
|
|
140
|
-
if (detailed) {
|
|
141
|
-
return this.targets.map((t) => ({
|
|
142
|
-
path: t.getPath(),
|
|
143
|
-
metadata: t.getMetadata(),
|
|
144
|
-
handler: t.getHandler(),
|
|
145
|
-
}));
|
|
146
|
-
}
|
|
147
|
-
if (grouped) {
|
|
148
|
-
return this.targets.reduce((acc, target) => {
|
|
149
|
-
const group = target.getPath().split('/')[1];
|
|
150
|
-
if (!acc[group]) acc[group] = [];
|
|
151
|
-
acc[group].push(target.getPath());
|
|
152
|
-
return acc;
|
|
153
|
-
}, {});
|
|
154
|
-
}
|
|
155
|
-
return this.targets.map((target) => target.getPath());
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const targetRegistry = new TargetRegistry();
|
|
160
|
-
export default targetRegistry;
|
|
1
|
+
import isMiddlewareValid from './middleware-validator.js';
|
|
2
|
+
import { standardizePath } from './path-validator.js';
|
|
3
|
+
|
|
4
|
+
class TargetRegistry {
|
|
5
|
+
constructor() {
|
|
6
|
+
if (TargetRegistry.instance) {
|
|
7
|
+
return TargetRegistry.instance;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
TargetRegistry.instance = this;
|
|
11
|
+
|
|
12
|
+
// TODO - Add a default target
|
|
13
|
+
this.targets = [];
|
|
14
|
+
this.globalMiddlewares = [];
|
|
15
|
+
/** Current source group (target file id) set by loader before importing a target file. */
|
|
16
|
+
this._currentSourceGroup = null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
setCurrentSourceGroup(group) {
|
|
20
|
+
this._currentSourceGroup = group ?? null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
getCurrentSourceGroup() {
|
|
24
|
+
return this._currentSourceGroup;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
addGlobalMiddleware() {
|
|
28
|
+
if (!arguments) return;
|
|
29
|
+
|
|
30
|
+
const middlewares = [...arguments];
|
|
31
|
+
const validMiddlewares = middlewares.filter(isMiddlewareValid);
|
|
32
|
+
this.globalMiddlewares = this.globalMiddlewares.concat(validMiddlewares);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {Array || Object} targets
|
|
37
|
+
*/
|
|
38
|
+
register(targets) {
|
|
39
|
+
if (Array.isArray(targets)) {
|
|
40
|
+
this.targets = this.targets.concat(targets);
|
|
41
|
+
} else {
|
|
42
|
+
this.targets.push(targets);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Matches an endpoint URL to a registered target, supporting parameterized routes.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} endpoint - The endpoint URL to match
|
|
50
|
+
* @returns {Object|null} An object with `target` and `params`, or null if no match
|
|
51
|
+
*/
|
|
52
|
+
aim(endpoint) {
|
|
53
|
+
const standardizedEndpoint = standardizePath(endpoint);
|
|
54
|
+
|
|
55
|
+
// First, try exact match (most specific)
|
|
56
|
+
const exactMatch = this.targets.find((target) => {
|
|
57
|
+
return target.getPath() === standardizedEndpoint;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (exactMatch) {
|
|
61
|
+
return { target: exactMatch, params: {} };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Then, try parameterized route matching
|
|
65
|
+
for (const target of this.targets) {
|
|
66
|
+
const targetPath = target.getPath();
|
|
67
|
+
const params = this.matchParameterizedRoute(
|
|
68
|
+
targetPath,
|
|
69
|
+
standardizedEndpoint,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (params !== null) {
|
|
73
|
+
return { target, params };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Matches a parameterized route pattern against an actual URL.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} pattern - The route pattern (e.g., '/api/categories/:id')
|
|
84
|
+
* @param {string} url - The actual URL to match (e.g., '/api/categories/123')
|
|
85
|
+
* @returns {Object|null} An object with extracted parameters, or null if no match
|
|
86
|
+
*/
|
|
87
|
+
matchParameterizedRoute(pattern, url) {
|
|
88
|
+
// Handle root path case
|
|
89
|
+
if (pattern === '/' && url === '/') {
|
|
90
|
+
return {};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Split both pattern and URL into segments
|
|
94
|
+
const patternSegments = pattern.split('/').filter((s) => s.length > 0);
|
|
95
|
+
const urlSegments = url.split('/').filter((s) => s.length > 0);
|
|
96
|
+
|
|
97
|
+
// Must have same number of segments
|
|
98
|
+
if (patternSegments.length !== urlSegments.length) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// If both are empty (root paths), they match
|
|
103
|
+
if (patternSegments.length === 0 && urlSegments.length === 0) {
|
|
104
|
+
return {};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const params = {};
|
|
108
|
+
|
|
109
|
+
// Match each segment
|
|
110
|
+
for (let i = 0; i < patternSegments.length; i++) {
|
|
111
|
+
const patternSegment = patternSegments[i];
|
|
112
|
+
const urlSegment = urlSegments[i];
|
|
113
|
+
|
|
114
|
+
// If it's a parameter (starts with :)
|
|
115
|
+
if (patternSegment.startsWith(':')) {
|
|
116
|
+
const paramName = patternSegment.slice(1); // Remove the ':'
|
|
117
|
+
params[paramName] = urlSegment;
|
|
118
|
+
} else if (patternSegment !== urlSegment) {
|
|
119
|
+
// If it's not a parameter and doesn't match, no match
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return params;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get all registered endpoints.
|
|
129
|
+
*
|
|
130
|
+
* @param {boolean|{ detailed?: boolean, grouped?: boolean }} [options] - If boolean, treated as grouped (backward compat).
|
|
131
|
+
* If object: detailed=true returns full metadata per endpoint; grouped=true returns paths grouped by first segment.
|
|
132
|
+
* @returns {string[]|Object|Array<{ path: string, metadata: object|null, handler: function }>}
|
|
133
|
+
*/
|
|
134
|
+
getAllEndpoints(options = {}) {
|
|
135
|
+
const grouped =
|
|
136
|
+
typeof options === 'boolean' ? options : (options && options.grouped);
|
|
137
|
+
const detailed =
|
|
138
|
+
typeof options === 'object' && options && options.detailed === true;
|
|
139
|
+
|
|
140
|
+
if (detailed) {
|
|
141
|
+
return this.targets.map((t) => ({
|
|
142
|
+
path: t.getPath(),
|
|
143
|
+
metadata: t.getMetadata(),
|
|
144
|
+
handler: t.getHandler(),
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
if (grouped) {
|
|
148
|
+
return this.targets.reduce((acc, target) => {
|
|
149
|
+
const group = target.getPath().split('/')[1];
|
|
150
|
+
if (!acc[group]) acc[group] = [];
|
|
151
|
+
acc[group].push(target.getPath());
|
|
152
|
+
return acc;
|
|
153
|
+
}, {});
|
|
154
|
+
}
|
|
155
|
+
return this.targets.map((target) => target.getPath());
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const targetRegistry = new TargetRegistry();
|
|
160
|
+
export default targetRegistry;
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import TejLogger from 'tej-logger';
|
|
2
|
-
import Ammo from '../ammo.js';
|
|
3
|
-
const logger = new TejLogger('ShootValidator');
|
|
4
|
-
|
|
5
|
-
const isShootValid = (shoot) => {
|
|
6
|
-
if (typeof shoot !== 'function') {
|
|
7
|
-
logger.error(`Shoot ${shoot} should be a function. Skipping...`);
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// Shoot should have 1 parameter, and it must be an instance of Ammo
|
|
12
|
-
if (shoot.length !== 1) {
|
|
13
|
-
logger.error(`Shoot function must have 1 parameter. Skipping...`);
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return true;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export default isShootValid;
|
|
1
|
+
import TejLogger from 'tej-logger';
|
|
2
|
+
import Ammo from '../ammo.js';
|
|
3
|
+
const logger = new TejLogger('ShootValidator');
|
|
4
|
+
|
|
5
|
+
const isShootValid = (shoot) => {
|
|
6
|
+
if (typeof shoot !== 'function') {
|
|
7
|
+
logger.error(`Shoot ${shoot} should be a function. Skipping...`);
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Shoot should have 1 parameter, and it must be an instance of Ammo
|
|
12
|
+
if (shoot.length !== 1) {
|
|
13
|
+
logger.error(`Shoot function must have 1 parameter. Skipping...`);
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
return true;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default isShootValid;
|