serverless-plugin-warmup 6.2.0 → 7.1.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.
- package/README.md +7 -4
- package/package.json +2 -2
- package/src/config.js +6 -4
- package/src/index.js +39 -25
- package/src/schema.js +3 -0
- package/src/warmer.js +13 -9
package/README.md
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
[![Serverless][serverless-badge]](serverless-badge-url)
|
|
3
3
|
[![npm version][npm-version-badge]][npm-version-badge-url]
|
|
4
4
|
[![npm monthly downloads][npm-downloads-badge]][npm-version-badge-url]
|
|
5
|
-
[](https://github.com/juanjoDiaz/serverless-plugin-warmup/actions/workflows/on-push.yaml)
|
|
6
6
|
[![Coverage Status][coveralls-badge]][coveralls-badge-url]
|
|
7
7
|
[](https://raw.githubusercontent.com/juanjoDiaz/serverless-plugin-warmup/master/LICENSE)
|
|
8
8
|
|
|
9
9
|
Keep your lambdas warm during winter.
|
|
10
10
|
|
|
11
11
|
**Requirements:**
|
|
12
|
-
*
|
|
12
|
+
* Node *v14.x* or higher
|
|
13
|
+
* Serverless *v3.x* or higher
|
|
13
14
|
* AWS provider
|
|
14
15
|
|
|
15
16
|
## How it works
|
|
@@ -43,6 +44,7 @@ custom:
|
|
|
43
44
|
events:
|
|
44
45
|
- schedule: cron(0/5 8-17 ? * MON-FRI *)
|
|
45
46
|
concurrency: 10
|
|
47
|
+
verbose: true
|
|
46
48
|
logRetentionInDays: 10
|
|
47
49
|
outOfOfficeHoursWarmer:
|
|
48
50
|
enabled: true
|
|
@@ -51,6 +53,7 @@ custom:
|
|
|
51
53
|
- schedule: cron(0/5 18-23 ? * MON-FRI *)
|
|
52
54
|
- schedule: cron(0/5 * ? * SAT-SUN *)
|
|
53
55
|
concurrency: 1
|
|
56
|
+
verbose: false
|
|
54
57
|
testWarmer:
|
|
55
58
|
enabled: false
|
|
56
59
|
```
|
|
@@ -69,6 +72,7 @@ The options are the same for all the warmers:
|
|
|
69
72
|
* **timeout** How many seconds until the warmer lambda times out. (defaults to `10`)
|
|
70
73
|
* **environment** Can be used to set environment variables in the warmer lambda. You can also unset variables configured at the provider by setting them to undefined. However, you should almost never have to change the default. (defaults to unset all package level environment variables. )
|
|
71
74
|
* **tracing** Specify whether to enable/disable tracing at the function level. When tracing is enabled, warmer functions will use NPM to install the X-Ray client and use it to trace requests (It takes any of the values supported by serverless as `boolean`, `Active`or `PassThrough` and defaults to the provider-level setting)
|
|
75
|
+
* **verbose** If set to false, it disables the console.logs placed on this warmer lambda (defaults to `true`)
|
|
72
76
|
* **logRetentionInDays** Set the retention time in days for the log group associated to this warmer lamba
|
|
73
77
|
* **prewarm** If set to true, it warms up your lambdas right after deploying (defaults to `false`)
|
|
74
78
|
|
|
@@ -105,6 +109,7 @@ custom:
|
|
|
105
109
|
- ./**
|
|
106
110
|
timeout: 20
|
|
107
111
|
tracing: true
|
|
112
|
+
verbose: false # Disable the logs
|
|
108
113
|
logRetentionInDays: 10
|
|
109
114
|
prewarm: true # Run WarmUp immediately after a deploymentlambda
|
|
110
115
|
clientContext:
|
|
@@ -603,8 +608,6 @@ This software is released under the MIT license. See [the license file](LICENSE)
|
|
|
603
608
|
[npm-version-badge]: https://badge.fury.io/js/serverless-plugin-warmup.svg
|
|
604
609
|
[npm-version-badge-url]: https://www.npmjs.com/package/serverless-plugin-warmup
|
|
605
610
|
[npm-downloads-badge]: https://img.shields.io/npm/dm/serverless-plugin-warmup.svg
|
|
606
|
-
[travis-badge]: https://travis-ci.org/juanjoDiaz/serverless-plugin-warmup.svg
|
|
607
|
-
[travis-badge-url]: https://travis-ci.org/juanjoDiaz/serverless-plugin-warmup
|
|
608
611
|
[coveralls-badge]: https://coveralls.io/repos/juanjoDiaz/serverless-plugin-warmup/badge.svg?branch=master
|
|
609
612
|
[coveralls-badge-url]: https://coveralls.io/r/juanjoDiaz/serverless-plugin-warmup?branch=master
|
|
610
613
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serverless-plugin-warmup",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.1.0",
|
|
4
4
|
"description": "Keep your lambdas warm during winter.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -35,6 +35,6 @@
|
|
|
35
35
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
36
36
|
"eslint-plugin-import": "^2.24.2",
|
|
37
37
|
"husky": "^7.0.2",
|
|
38
|
-
"jest": "^27.
|
|
38
|
+
"jest": "^27.5.0"
|
|
39
39
|
}
|
|
40
40
|
}
|
package/src/config.js
CHANGED
|
@@ -41,6 +41,7 @@ function getWarmerConfig(config, defaultOpts) {
|
|
|
41
41
|
? config.environment
|
|
42
42
|
: defaultOpts.environment,
|
|
43
43
|
tracing: (config.tracing !== undefined) ? config.tracing : defaultOpts.tracing,
|
|
44
|
+
verbose: (config.verbose !== undefined) ? config.verbose : defaultOpts.verbose,
|
|
44
45
|
logRetentionInDays: (config.logRetentionInDays !== undefined)
|
|
45
46
|
? config.logRetentionInDays
|
|
46
47
|
: defaultOpts.logRetentionInDays,
|
|
@@ -82,7 +83,7 @@ function getFunctionConfig(config, defaultOpts) {
|
|
|
82
83
|
*
|
|
83
84
|
* @return {Array} - List of functions to be warmed up and their specific configs
|
|
84
85
|
* */
|
|
85
|
-
function getFunctionsByWarmer(service, stage, configsByWarmer) {
|
|
86
|
+
function getFunctionsByWarmer(service, stage, configsByWarmer, serverlessClasses) {
|
|
86
87
|
const functions = service.getAllFunctions()
|
|
87
88
|
.map((name) => service.getFunction(name))
|
|
88
89
|
.map((config) => {
|
|
@@ -100,7 +101,7 @@ function getFunctionsByWarmer(service, stage, configsByWarmer) {
|
|
|
100
101
|
const unknownWarmers = Object.keys(config.warmup)
|
|
101
102
|
.filter((warmerName) => configsByWarmer[warmerName] === undefined);
|
|
102
103
|
if (unknownWarmers.length > 0) {
|
|
103
|
-
throw new Error(`WarmUp: Invalid function-level warmup configuration (${unknownWarmers.join(', ')}) in function ${config.name}. Every warmer should be declared in the custom section.`);
|
|
104
|
+
throw new serverlessClasses.Error(`WarmUp: Invalid function-level warmup configuration (${unknownWarmers.join(', ')}) in function ${config.name}. Every warmer should be declared in the custom section.`);
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
return {
|
|
@@ -137,7 +138,7 @@ function getFunctionsByWarmer(service, stage, configsByWarmer) {
|
|
|
137
138
|
*
|
|
138
139
|
* @return {Object} - Configuration options to be used by the plugin
|
|
139
140
|
* */
|
|
140
|
-
function getConfigsByWarmer(service, stage) {
|
|
141
|
+
function getConfigsByWarmer({ service, classes }, stage) {
|
|
141
142
|
const getWarmerDefaultOpts = (warmerName) => ({
|
|
142
143
|
folderName: path.join('.warmup', warmerName),
|
|
143
144
|
cleanFolder: true,
|
|
@@ -151,6 +152,7 @@ function getConfigsByWarmer(service, stage) {
|
|
|
151
152
|
timeout: 10,
|
|
152
153
|
environment: Object.keys(service.provider.environment || [])
|
|
153
154
|
.reduce((obj, k) => ({ ...obj, [k]: undefined }), {}),
|
|
155
|
+
verbose: true,
|
|
154
156
|
prewarm: false,
|
|
155
157
|
});
|
|
156
158
|
|
|
@@ -170,7 +172,7 @@ function getConfigsByWarmer(service, stage) {
|
|
|
170
172
|
},
|
|
171
173
|
}), {});
|
|
172
174
|
|
|
173
|
-
const functionsByWarmer = getFunctionsByWarmer(service, stage, configsByWarmer);
|
|
175
|
+
const functionsByWarmer = getFunctionsByWarmer(service, stage, configsByWarmer, classes);
|
|
174
176
|
|
|
175
177
|
return Object.entries(configsByWarmer).reduce((warmers, [warmerName, warmerConfig]) => ({
|
|
176
178
|
...warmers,
|
package/src/index.js
CHANGED
|
@@ -28,12 +28,13 @@ class WarmUp {
|
|
|
28
28
|
* @constructor
|
|
29
29
|
*
|
|
30
30
|
* @param {!Object} serverless - Serverless object
|
|
31
|
-
* @param {!Object}
|
|
31
|
+
* @param {!Object} cliOptions - Serverless cliOptions
|
|
32
32
|
* */
|
|
33
|
-
constructor(serverless,
|
|
33
|
+
constructor(serverless, cliOptions, { log }) {
|
|
34
34
|
/** Serverless variables */
|
|
35
35
|
this.serverless = serverless;
|
|
36
|
-
this.
|
|
36
|
+
this.cliOptions = cliOptions;
|
|
37
|
+
this.log = log;
|
|
37
38
|
|
|
38
39
|
this.provider = this.serverless.getProvider('aws');
|
|
39
40
|
|
|
@@ -46,7 +47,7 @@ class WarmUp {
|
|
|
46
47
|
cleanupTempDir: { lifecycleEvents: ['cleanup'] },
|
|
47
48
|
prewarm: {
|
|
48
49
|
lifecycleEvents: ['start', 'end'],
|
|
49
|
-
|
|
50
|
+
cliOptions: {
|
|
50
51
|
warmers: {
|
|
51
52
|
shortcut: 'w',
|
|
52
53
|
usage: 'Comma-separated list of warmer names to prewarm.',
|
|
@@ -70,8 +71,8 @@ class WarmUp {
|
|
|
70
71
|
'warmup:cleanupTempDir:cleanup': this.cleanUp.bind(this),
|
|
71
72
|
'before:warmup:prewarm:start': this.configPlugin.bind(this),
|
|
72
73
|
'warmup:prewarm:start': this.prewarmFunctions.bind(this),
|
|
73
|
-
// Workaround webpack/bundle plugins
|
|
74
|
-
'before:package:createDeploymentArtifacts': this.
|
|
74
|
+
// Workaround webpack/bundle plugins and serverless_sdk
|
|
75
|
+
'before:package:createDeploymentArtifacts': this.resetWarmerConfigs.bind(this),
|
|
75
76
|
};
|
|
76
77
|
|
|
77
78
|
// Fixed for issues in Serverles
|
|
@@ -85,7 +86,7 @@ class WarmUp {
|
|
|
85
86
|
configPlugin() {
|
|
86
87
|
this.stage = this.stage || this.provider.getStage();
|
|
87
88
|
this.configsByWarmer = this.configsByWarmer
|
|
88
|
-
|| getConfigsByWarmer(this.serverless
|
|
89
|
+
|| getConfigsByWarmer(this.serverless, this.stage);
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
/**
|
|
@@ -101,6 +102,23 @@ class WarmUp {
|
|
|
101
102
|
.map(([warmerName, warmerConfig]) => this.configureWarmer(warmerName, warmerConfig)));
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
/**
|
|
106
|
+
* @description Workaround webpack/bundle plugins and serverless_sdk.
|
|
107
|
+
* Reset the plugin and ignore changes
|
|
108
|
+
*
|
|
109
|
+
* @fulfil {} — Warm up set
|
|
110
|
+
* @reject {Error} Warm up error
|
|
111
|
+
*
|
|
112
|
+
* @return {Promise}
|
|
113
|
+
* */
|
|
114
|
+
async resetWarmerConfigs() {
|
|
115
|
+
Object.entries(this.configsByWarmer)
|
|
116
|
+
.forEach(([warmerName, warmerConfig]) => {
|
|
117
|
+
if (warmerConfig.functions.length === 0) return;
|
|
118
|
+
addWarmUpFunctionToService(this.serverless.service, warmerName, warmerConfig);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
104
122
|
/**
|
|
105
123
|
* @description Warmup cleanup hook.
|
|
106
124
|
*
|
|
@@ -122,7 +140,7 @@ class WarmUp {
|
|
|
122
140
|
);
|
|
123
141
|
} catch (err) {
|
|
124
142
|
if (err.code !== 'ENOENT') {
|
|
125
|
-
this.
|
|
143
|
+
this.log.error(`WarmUp: Couldn't clean up temporary folder ${folderToClean}.`);
|
|
126
144
|
}
|
|
127
145
|
}
|
|
128
146
|
}));
|
|
@@ -137,7 +155,7 @@ class WarmUp {
|
|
|
137
155
|
}
|
|
138
156
|
} catch (err) {
|
|
139
157
|
if (err.code !== 'ENOENT') {
|
|
140
|
-
this.
|
|
158
|
+
this.log.error('WarmUp: Couldn\'t clean up temporary folder .warmup.');
|
|
141
159
|
}
|
|
142
160
|
}
|
|
143
161
|
}
|
|
@@ -151,8 +169,8 @@ class WarmUp {
|
|
|
151
169
|
* @return {Promise}
|
|
152
170
|
* */
|
|
153
171
|
async prewarmFunctions() {
|
|
154
|
-
const warmerNames = (this.
|
|
155
|
-
? this.
|
|
172
|
+
const warmerNames = (this.cliOptions.warmers)
|
|
173
|
+
? this.cliOptions.warmers.split(',')
|
|
156
174
|
: Object.entries(this.configsByWarmer)
|
|
157
175
|
.filter(([, warmerConfig]) => warmerConfig.prewarm)
|
|
158
176
|
.map(([warmerName]) => warmerName);
|
|
@@ -160,7 +178,7 @@ class WarmUp {
|
|
|
160
178
|
await Promise.all(warmerNames.map(async (warmerName) => {
|
|
161
179
|
const warmerConfig = this.configsByWarmer[warmerName];
|
|
162
180
|
if (!warmerConfig) {
|
|
163
|
-
throw new Error(`Warmer names ${warmerName} doesn't exist.`);
|
|
181
|
+
throw new this.serverless.classes.Error(`Warmer names ${warmerName} doesn't exist.`);
|
|
164
182
|
}
|
|
165
183
|
addWarmUpFunctionToService(this.serverless.service, warmerName, warmerConfig);
|
|
166
184
|
await this.invokeWarmer(warmerName, warmerConfig);
|
|
@@ -173,24 +191,20 @@ class WarmUp {
|
|
|
173
191
|
* */
|
|
174
192
|
async configureWarmer(warmerName, warmerConfig) {
|
|
175
193
|
if (warmerConfig.functions.length === 0) {
|
|
176
|
-
this.
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Avoid double processing due to the workaround for webpack/bundle plugins
|
|
181
|
-
// resetting the plugin and ignoring changes
|
|
182
|
-
if (this.serverless.service.functions[`warmUpPlugin${capitalize(warmerName)}`]) {
|
|
194
|
+
this.log.warning(`WarmUp: Skipping warmer "${warmerName}" creation. No functions to warm up.`);
|
|
183
195
|
return;
|
|
184
196
|
}
|
|
185
197
|
|
|
186
|
-
this.
|
|
187
|
-
|
|
198
|
+
this.log.notice(`WarmUp: Creating warmer "${warmerName}" to warm up ${warmerConfig.functions.length} function${warmerConfig.functions.length === 1 ? '' : 's'}`);
|
|
199
|
+
this.log.info(':');
|
|
200
|
+
warmerConfig.functions.forEach((func) => this.log.info(` * ${func.name}`));
|
|
188
201
|
|
|
189
202
|
const handlerFolder = path.join(this.serviceDir, warmerConfig.folderName);
|
|
190
203
|
|
|
191
204
|
await createWarmUpFunctionArtifact(
|
|
192
205
|
warmerConfig.functions,
|
|
193
206
|
warmerConfig.tracing,
|
|
207
|
+
warmerConfig.verbose,
|
|
194
208
|
this.provider.getRegion(),
|
|
195
209
|
handlerFolder,
|
|
196
210
|
);
|
|
@@ -209,11 +223,11 @@ class WarmUp {
|
|
|
209
223
|
|
|
210
224
|
async invokeWarmer(warmerName, warmerConfig) {
|
|
211
225
|
if (warmerConfig.functions.length === 0) {
|
|
212
|
-
this.
|
|
226
|
+
this.log.warning(`WarmUp: Skipping prewarming using warmer "${warmerName}". No functions to warm up.`);
|
|
213
227
|
return;
|
|
214
228
|
}
|
|
215
229
|
|
|
216
|
-
this.
|
|
230
|
+
this.log.notice(`WarmUp: Prewarming up your functions using warmer "${warmerName}".`);
|
|
217
231
|
|
|
218
232
|
try {
|
|
219
233
|
const { SERVERLESS_ALIAS } = this.serverless.service.getFunction(`warmUpPlugin${capitalize(warmerName)}`).environment || {};
|
|
@@ -226,9 +240,9 @@ class WarmUp {
|
|
|
226
240
|
};
|
|
227
241
|
|
|
228
242
|
await this.provider.request('Lambda', 'invoke', params);
|
|
229
|
-
this.
|
|
243
|
+
this.log.notice(`WarmUp: Warmer "${warmerName}" successfully prewarmed your functions.`);
|
|
230
244
|
} catch (err) {
|
|
231
|
-
this.
|
|
245
|
+
this.log.error(`WarmUp: Error while prewarming your functions using warmer "${warmerName}".`, err);
|
|
232
246
|
}
|
|
233
247
|
}
|
|
234
248
|
}
|
package/src/schema.js
CHANGED
|
@@ -107,6 +107,7 @@ function extendServerlessSchema(serverless) {
|
|
|
107
107
|
timeout: { $ref: '#/definitions/awsLambdaTimeout' },
|
|
108
108
|
environment: { $ref: '#/definitions/awsLambdaEnvironment' },
|
|
109
109
|
tracing: { $ref: '#/definitions/awsLambdaTracing' },
|
|
110
|
+
verbose: { type: 'boolean' },
|
|
110
111
|
logRetentionInDays: {
|
|
111
112
|
type: 'number',
|
|
112
113
|
enum: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653],
|
|
@@ -139,6 +140,7 @@ function extendServerlessSchema(serverless) {
|
|
|
139
140
|
serverless.configSchemaHandler.defineCustomProperties({
|
|
140
141
|
properties: {
|
|
141
142
|
warmup: {
|
|
143
|
+
type: 'object',
|
|
142
144
|
patternProperties: {
|
|
143
145
|
'.*': {
|
|
144
146
|
type: 'object',
|
|
@@ -156,6 +158,7 @@ function extendServerlessSchema(serverless) {
|
|
|
156
158
|
type: 'object',
|
|
157
159
|
properties: {
|
|
158
160
|
warmup: {
|
|
161
|
+
type: 'object',
|
|
159
162
|
patternProperties: {
|
|
160
163
|
'.*': {
|
|
161
164
|
type: 'object',
|
package/src/warmer.js
CHANGED
|
@@ -94,7 +94,7 @@ function addWarmUpFunctionRoleToResources(service, stage, warmerName, warmerConf
|
|
|
94
94
|
'lambda:InvokeFunction',
|
|
95
95
|
],
|
|
96
96
|
Resource: warmerConfig.functions.map((fn) => ({
|
|
97
|
-
'Fn::Sub': `arn:\${AWS::Partition}:lambda:\${AWS::Region}:\${AWS::AccountId}:function:${fn.name}
|
|
97
|
+
'Fn::Sub': `arn:\${AWS::Partition}:lambda:\${AWS::Region}:\${AWS::AccountId}:function:${fn.name}*`,
|
|
98
98
|
})),
|
|
99
99
|
},
|
|
100
100
|
{
|
|
@@ -125,7 +125,7 @@ function addWarmUpFunctionRoleToResources(service, stage, warmerName, warmerConf
|
|
|
125
125
|
*
|
|
126
126
|
* @return {Promise}
|
|
127
127
|
* */
|
|
128
|
-
async function createWarmUpFunctionArtifact(functions, tracing, region, handlerFolder) {
|
|
128
|
+
async function createWarmUpFunctionArtifact(functions, tracing, verbose, region, handlerFolder) {
|
|
129
129
|
const warmUpFunction = `'use strict';
|
|
130
130
|
|
|
131
131
|
/** Generated by Serverless WarmUp Plugin **/
|
|
@@ -143,28 +143,32 @@ const lambda = new AWS.Lambda({
|
|
|
143
143
|
});
|
|
144
144
|
const functions = ${JSON.stringify(functions, null, ' ')};
|
|
145
145
|
|
|
146
|
+
function logVerbose(str) {
|
|
147
|
+
${verbose ? 'console.log(str);' : ''}
|
|
148
|
+
}
|
|
149
|
+
|
|
146
150
|
function getConcurrency(func, envVars) {
|
|
147
151
|
const functionConcurrency = envVars[\`WARMUP_CONCURRENCY_\${func.name.toUpperCase().replace(/-/g, '_')}\`];
|
|
148
152
|
|
|
149
153
|
if (functionConcurrency) {
|
|
150
154
|
const concurrency = parseInt(functionConcurrency);
|
|
151
|
-
|
|
155
|
+
logVerbose(\`Warming up function: \${func.name} with concurrency: \${concurrency} (from function-specific environment variable)\`);
|
|
152
156
|
return concurrency;
|
|
153
157
|
}
|
|
154
158
|
|
|
155
159
|
if (envVars.WARMUP_CONCURRENCY) {
|
|
156
160
|
const concurrency = parseInt(envVars.WARMUP_CONCURRENCY);
|
|
157
|
-
|
|
161
|
+
logVerbose(\`Warming up function: \${func.name} with concurrency: \${concurrency} (from global environment variable)\`);
|
|
158
162
|
return concurrency;
|
|
159
163
|
}
|
|
160
164
|
|
|
161
165
|
const concurrency = parseInt(func.config.concurrency);
|
|
162
|
-
|
|
166
|
+
logVerbose(\`Warming up function: \${func.name} with concurrency: \${concurrency}\`);
|
|
163
167
|
return concurrency;
|
|
164
168
|
}
|
|
165
169
|
|
|
166
170
|
module.exports.warmUp = async (event, context) => {
|
|
167
|
-
|
|
171
|
+
logVerbose('Warm Up Start');
|
|
168
172
|
|
|
169
173
|
const invokes = await Promise.all(functions.map(async (func) => {
|
|
170
174
|
const concurrency = getConcurrency(func, process.env);
|
|
@@ -186,15 +190,15 @@ module.exports.warmUp = async (event, context) => {
|
|
|
186
190
|
|
|
187
191
|
try {
|
|
188
192
|
await Promise.all(Array(concurrency).fill(0).map(async () => await lambda.invoke(params).promise()));
|
|
189
|
-
|
|
193
|
+
logVerbose(\`Warm Up Invoke Success: \${func.name}\`);
|
|
190
194
|
return true;
|
|
191
195
|
} catch (e) {
|
|
192
|
-
console.
|
|
196
|
+
console.error(\`Warm Up Invoke Error: \${func.name}\`, e);
|
|
193
197
|
return false;
|
|
194
198
|
}
|
|
195
199
|
}));
|
|
196
200
|
|
|
197
|
-
|
|
201
|
+
logVerbose(\`Warm Up Finished with \${invokes.filter(r => !r).length} invoke errors\`);
|
|
198
202
|
}`;
|
|
199
203
|
|
|
200
204
|
/** Write warm up file */
|