@cocreate/lazy-loader 1.23.0 → 1.23.1
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/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/server.js +704 -570
package/src/server.js
CHANGED
|
@@ -1,592 +1,726 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const path = require(
|
|
3
|
-
const { URL } = require(
|
|
4
|
-
const vm = require(
|
|
1
|
+
const fs = require("fs").promises;
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { URL } = require("url");
|
|
4
|
+
const vm = require("vm");
|
|
5
5
|
const Config = require("@cocreate/config");
|
|
6
|
-
const { getValueFromObject } = require(
|
|
7
|
-
|
|
6
|
+
const { getValueFromObject } = require("@cocreate/utils");
|
|
8
7
|
|
|
9
8
|
class CoCreateLazyLoader {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
9
|
+
constructor(server, crud, files) {
|
|
10
|
+
this.server = server;
|
|
11
|
+
this.wsManager = crud.wsManager;
|
|
12
|
+
this.crud = crud;
|
|
13
|
+
this.files = files;
|
|
14
|
+
this.exclusion = { ...require.cache };
|
|
15
|
+
this.modules = {};
|
|
16
|
+
this.init();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async init() {
|
|
20
|
+
const scriptsDirectory = "./scripts";
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
await fs.mkdir(scriptsDirectory, { recursive: true });
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error("Error creating scripts directory:", error);
|
|
26
|
+
throw error; // Halt execution if directory creation fails
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.modules = await Config("modules", false, false);
|
|
30
|
+
if (!this.modules) return;
|
|
31
|
+
else this.modules = this.modules.modules;
|
|
32
|
+
|
|
33
|
+
for (let name of Object.keys(this.modules)) {
|
|
34
|
+
this.wsManager.on(this.modules[name].event, async (data) => {
|
|
35
|
+
this.executeScriptWithTimeout(name, data);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.server.https.on("request", (req, res) => this.request(req, res));
|
|
40
|
+
this.server.http.on("request", (req, res) => this.request(req, res));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async request(req, res) {
|
|
44
|
+
try {
|
|
45
|
+
// TODO: track usage
|
|
46
|
+
const valideUrl = new URL(`http://${req.headers.host}${req.url}`);
|
|
47
|
+
const hostname = valideUrl.hostname;
|
|
48
|
+
let organization;
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
organization = await this.crud.getOrganization({
|
|
52
|
+
host: hostname
|
|
53
|
+
});
|
|
54
|
+
} catch {
|
|
55
|
+
return this.files.send(
|
|
56
|
+
req,
|
|
57
|
+
res,
|
|
58
|
+
this.crud,
|
|
59
|
+
organization,
|
|
60
|
+
valideUrl
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (valideUrl.pathname.startsWith("/webhooks/")) {
|
|
65
|
+
let name = req.url.split("/")[2]; // Assuming URL structure is /webhooks/name/...
|
|
66
|
+
if (this.modules[name]) {
|
|
67
|
+
this.executeScriptWithTimeout(name, {
|
|
68
|
+
req,
|
|
69
|
+
res,
|
|
70
|
+
host: hostname,
|
|
71
|
+
organization,
|
|
72
|
+
valideUrl,
|
|
73
|
+
organization_id: organization._id
|
|
74
|
+
});
|
|
75
|
+
} else {
|
|
76
|
+
// Handle unknown module or missing webhook method
|
|
77
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
78
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
this.files.send(req, res, this.crud, organization, valideUrl);
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
85
|
+
res.end("Invalid host format");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async executeScriptWithTimeout(name, data) {
|
|
90
|
+
try {
|
|
91
|
+
if (
|
|
92
|
+
this.modules[name].initialize ||
|
|
93
|
+
this.modules[name].initialize === ""
|
|
94
|
+
) {
|
|
95
|
+
if (data.req)
|
|
96
|
+
data = await this.webhooks(this.modules[name], data, name);
|
|
97
|
+
else data = await this.api(this.modules[name], data);
|
|
98
|
+
} else {
|
|
99
|
+
if (!this.modules[name].content) {
|
|
100
|
+
if (this.modules[name].path)
|
|
101
|
+
this.modules[name].content = await require(this.modules[
|
|
102
|
+
name
|
|
103
|
+
].path);
|
|
104
|
+
else {
|
|
105
|
+
try {
|
|
106
|
+
const scriptPath = path.join(
|
|
107
|
+
scriptsDirectory,
|
|
108
|
+
`${name}.js`
|
|
109
|
+
);
|
|
110
|
+
await fs.access(scriptPath);
|
|
111
|
+
this.modules[name].content = await fs.readFile(
|
|
112
|
+
scriptPath,
|
|
113
|
+
"utf8"
|
|
114
|
+
);
|
|
115
|
+
} catch {
|
|
116
|
+
this.modules[name].content =
|
|
117
|
+
await fetchScriptFromDatabaseAndSave(
|
|
118
|
+
name,
|
|
119
|
+
this.modules[name],
|
|
120
|
+
data
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (this.modules[name].content) {
|
|
127
|
+
data.apis = await this.getApiKey(data, name);
|
|
128
|
+
data.crud = this.crud;
|
|
129
|
+
data = await this.modules[name].content.send(data);
|
|
130
|
+
delete data.apis;
|
|
131
|
+
delete data.crud;
|
|
132
|
+
} else return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (data.socket) this.wsManager.send(data);
|
|
136
|
+
|
|
137
|
+
if (
|
|
138
|
+
this.modules[name].unload === false ||
|
|
139
|
+
this.modules[name].unload === "false"
|
|
140
|
+
)
|
|
141
|
+
return;
|
|
142
|
+
else if (
|
|
143
|
+
this.modules[name].unload === true ||
|
|
144
|
+
this.modules[name].unload === "true"
|
|
145
|
+
)
|
|
146
|
+
console.log("config should unload after completeion ");
|
|
147
|
+
else if (
|
|
148
|
+
(this.modules[name].unload = parseInt(
|
|
149
|
+
this.modules[name].unload,
|
|
150
|
+
10
|
|
151
|
+
))
|
|
152
|
+
) {
|
|
153
|
+
// Check if the script is already loaded
|
|
154
|
+
if (this.modules[name].timeout) {
|
|
155
|
+
clearTimeout(this.modules[name].timeout);
|
|
156
|
+
} else if (!this.modules[name].path) {
|
|
157
|
+
// Execute the script
|
|
158
|
+
this.modules[name].context = new vm.createContext({});
|
|
159
|
+
const script = new vm.Script(this.modules[name].context);
|
|
160
|
+
script.runInContext(context);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Reset or set the timeout
|
|
164
|
+
const timeout = setTimeout(() => {
|
|
165
|
+
// delete this.modules[name]
|
|
166
|
+
delete this.modules[name].timeout;
|
|
167
|
+
delete this.modules[name].context;
|
|
168
|
+
delete this.modules[name].content;
|
|
169
|
+
console.log(`Module ${name} removed due to inactivity.`);
|
|
170
|
+
clearModuleCache(name);
|
|
171
|
+
}, this.modules[name].unload);
|
|
172
|
+
|
|
173
|
+
this.modules[name].timeout = timeout;
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
data.error = error.message;
|
|
177
|
+
if (data.req) {
|
|
178
|
+
data.res.writeHead(400, { "Content-Type": "text/plain" });
|
|
179
|
+
data.res.end(`Lazyload Error: ${error.message}`);
|
|
180
|
+
}
|
|
181
|
+
if (data.socket) this.wsManager.send(data);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* TODO: Implement Enhanced API Configuration Handling
|
|
187
|
+
*
|
|
188
|
+
* Description:
|
|
189
|
+
* - Implement functionality to dynamically handle API configurations, supporting both complete and base URL endpoints with automatic method-based path appending.
|
|
190
|
+
* - Enable dynamic generation of query parameters from a designated object (`stripe` in the examples) when `query` is true.
|
|
191
|
+
*
|
|
192
|
+
* Requirements:
|
|
193
|
+
* 1. Dynamic Endpoint Handling:
|
|
194
|
+
* - Check if the endpoint configuration is a complete URL or a base URL.
|
|
195
|
+
* - If the `method` derived path is not already included in the endpoint, append it dynamically.
|
|
196
|
+
* Example:
|
|
197
|
+
* `{ "method": "stripe.accounts.retrieve", "endpoint": "https://api.stripe.com", "query": true, "stripe": { "acct": "acct_123", "name": "John Doe" } }`
|
|
198
|
+
* `{ "method": "stripe.accounts.retrieve", "endpoint": "https://api.stripe.com/accounts/retrieve", "query": true, "stripe": { "acct": "acct_123", "name": "John Doe" } }`
|
|
199
|
+
* - Develop logic to parse the `method` and check against the endpoint. If necessary, append the appropriate API method segment.
|
|
200
|
+
*
|
|
201
|
+
* 2. Query Parameter Handling:
|
|
202
|
+
* - Dynamically construct and append query parameters from the `stripe` object if `query` is true. Ensure proper URL-encoding of keys and values.
|
|
203
|
+
*
|
|
204
|
+
* 3. Security:
|
|
205
|
+
* - Use the `method` for permission checks, ensuring that each API request complies with security protocols.
|
|
206
|
+
*
|
|
207
|
+
* 4. Testing:
|
|
208
|
+
* - Test both scenarios where the endpoint may or may not include the method path to ensure the dynamic construction works correctly.
|
|
209
|
+
* - Ensure that all query parameters are correctly formatted and appended.
|
|
210
|
+
*
|
|
211
|
+
* Notes:
|
|
212
|
+
* - Consider utility functions for parsing and modifying URLs, as well as for encoding parameters.
|
|
213
|
+
* - Maintain clear and detailed documentation for each part of the implementation to assist future development and troubleshooting.
|
|
214
|
+
*/
|
|
215
|
+
|
|
216
|
+
async api(config, data) {
|
|
217
|
+
try {
|
|
218
|
+
const methodPath = data.method.split(".");
|
|
219
|
+
const name = methodPath.shift();
|
|
220
|
+
|
|
221
|
+
const apis = await this.getApiKey(data, name);
|
|
222
|
+
|
|
223
|
+
const key = apis.key;
|
|
224
|
+
if (!key)
|
|
225
|
+
throw new Error(
|
|
226
|
+
`Missing ${name} key in organization apis object`
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
// ToDo: if data.endpoint service not required as endpoint will be used
|
|
230
|
+
let instance;
|
|
231
|
+
|
|
232
|
+
// Try using require() first, for CommonJS modules
|
|
233
|
+
try {
|
|
234
|
+
instance = require(config.path); // Attempt to require the module
|
|
235
|
+
} catch (err) {
|
|
236
|
+
if (err.code === "ERR_REQUIRE_ESM") {
|
|
237
|
+
// If it's an ESM module, fallback to dynamic import()
|
|
238
|
+
instance = await import(config.path);
|
|
239
|
+
} else {
|
|
240
|
+
throw err; // Re-throw other errors
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (config.initialize) {
|
|
245
|
+
if (Array.isArray(config.initialize)) {
|
|
246
|
+
const initializations = [];
|
|
247
|
+
for (let i = 0; i < config.initialize.length; i++) {
|
|
248
|
+
const initialize = config.initialize[i].split(".");
|
|
249
|
+
initializations.push(instance);
|
|
250
|
+
// Traverse the nested structure to reach the correct constructor
|
|
251
|
+
for (let j = 0; j < initialize.length; j++) {
|
|
252
|
+
if (initializations[i][initialize[j]]) {
|
|
253
|
+
initializations[i] =
|
|
254
|
+
initializations[i][initialize[j]];
|
|
255
|
+
} else {
|
|
256
|
+
throw new Error(
|
|
257
|
+
`Service path ${config.initialize[i]} is incorrect at ${initialize[j]}`
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
instance = new initializations[1](
|
|
263
|
+
new initializations[0](key)
|
|
264
|
+
);
|
|
265
|
+
} else {
|
|
266
|
+
const initialize = config.initialize.split(".");
|
|
267
|
+
// Traverse the nested structure to reach the correct constructor
|
|
268
|
+
for (let i = 0; i < initialize.length; i++) {
|
|
269
|
+
if (instance[initialize[i]]) {
|
|
270
|
+
instance = instance[initialize[i]];
|
|
271
|
+
} else {
|
|
272
|
+
throw new Error(
|
|
273
|
+
`Service path ${config.initialize} is incorrect at ${initialize[i]}`
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// instance = new instance(key);
|
|
279
|
+
}
|
|
280
|
+
// else
|
|
281
|
+
instance = new instance(key);
|
|
282
|
+
|
|
283
|
+
let params = [],
|
|
284
|
+
mainParam = false;
|
|
285
|
+
for (let i = 0; true; i++) {
|
|
286
|
+
if (`$param[${i}]` in data[name]) {
|
|
287
|
+
params.push(data[name][`$param[${i}]`]);
|
|
288
|
+
delete data[name][`$param[${i}]`];
|
|
289
|
+
} else if (!mainParam) {
|
|
290
|
+
params.push(data[name]);
|
|
291
|
+
mainParam = true;
|
|
292
|
+
} else {
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// TODO: should run processOperators before in order to perform complex opertions and get data, will need to loop back on permission in order to authenticate and autorize
|
|
298
|
+
// data[name] = await processOperators(data, null, data[name]);
|
|
299
|
+
|
|
300
|
+
// data[name] = await processOperators(data, null, data[name]);
|
|
301
|
+
|
|
302
|
+
// execute = await this.processOperators(data, event, execute);
|
|
303
|
+
|
|
304
|
+
data[name] = await executeMethod(
|
|
305
|
+
data.method,
|
|
306
|
+
methodPath,
|
|
307
|
+
instance,
|
|
308
|
+
params
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
// TODO: should run processOperators after in order to perform complex opertions and get data
|
|
312
|
+
// data[name] = await processOperators(data, data[name]);
|
|
313
|
+
|
|
314
|
+
return data;
|
|
315
|
+
} catch (error) {
|
|
316
|
+
data.error = error.message;
|
|
317
|
+
return data;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async webhooks(config, data, name) {
|
|
322
|
+
try {
|
|
323
|
+
const apis = await this.getApiKey(data, name);
|
|
324
|
+
|
|
325
|
+
const key = apis.key;
|
|
326
|
+
if (!key)
|
|
327
|
+
throw new Error(
|
|
328
|
+
`Missing ${name} key in organization apis object`
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
let webhookName = data.req.url.split("/");
|
|
332
|
+
webhookName = webhookName[webhookName.length - 1];
|
|
333
|
+
|
|
334
|
+
const webhook = apis.webhooks[webhookName];
|
|
335
|
+
if (!webhook)
|
|
336
|
+
throw new Error(
|
|
337
|
+
`Webhook ${name} ${webhookName} is not defined`
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// eventDataKey is used to access the event data
|
|
341
|
+
let eventDataKey = webhook.eventDataKey || apis.eventDataKey;
|
|
342
|
+
if (!eventDataKey)
|
|
343
|
+
throw new Error(`Webhook ${name} eventKey is not defined`);
|
|
344
|
+
|
|
345
|
+
// eventNameKey is used to access the event the event name
|
|
346
|
+
let eventNameKey = webhook.eventNameKey || apis.eventNameKey;
|
|
347
|
+
if (!eventNameKey)
|
|
348
|
+
throw new Error(`Webhook ${name} eventNameKey is not defined`);
|
|
349
|
+
|
|
350
|
+
if (!webhook.events)
|
|
351
|
+
throw new Error(`Webhook ${name} events are not defined`);
|
|
352
|
+
|
|
353
|
+
data.rawBody = "";
|
|
354
|
+
await new Promise((resolve, reject) => {
|
|
355
|
+
data.req.on("data", (chunk) => {
|
|
356
|
+
data.rawBody += chunk.toString();
|
|
357
|
+
});
|
|
358
|
+
data.req.on("end", () => {
|
|
359
|
+
resolve();
|
|
360
|
+
});
|
|
361
|
+
data.req.on("error", (err) => {
|
|
362
|
+
reject(err);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
let parameters, method;
|
|
367
|
+
|
|
368
|
+
if (webhook.authenticate && webhook.authenticate.method) {
|
|
369
|
+
method = webhook.authenticate.method;
|
|
370
|
+
} else if (apis.authenticate && apis.authenticate.method) {
|
|
371
|
+
method = apis.authenticate.method;
|
|
372
|
+
} else
|
|
373
|
+
throw new Error(
|
|
374
|
+
`Webhook ${name} authenticate method is not defined`
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
if (webhook.authenticate && webhook.authenticate.parameters) {
|
|
378
|
+
parameters = webhook.authenticate.parameters;
|
|
379
|
+
} else if (apis.authenticate && apis.authenticate.parameters) {
|
|
380
|
+
parameters = apis.authenticate.parameters;
|
|
381
|
+
} else
|
|
382
|
+
throw new Error(
|
|
383
|
+
`Webhook ${name} authenticate parameters is not defined`
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
// TODO: webhook secert could be a key pair
|
|
387
|
+
|
|
388
|
+
let event;
|
|
389
|
+
if (!method) {
|
|
390
|
+
if (!parameters[0] !== parameters[1])
|
|
391
|
+
throw new Error(
|
|
392
|
+
`Webhook secret failed for ${name}. Unauthorized access attempt.`
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
event = JSON.parse(data.rawBody);
|
|
396
|
+
} else {
|
|
397
|
+
const service = require(config.path);
|
|
398
|
+
let instance;
|
|
399
|
+
if (config.initialize)
|
|
400
|
+
instance = new service[config.initialize](key);
|
|
401
|
+
else instance = new service(key);
|
|
402
|
+
|
|
403
|
+
const methodPath = method.split(".");
|
|
404
|
+
|
|
405
|
+
await this.processOperators(data, "", parameters);
|
|
406
|
+
|
|
407
|
+
event = await executeMethod(
|
|
408
|
+
method,
|
|
409
|
+
methodPath,
|
|
410
|
+
instance,
|
|
411
|
+
parameters
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
let eventName = getValueFromObject(event, eventNameKey);
|
|
416
|
+
if (!eventName)
|
|
417
|
+
throw new Error(
|
|
418
|
+
`Webhook ${name} eventNameKey: ${eventNameKey} could not be found in the event.`
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
let eventData = getValueFromObject(event, eventDataKey);
|
|
422
|
+
if (!eventData)
|
|
423
|
+
throw new Error(
|
|
424
|
+
`Webhook ${name} eventDataKey: ${eventDataKey} could not be found in the event.`
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
let execute = webhook.events[eventName];
|
|
428
|
+
if (execute) {
|
|
429
|
+
execute = await this.processOperators(data, event, execute);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
data.res.writeHead(200, { "Content-Type": "application/json" });
|
|
433
|
+
data.res.end(
|
|
434
|
+
JSON.stringify({ message: "Webhook received and processed" })
|
|
435
|
+
);
|
|
436
|
+
return data;
|
|
437
|
+
} catch (error) {
|
|
438
|
+
data.error = error.message;
|
|
439
|
+
data.res.writeHead(400, { "Content-Type": "text/plain" });
|
|
440
|
+
data.res.end(error.message);
|
|
441
|
+
return data;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
async processOperators(data, event, execute) {
|
|
446
|
+
if (Array.isArray(execute)) {
|
|
447
|
+
for (let index = 0; index < execute.length; index++) {
|
|
448
|
+
execute[index] = await this.processOperators(
|
|
449
|
+
data,
|
|
450
|
+
event,
|
|
451
|
+
execute[index]
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
} else if (typeof execute === "object" && execute !== null) {
|
|
455
|
+
for (let key of Object.keys(execute)) {
|
|
456
|
+
if (
|
|
457
|
+
key.startsWith("$") &&
|
|
458
|
+
!["$storage", "$database", "$array", "$filter"].includes(
|
|
459
|
+
key
|
|
460
|
+
)
|
|
461
|
+
) {
|
|
462
|
+
execute[key] = await this.processOperator(
|
|
463
|
+
data,
|
|
464
|
+
event,
|
|
465
|
+
key,
|
|
466
|
+
execute[key]
|
|
467
|
+
);
|
|
468
|
+
} else if (
|
|
469
|
+
typeof execute[key] === "string" &&
|
|
470
|
+
execute[key].startsWith("$") &&
|
|
471
|
+
!["$storage", "$database", "$array", "$filter"].includes(
|
|
472
|
+
execute[key]
|
|
473
|
+
)
|
|
474
|
+
) {
|
|
475
|
+
execute[key] = await this.processOperator(
|
|
476
|
+
data,
|
|
477
|
+
event,
|
|
478
|
+
execute[key]
|
|
479
|
+
);
|
|
480
|
+
} else if (Array.isArray(execute[key])) {
|
|
481
|
+
execute[key] = await this.processOperators(
|
|
482
|
+
data,
|
|
483
|
+
event,
|
|
484
|
+
execute[key]
|
|
485
|
+
);
|
|
486
|
+
} else if (
|
|
487
|
+
typeof execute[key] === "object" &&
|
|
488
|
+
execute[key] !== null
|
|
489
|
+
) {
|
|
490
|
+
execute[key] = await this.processOperators(
|
|
491
|
+
data,
|
|
492
|
+
event,
|
|
493
|
+
execute[key]
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
} else if (
|
|
498
|
+
typeof execute === "string" &&
|
|
499
|
+
execute.startsWith("$") &&
|
|
500
|
+
!["$storage", "$database", "$array", "$filter"].includes(execute)
|
|
501
|
+
) {
|
|
502
|
+
execute = await this.processOperator(data, event, execute);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return execute;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
async processOperator(data, event, operator, context) {
|
|
509
|
+
let result;
|
|
510
|
+
if (operator.startsWith("$data.")) {
|
|
511
|
+
result = getValueFromObject(data, operator.substring(6));
|
|
512
|
+
return getValueFromObject(data, operator.substring(6));
|
|
513
|
+
} else if (operator.startsWith("$req")) {
|
|
514
|
+
return getValueFromObject(data, operator.substring(1));
|
|
515
|
+
} else if (operator.startsWith("$header")) {
|
|
516
|
+
return getValueFromObject(data.req, operator.substring(1));
|
|
517
|
+
} else if (operator.startsWith("$rawBody")) {
|
|
518
|
+
return getValueFromObject(data, operator.substring(1));
|
|
519
|
+
} else if (operator.startsWith("$crud")) {
|
|
520
|
+
let results = context;
|
|
521
|
+
let isObject = false;
|
|
522
|
+
if (!Array.isArray(results)) {
|
|
523
|
+
isObject = true;
|
|
524
|
+
results = [results];
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
for (let i = 0; i < results.length; i++) {
|
|
528
|
+
results[i] = await this.processOperators(
|
|
529
|
+
data,
|
|
530
|
+
event,
|
|
531
|
+
results[i]
|
|
532
|
+
);
|
|
533
|
+
results[i] = await this.crud.send(results[i]);
|
|
534
|
+
if (operator.startsWith("$crud."))
|
|
535
|
+
results[i] = getValueFromObject(
|
|
536
|
+
operator,
|
|
537
|
+
operator.substring(6)
|
|
538
|
+
);
|
|
539
|
+
results[i] = await this.processOperators(
|
|
540
|
+
data,
|
|
541
|
+
event,
|
|
542
|
+
results[i]
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (isObject) results = results[0];
|
|
547
|
+
|
|
548
|
+
return results;
|
|
549
|
+
} else if (operator.startsWith("$socket")) {
|
|
550
|
+
context = await this.processOperators(data, event, context);
|
|
551
|
+
result = await this.socket.send(context);
|
|
552
|
+
if (operator.startsWith("$socket."))
|
|
553
|
+
result = getValueFromObject(operator, operator.substring(6));
|
|
554
|
+
return await this.processOperators(data, event, result);
|
|
555
|
+
} else if (operator.startsWith("$api")) {
|
|
556
|
+
context = await this.processOperators(data, event, context);
|
|
557
|
+
let name = context.method.split(".")[0];
|
|
558
|
+
result = this.executeScriptWithTimeout(name, context);
|
|
559
|
+
if (operator.startsWith("$api."))
|
|
560
|
+
result = getValueFromObject(event, operator.substring(5));
|
|
561
|
+
return await this.processOperators(data, event, result);
|
|
562
|
+
} else if (operator.startsWith("$event")) {
|
|
563
|
+
if (operator.startsWith("$event."))
|
|
564
|
+
result = getValueFromObject(event, operator.substring(7));
|
|
565
|
+
return await this.processOperators(data, event, result);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return operator;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
async getApiKey(data, name) {
|
|
572
|
+
let organization = await this.crud.getOrganization(data);
|
|
573
|
+
if (organization.error) throw new Error(organization.error);
|
|
574
|
+
if (!organization.apis)
|
|
575
|
+
throw new Error("Missing apis object in organization object");
|
|
576
|
+
if (!organization.apis[name])
|
|
577
|
+
throw new Error(`Missing ${name} in organization apis object`);
|
|
578
|
+
return organization.apis[name];
|
|
579
|
+
}
|
|
462
580
|
}
|
|
463
581
|
|
|
464
582
|
async function executeMethod(method, methodPath, instance, params) {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
583
|
+
try {
|
|
584
|
+
switch (methodPath.length) {
|
|
585
|
+
case 1:
|
|
586
|
+
return await instance[methodPath[0]](...params);
|
|
587
|
+
case 2:
|
|
588
|
+
return await instance[methodPath[0]][methodPath[1]](...params);
|
|
589
|
+
case 3:
|
|
590
|
+
return await instance[methodPath[0]][methodPath[1]][
|
|
591
|
+
methodPath[2]
|
|
592
|
+
](...params);
|
|
593
|
+
case 4:
|
|
594
|
+
return await instance[methodPath[0]][methodPath[1]][
|
|
595
|
+
methodPath[2]
|
|
596
|
+
][methodPath[3]](...params);
|
|
597
|
+
case 5:
|
|
598
|
+
return await instance[methodPath[0]][methodPath[1]][
|
|
599
|
+
methodPath[2]
|
|
600
|
+
][methodPath[3]][methodPath[4]](...params);
|
|
601
|
+
case 6:
|
|
602
|
+
return await instance[methodPath[0]][methodPath[1]][
|
|
603
|
+
methodPath[2]
|
|
604
|
+
][methodPath[3]][methodPath[4]][methodPath[5]](...params);
|
|
605
|
+
case 7:
|
|
606
|
+
return await instance[methodPath[0]][methodPath[1]][
|
|
607
|
+
methodPath[2]
|
|
608
|
+
][methodPath[3]][methodPath[4]][methodPath[5]][methodPath[6]](
|
|
609
|
+
...params
|
|
610
|
+
);
|
|
611
|
+
case 8:
|
|
612
|
+
return await instance[methodPath[0]][methodPath[1]][
|
|
613
|
+
methodPath[2]
|
|
614
|
+
][methodPath[3]][methodPath[4]][methodPath[5]][methodPath[6]][
|
|
615
|
+
methodPath[7]
|
|
616
|
+
](...params);
|
|
617
|
+
default:
|
|
618
|
+
const methodName = methodPath.pop();
|
|
619
|
+
let Method = instance;
|
|
620
|
+
for (let i = 0; i < methodPath.length; i++) {
|
|
621
|
+
Method = Method[methodPath[i]];
|
|
622
|
+
if (Method === undefined) {
|
|
623
|
+
throw new Error(
|
|
624
|
+
`Method ${methodPath[i]} not found using ${method}.`
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (typeof Method[methodName] !== "function")
|
|
630
|
+
throw new Error(`Method ${method} is not a function.`);
|
|
631
|
+
|
|
632
|
+
return await Method[methodName](...params);
|
|
633
|
+
}
|
|
634
|
+
} catch (error) {
|
|
635
|
+
throw new Error(error);
|
|
636
|
+
}
|
|
501
637
|
}
|
|
502
638
|
|
|
503
639
|
function getModuleDependencies(modulePath) {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
640
|
+
let moduleObj = require.cache[modulePath];
|
|
641
|
+
if (!moduleObj) {
|
|
642
|
+
return [];
|
|
643
|
+
}
|
|
508
644
|
|
|
509
|
-
|
|
510
|
-
|
|
645
|
+
// Get all child module paths
|
|
646
|
+
return moduleObj.children.map((child) => child.id);
|
|
511
647
|
}
|
|
512
648
|
|
|
513
649
|
function isModuleUsedElsewhere(modulePath, name) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
650
|
+
return Object.keys(require.cache).some((path) => {
|
|
651
|
+
const moduleObj = require.cache[path];
|
|
652
|
+
// return moduleObj.children.some(child => child.id === modulePath && path !== modulePath);
|
|
653
|
+
return moduleObj.children.some((child) => {
|
|
654
|
+
// let test = child.id === modulePath && path !== modulePath
|
|
655
|
+
// if (test)
|
|
656
|
+
// return test
|
|
657
|
+
return child.id === modulePath && path !== modulePath;
|
|
658
|
+
});
|
|
659
|
+
});
|
|
524
660
|
}
|
|
525
661
|
|
|
526
662
|
function clearModuleCache(name) {
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
663
|
+
try {
|
|
664
|
+
const modulePath = require.resolve(name);
|
|
665
|
+
const dependencies = getModuleDependencies(modulePath);
|
|
666
|
+
|
|
667
|
+
// Check if the module is a dependency of other modules
|
|
668
|
+
// const moduleObj = require.cache[modulePath];
|
|
669
|
+
// if (moduleObj && moduleObj.parent) {
|
|
670
|
+
// console.log(`Module ${name} is a dependency of other modules.`);
|
|
671
|
+
// return;
|
|
672
|
+
// }
|
|
673
|
+
|
|
674
|
+
// Check if the module is used by other modules
|
|
675
|
+
if (isModuleUsedElsewhere(modulePath, name)) {
|
|
676
|
+
console.log(`Module ${name} is a dependency of other modules.`);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Remove the module from the cache
|
|
681
|
+
delete require.cache[modulePath];
|
|
682
|
+
console.log(`Module ${name} has been removed from cache.`);
|
|
683
|
+
// Recursively clear dependencies from cache
|
|
684
|
+
dependencies.forEach((depPath) => {
|
|
685
|
+
clearModuleCache(depPath);
|
|
686
|
+
});
|
|
687
|
+
} catch (error) {
|
|
688
|
+
console.error(
|
|
689
|
+
`Error clearing module cache for ${name}: ${error.message}`
|
|
690
|
+
);
|
|
691
|
+
}
|
|
555
692
|
}
|
|
556
693
|
|
|
557
694
|
// Function to fetch script from database and save to disk
|
|
558
695
|
async function fetchScriptFromDatabaseAndSave(name, moduleConfig) {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
696
|
+
let data = {
|
|
697
|
+
method: "object.read",
|
|
698
|
+
host: moduleConfig.object.hostname,
|
|
699
|
+
array: moduleConfig.array,
|
|
700
|
+
$filter: {
|
|
701
|
+
query: {
|
|
702
|
+
host: { $in: [moduleConfig.object.hostname, "*"] },
|
|
703
|
+
pathname: moduleConfig.object.pathname
|
|
704
|
+
},
|
|
705
|
+
limit: 1
|
|
706
|
+
},
|
|
707
|
+
organization_id
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
let file = await this.crud.send(data);
|
|
711
|
+
let src;
|
|
712
|
+
|
|
713
|
+
if (file && file.object && file.object[0]) {
|
|
714
|
+
src = file.object[0].src;
|
|
715
|
+
} else {
|
|
716
|
+
throw new Error("Script not found in database");
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Save to disk for future use
|
|
720
|
+
const scriptPath = path.join(scriptsDirectory, `${name}.js`);
|
|
721
|
+
await fs.writeFile(scriptPath, src);
|
|
722
|
+
|
|
723
|
+
return src;
|
|
587
724
|
}
|
|
588
725
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
module.exports = CoCreateLazyLoader;
|
|
726
|
+
module.exports = CoCreateLazyLoader;
|