spice-js 2.7.1 → 2.7.3
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/build/index.js +5 -1
- package/build/models/SpiceModel.js +156 -83
- package/build/utility/ResourceReloader.js +426 -0
- package/package.json +1 -1
- package/src/index.js +17 -15
- package/src/models/SpiceModel.js +79 -34
- package/src/utility/ResourceReloader.js +333 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.reloadSchema = reloadSchema;
|
|
5
|
+
exports.reloadModel = reloadModel;
|
|
6
|
+
exports.reloadController = reloadController;
|
|
7
|
+
exports.reloadCache = reloadCache;
|
|
8
|
+
exports.registerRoute = registerRoute;
|
|
9
|
+
exports.reloadResource = reloadResource;
|
|
10
|
+
exports.removeResourceFromMemory = removeResourceFromMemory;
|
|
11
|
+
exports.default = void 0;
|
|
12
|
+
|
|
13
|
+
var _path = _interopRequireDefault(require("path"));
|
|
14
|
+
|
|
15
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
16
|
+
|
|
17
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
18
|
+
|
|
19
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
20
|
+
|
|
21
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* ResourceReloader - Utility for dynamically reloading resources at runtime
|
|
25
|
+
* without requiring a full application restart.
|
|
26
|
+
*
|
|
27
|
+
* This enables zero-downtime updates for:
|
|
28
|
+
* - Schema updates
|
|
29
|
+
* - Resource creation
|
|
30
|
+
* - Resource deletion (removes from memory, routes orphaned until restart)
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Clear the Node.js require cache for a specific file path
|
|
35
|
+
* @param {string} filePath - The full path to the file
|
|
36
|
+
*/
|
|
37
|
+
function clearRequireCache(filePath) {
|
|
38
|
+
// Resolve the file path to handle different require formats
|
|
39
|
+
var resolvedPath = require.resolve(filePath);
|
|
40
|
+
|
|
41
|
+
if (require.cache[resolvedPath]) {
|
|
42
|
+
delete require.cache[resolvedPath];
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Reload a schema into memory
|
|
50
|
+
* @param {string} resourceName - The resource name (e.g., "User", "Product")
|
|
51
|
+
* @returns {object} Result with success status and message
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
function reloadSchema(_x) {
|
|
56
|
+
return _reloadSchema.apply(this, arguments);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Reload a model into memory
|
|
60
|
+
* @param {string} resourceName - The resource name (e.g., "User", "Product")
|
|
61
|
+
* @returns {object} Result with success status and message
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
function _reloadSchema() {
|
|
66
|
+
_reloadSchema = _asyncToGenerator(function* (resourceName) {
|
|
67
|
+
try {
|
|
68
|
+
var schemaPath = _path.default.join(spice.root_path, "schemas", resourceName + ".js");
|
|
69
|
+
|
|
70
|
+
if (!_fs.default.existsSync(schemaPath)) {
|
|
71
|
+
return {
|
|
72
|
+
success: false,
|
|
73
|
+
error: "Schema file not found: " + schemaPath
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
clearRequireCache(schemaPath);
|
|
78
|
+
|
|
79
|
+
var imported = require(schemaPath);
|
|
80
|
+
|
|
81
|
+
var schema = imported.default || imported; // Store with both original and lowercase keys
|
|
82
|
+
|
|
83
|
+
spice.schemas[resourceName] = schema;
|
|
84
|
+
spice.schemas[resourceName.toLowerCase()] = schema;
|
|
85
|
+
return {
|
|
86
|
+
success: true,
|
|
87
|
+
message: "Schema " + resourceName + " reloaded"
|
|
88
|
+
};
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
error: "Failed to reload schema: " + error.message
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
return _reloadSchema.apply(this, arguments);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function reloadModel(_x2) {
|
|
100
|
+
return _reloadModel.apply(this, arguments);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Reload a controller into memory
|
|
104
|
+
* @param {string} resourceName - The resource name (e.g., "User", "Product")
|
|
105
|
+
* @returns {object} Result with success status and message
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
function _reloadModel() {
|
|
110
|
+
_reloadModel = _asyncToGenerator(function* (resourceName) {
|
|
111
|
+
try {
|
|
112
|
+
var modelPath = _path.default.join(spice.root_path, "models", resourceName + ".js");
|
|
113
|
+
|
|
114
|
+
if (!_fs.default.existsSync(modelPath)) {
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
error: "Model file not found: " + modelPath
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
clearRequireCache(modelPath);
|
|
122
|
+
|
|
123
|
+
var imported = require(modelPath);
|
|
124
|
+
|
|
125
|
+
var model = imported.default || imported; // Store with both original and lowercase keys
|
|
126
|
+
|
|
127
|
+
spice.models[resourceName] = model;
|
|
128
|
+
spice.models[resourceName.toLowerCase()] = model;
|
|
129
|
+
return {
|
|
130
|
+
success: true,
|
|
131
|
+
message: "Model " + resourceName + " reloaded"
|
|
132
|
+
};
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
error: "Failed to reload model: " + error.message
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return _reloadModel.apply(this, arguments);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function reloadController(_x3) {
|
|
144
|
+
return _reloadController.apply(this, arguments);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Reload a cache into memory
|
|
148
|
+
* @param {string} resourceName - The resource name (e.g., "User", "Product")
|
|
149
|
+
* @returns {object} Result with success status and message
|
|
150
|
+
*/
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
function _reloadController() {
|
|
154
|
+
_reloadController = _asyncToGenerator(function* (resourceName) {
|
|
155
|
+
try {
|
|
156
|
+
var controllerPath = _path.default.join(spice.root_path, "controllers", resourceName + ".js");
|
|
157
|
+
|
|
158
|
+
if (!_fs.default.existsSync(controllerPath)) {
|
|
159
|
+
return {
|
|
160
|
+
success: false,
|
|
161
|
+
error: "Controller file not found: " + controllerPath
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
clearRequireCache(controllerPath);
|
|
166
|
+
|
|
167
|
+
var imported = require(controllerPath);
|
|
168
|
+
|
|
169
|
+
var controller = imported.default || imported; // Store with both original and lowercase keys
|
|
170
|
+
|
|
171
|
+
spice.controllers[resourceName] = controller;
|
|
172
|
+
spice.controllers[resourceName.toLowerCase()] = controller;
|
|
173
|
+
return {
|
|
174
|
+
success: true,
|
|
175
|
+
message: "Controller " + resourceName + " reloaded"
|
|
176
|
+
};
|
|
177
|
+
} catch (error) {
|
|
178
|
+
return {
|
|
179
|
+
success: false,
|
|
180
|
+
error: "Failed to reload controller: " + error.message
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
return _reloadController.apply(this, arguments);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function reloadCache(_x4) {
|
|
188
|
+
return _reloadCache.apply(this, arguments);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Register a new route with the Koa app
|
|
192
|
+
* @param {string} resourceName - The resource name (e.g., "User", "Product")
|
|
193
|
+
* @returns {object} Result with success status and message
|
|
194
|
+
*/
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
function _reloadCache() {
|
|
198
|
+
_reloadCache = _asyncToGenerator(function* (resourceName) {
|
|
199
|
+
try {
|
|
200
|
+
var cachePath = _path.default.join(spice.root_path, "cache", resourceName + ".js");
|
|
201
|
+
|
|
202
|
+
if (!_fs.default.existsSync(cachePath)) {
|
|
203
|
+
return {
|
|
204
|
+
success: false,
|
|
205
|
+
error: "Cache file not found: " + cachePath
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
clearRequireCache(cachePath);
|
|
210
|
+
|
|
211
|
+
var imported = require(cachePath);
|
|
212
|
+
|
|
213
|
+
var cache = imported.default || imported; // Store with both original and lowercase keys
|
|
214
|
+
|
|
215
|
+
spice.cache[resourceName] = cache;
|
|
216
|
+
spice.cache[resourceName.toLowerCase()] = cache;
|
|
217
|
+
return {
|
|
218
|
+
success: true,
|
|
219
|
+
message: "Cache " + resourceName + " reloaded"
|
|
220
|
+
};
|
|
221
|
+
} catch (error) {
|
|
222
|
+
return {
|
|
223
|
+
success: false,
|
|
224
|
+
error: "Failed to reload cache: " + error.message
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
return _reloadCache.apply(this, arguments);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function registerRoute(_x5) {
|
|
232
|
+
return _registerRoute.apply(this, arguments);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Reload all components of a resource (schema, model, controller, cache, route)
|
|
236
|
+
* @param {string} resourceName - The resource name (e.g., "User", "Product")
|
|
237
|
+
* @returns {object} Result with success status, messages, and any errors
|
|
238
|
+
*/
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
function _registerRoute() {
|
|
242
|
+
_registerRoute = _asyncToGenerator(function* (resourceName) {
|
|
243
|
+
try {
|
|
244
|
+
var routePath = _path.default.join(spice.root_path, "routes", resourceName + ".js");
|
|
245
|
+
|
|
246
|
+
if (!_fs.default.existsSync(routePath)) {
|
|
247
|
+
return {
|
|
248
|
+
success: false,
|
|
249
|
+
error: "Route file not found: " + routePath
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
clearRequireCache(routePath);
|
|
254
|
+
|
|
255
|
+
var router = require(routePath); // Handle ESM default exports
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
if (router.default) {
|
|
259
|
+
router = router.default;
|
|
260
|
+
} // Store the router
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
spice.routers[resourceName] = router;
|
|
264
|
+
spice.routers[resourceName.toLowerCase()] = router; // Register with Koa app
|
|
265
|
+
|
|
266
|
+
spice.app.use(router.routes()).use(router.allowedMethods());
|
|
267
|
+
return {
|
|
268
|
+
success: true,
|
|
269
|
+
message: "Route " + resourceName + " registered"
|
|
270
|
+
};
|
|
271
|
+
} catch (error) {
|
|
272
|
+
return {
|
|
273
|
+
success: false,
|
|
274
|
+
error: "Failed to register route: " + error.message
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
return _registerRoute.apply(this, arguments);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function reloadResource(_x6) {
|
|
282
|
+
return _reloadResource.apply(this, arguments);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Remove a resource from memory
|
|
286
|
+
* This removes the resource from spice.models, spice.controllers, spice.schemas, spice.cache
|
|
287
|
+
* Note: Routes cannot be unregistered from Koa, they will remain orphaned until restart
|
|
288
|
+
* @param {string} resourceName - The resource name (e.g., "User", "Product")
|
|
289
|
+
* @returns {object} Result with success status and message
|
|
290
|
+
*/
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
function _reloadResource() {
|
|
294
|
+
_reloadResource = _asyncToGenerator(function* (resourceName) {
|
|
295
|
+
var results = {
|
|
296
|
+
success: true,
|
|
297
|
+
messages: [],
|
|
298
|
+
errors: []
|
|
299
|
+
}; // Reload in order: schema first (dependencies might rely on it)
|
|
300
|
+
|
|
301
|
+
var operations = [{
|
|
302
|
+
name: "schema",
|
|
303
|
+
fn: reloadSchema
|
|
304
|
+
}, {
|
|
305
|
+
name: "model",
|
|
306
|
+
fn: reloadModel
|
|
307
|
+
}, {
|
|
308
|
+
name: "controller",
|
|
309
|
+
fn: reloadController
|
|
310
|
+
}, {
|
|
311
|
+
name: "cache",
|
|
312
|
+
fn: reloadCache
|
|
313
|
+
}, {
|
|
314
|
+
name: "route",
|
|
315
|
+
fn: registerRoute
|
|
316
|
+
}];
|
|
317
|
+
|
|
318
|
+
for (var op of operations) {
|
|
319
|
+
var result = yield op.fn(resourceName);
|
|
320
|
+
|
|
321
|
+
if (result.success) {
|
|
322
|
+
results.messages.push(result.message);
|
|
323
|
+
} else {
|
|
324
|
+
results.errors.push(op.name + ": " + result.error); // Continue anyway - some resources might not have all components
|
|
325
|
+
}
|
|
326
|
+
} // Consider success if at least some components loaded
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
results.success = results.messages.length > 0;
|
|
330
|
+
return results;
|
|
331
|
+
});
|
|
332
|
+
return _reloadResource.apply(this, arguments);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function removeResourceFromMemory(resourceName) {
|
|
336
|
+
var removed = []; // Remove from schemas
|
|
337
|
+
|
|
338
|
+
if (spice.schemas) {
|
|
339
|
+
if (spice.schemas[resourceName]) {
|
|
340
|
+
delete spice.schemas[resourceName];
|
|
341
|
+
removed.push("schema");
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (spice.schemas[resourceName.toLowerCase()]) {
|
|
345
|
+
delete spice.schemas[resourceName.toLowerCase()];
|
|
346
|
+
}
|
|
347
|
+
} // Remove from models
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
if (spice.models) {
|
|
351
|
+
if (spice.models[resourceName]) {
|
|
352
|
+
delete spice.models[resourceName];
|
|
353
|
+
removed.push("model");
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (spice.models[resourceName.toLowerCase()]) {
|
|
357
|
+
delete spice.models[resourceName.toLowerCase()];
|
|
358
|
+
}
|
|
359
|
+
} // Remove from controllers
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
if (spice.controllers) {
|
|
363
|
+
if (spice.controllers[resourceName]) {
|
|
364
|
+
delete spice.controllers[resourceName];
|
|
365
|
+
removed.push("controller");
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (spice.controllers[resourceName.toLowerCase()]) {
|
|
369
|
+
delete spice.controllers[resourceName.toLowerCase()];
|
|
370
|
+
}
|
|
371
|
+
} // Remove from cache
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
if (spice.cache) {
|
|
375
|
+
if (spice.cache[resourceName]) {
|
|
376
|
+
delete spice.cache[resourceName];
|
|
377
|
+
removed.push("cache");
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (spice.cache[resourceName.toLowerCase()]) {
|
|
381
|
+
delete spice.cache[resourceName.toLowerCase()];
|
|
382
|
+
}
|
|
383
|
+
} // Remove from routers (the route middleware remains in Koa but won't have backing)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
if (spice.routers) {
|
|
387
|
+
if (spice.routers[resourceName]) {
|
|
388
|
+
delete spice.routers[resourceName];
|
|
389
|
+
removed.push("router");
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (spice.routers[resourceName.toLowerCase()]) {
|
|
393
|
+
delete spice.routers[resourceName.toLowerCase()];
|
|
394
|
+
}
|
|
395
|
+
} // Also clear from Node.js require cache to prevent stale imports
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
var resourceTypes = ["schemas", "models", "controllers", "cache", "routes"];
|
|
399
|
+
|
|
400
|
+
for (var type of resourceTypes) {
|
|
401
|
+
var filePath = _path.default.join(spice.root_path, type, resourceName + ".js");
|
|
402
|
+
|
|
403
|
+
try {
|
|
404
|
+
clearRequireCache(filePath);
|
|
405
|
+
} catch (e) {// File might not exist, that's ok
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
success: true,
|
|
411
|
+
message: "Resource " + resourceName + " removed from memory",
|
|
412
|
+
removed,
|
|
413
|
+
note: "Routes remain in Koa middleware stack but will fail without backing controller/model. Full cleanup on restart."
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
var _default = {
|
|
418
|
+
reloadSchema,
|
|
419
|
+
reloadModel,
|
|
420
|
+
reloadController,
|
|
421
|
+
reloadCache,
|
|
422
|
+
registerRoute,
|
|
423
|
+
reloadResource,
|
|
424
|
+
removeResourceFromMemory
|
|
425
|
+
};
|
|
426
|
+
exports.default = _default;
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -24,6 +24,8 @@ export { default as Crypt } from "./utility/Crypt";
|
|
|
24
24
|
export { default as Serializer } from "./utility/Serializer";
|
|
25
25
|
|
|
26
26
|
export { default as RestHelper } from "./utility/RestHelper";
|
|
27
|
+
|
|
28
|
+
export { default as ResourceReloader } from "./utility/ResourceReloader";
|
|
27
29
|
import Status from "./utility/Status";
|
|
28
30
|
export { default as Mail } from "./mail/Mail";
|
|
29
31
|
export { default as DataType } from "./utility/DataType";
|
|
@@ -80,9 +82,9 @@ export default class Spice {
|
|
|
80
82
|
|
|
81
83
|
spice.addModifier = function (resource, modifier) {
|
|
82
84
|
spice.mofifiers[resource.toLowerCase()] =
|
|
83
|
-
spice.mofifiers[resource] == undefined
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
spice.mofifiers[resource] == undefined ?
|
|
86
|
+
[modifier]
|
|
87
|
+
: [...spice.mofifiers[resource], modifier];
|
|
86
88
|
};
|
|
87
89
|
|
|
88
90
|
spice.getModifiers = function (resource) {
|
|
@@ -96,14 +98,14 @@ export default class Spice {
|
|
|
96
98
|
/* app._io.on("connection", (sock) => {
|
|
97
99
|
console.log("Connection Up", sock);
|
|
98
100
|
}); */
|
|
99
|
-
|
|
101
|
+
|
|
100
102
|
// ⚡ OPTIMIZED: Load routes and models first, then generate docs lazily
|
|
101
103
|
await require("./loaders").load();
|
|
102
|
-
|
|
104
|
+
|
|
103
105
|
// ⚡ LAZY DOCS: Generate docs in background after startup to not block server
|
|
104
106
|
let docsCache = null;
|
|
105
107
|
let docsGenerating = false;
|
|
106
|
-
|
|
108
|
+
|
|
107
109
|
// Generate docs asynchronously after startup
|
|
108
110
|
const generateDocsInBackground = async () => {
|
|
109
111
|
if (docsCache || docsGenerating) return;
|
|
@@ -114,26 +116,26 @@ export default class Spice {
|
|
|
114
116
|
console.log("API documentation generated successfully");
|
|
115
117
|
} catch (error) {
|
|
116
118
|
console.error("Error generating docs:", error);
|
|
117
|
-
docsCache = {
|
|
118
|
-
swagger: "2.0",
|
|
119
|
-
info: { title: "API Docs", version: "1.0.0" },
|
|
120
|
-
paths: {},
|
|
121
|
-
definitions: {}
|
|
119
|
+
docsCache = {
|
|
120
|
+
swagger: "2.0",
|
|
121
|
+
info: { title: "API Docs", version: "1.0.0" },
|
|
122
|
+
paths: {},
|
|
123
|
+
definitions: {},
|
|
122
124
|
};
|
|
123
125
|
} finally {
|
|
124
126
|
docsGenerating = false;
|
|
125
127
|
}
|
|
126
128
|
};
|
|
127
|
-
|
|
129
|
+
|
|
128
130
|
// Start generating docs in background (non-blocking)
|
|
129
131
|
setTimeout(generateDocsInBackground, 100);
|
|
130
|
-
|
|
132
|
+
|
|
131
133
|
// Middleware to serve docs - will wait if still generating
|
|
132
134
|
app.use(async (ctx, next) => {
|
|
133
135
|
if (ctx.path === "/docs/spec" || ctx.path === "/docs/spec.json") {
|
|
134
136
|
// Wait for docs to be generated if not ready
|
|
135
137
|
while (docsGenerating && !docsCache) {
|
|
136
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
138
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
137
139
|
}
|
|
138
140
|
if (!docsCache) {
|
|
139
141
|
await generateDocsInBackground();
|
|
@@ -143,7 +145,7 @@ export default class Spice {
|
|
|
143
145
|
}
|
|
144
146
|
await next();
|
|
145
147
|
});
|
|
146
|
-
|
|
148
|
+
|
|
147
149
|
app.use(
|
|
148
150
|
koaSwagger({
|
|
149
151
|
hideTopbar: true,
|
package/src/models/SpiceModel.js
CHANGED
|
@@ -606,7 +606,10 @@ export default class SpiceModel {
|
|
|
606
606
|
}
|
|
607
607
|
|
|
608
608
|
async getMulti(args) {
|
|
609
|
-
|
|
609
|
+
// ⚡ Profiling: use track() for proper async context forking
|
|
610
|
+
const p = this[_ctx]?.profiler;
|
|
611
|
+
|
|
612
|
+
const doGetMulti = async () => {
|
|
610
613
|
if (!args) {
|
|
611
614
|
args = {};
|
|
612
615
|
}
|
|
@@ -653,6 +656,15 @@ export default class SpiceModel {
|
|
|
653
656
|
}
|
|
654
657
|
|
|
655
658
|
return results;
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
try {
|
|
662
|
+
if (p) {
|
|
663
|
+
return await p.track(`${this.type}.getMulti`, doGetMulti, {
|
|
664
|
+
ids_count: args?.ids?.length || 0,
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
return await doGetMulti();
|
|
656
668
|
} catch (e) {
|
|
657
669
|
console.warn(e.stack);
|
|
658
670
|
throw e;
|
|
@@ -1445,6 +1457,9 @@ export default class SpiceModel {
|
|
|
1445
1457
|
}
|
|
1446
1458
|
|
|
1447
1459
|
async mapToObject(data, Class, source_property, store_property, property) {
|
|
1460
|
+
// ⚡ Get profiler for proper async context forking
|
|
1461
|
+
const p = this[_ctx]?.profiler;
|
|
1462
|
+
|
|
1448
1463
|
let original_is_array = _.isArray(data);
|
|
1449
1464
|
if (!original_is_array) {
|
|
1450
1465
|
data = Array.of(data);
|
|
@@ -1469,21 +1484,36 @@ export default class SpiceModel {
|
|
|
1469
1484
|
`${this[_current_path]}.${source_property}`
|
|
1470
1485
|
: source_property;
|
|
1471
1486
|
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
+
// ⚡ Wrap in profiler track() to ensure proper async context for child operations
|
|
1488
|
+
const fetchRelated = async () => {
|
|
1489
|
+
return await Promise.allSettled(
|
|
1490
|
+
_.map(classes, (obj) => {
|
|
1491
|
+
return new obj({
|
|
1492
|
+
...this[_args],
|
|
1493
|
+
skip_cache: this[_skip_cache],
|
|
1494
|
+
_level: this[_level] + 1,
|
|
1495
|
+
mapping_dept: this[_mapping_dept],
|
|
1496
|
+
mapping_dept_exempt: this[_mapping_dept_exempt],
|
|
1497
|
+
_current_path: childPath,
|
|
1498
|
+
}).getMulti({
|
|
1499
|
+
skip_hooks: true,
|
|
1500
|
+
ids: ids,
|
|
1501
|
+
});
|
|
1502
|
+
})
|
|
1503
|
+
);
|
|
1504
|
+
};
|
|
1505
|
+
|
|
1506
|
+
var returned_all;
|
|
1507
|
+
if (p && ids.length > 0) {
|
|
1508
|
+
returned_all = await p.track(
|
|
1509
|
+
`${this.type}.map.${source_property}`,
|
|
1510
|
+
fetchRelated,
|
|
1511
|
+
{ ids_count: ids.length }
|
|
1512
|
+
);
|
|
1513
|
+
} else {
|
|
1514
|
+
returned_all = await fetchRelated();
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1487
1517
|
let ug = _.flatten(
|
|
1488
1518
|
_.compact(
|
|
1489
1519
|
_.map(returned_all, (returned_obj) => {
|
|
@@ -1511,6 +1541,9 @@ export default class SpiceModel {
|
|
|
1511
1541
|
store_property,
|
|
1512
1542
|
property
|
|
1513
1543
|
) {
|
|
1544
|
+
// ⚡ Get profiler for proper async context forking
|
|
1545
|
+
const p = this[_ctx]?.profiler;
|
|
1546
|
+
|
|
1514
1547
|
let original_is_array = _.isArray(data);
|
|
1515
1548
|
if (!original_is_array) {
|
|
1516
1549
|
data = Array.of(data);
|
|
@@ -1540,21 +1573,36 @@ export default class SpiceModel {
|
|
|
1540
1573
|
: source_property;
|
|
1541
1574
|
|
|
1542
1575
|
let classes = _.compact(_.isArray(Class) ? Class : [Class]);
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1576
|
+
|
|
1577
|
+
// ⚡ Wrap in profiler track() to ensure proper async context for child operations
|
|
1578
|
+
const fetchRelated = async () => {
|
|
1579
|
+
return await Promise.allSettled(
|
|
1580
|
+
_.map(classes, (obj) => {
|
|
1581
|
+
return new obj({
|
|
1582
|
+
...this[_args],
|
|
1583
|
+
skip_cache: this[_skip_cache],
|
|
1584
|
+
_level: this[_level] + 1,
|
|
1585
|
+
mapping_dept: this[_mapping_dept],
|
|
1586
|
+
mapping_dept_exempt: this[_mapping_dept_exempt],
|
|
1587
|
+
_current_path: childPath,
|
|
1588
|
+
}).getMulti({
|
|
1589
|
+
skip_hooks: true,
|
|
1590
|
+
ids: ids,
|
|
1591
|
+
});
|
|
1592
|
+
})
|
|
1593
|
+
);
|
|
1594
|
+
};
|
|
1595
|
+
|
|
1596
|
+
var returned_all;
|
|
1597
|
+
if (p && ids.length > 0) {
|
|
1598
|
+
returned_all = await p.track(
|
|
1599
|
+
`${this.type}.mapArray.${source_property}`,
|
|
1600
|
+
fetchRelated,
|
|
1601
|
+
{ ids_count: ids.length }
|
|
1602
|
+
);
|
|
1603
|
+
} else {
|
|
1604
|
+
returned_all = await fetchRelated();
|
|
1605
|
+
}
|
|
1558
1606
|
|
|
1559
1607
|
var returned_objects = _.flatten(
|
|
1560
1608
|
_.compact(
|
|
@@ -1564,9 +1612,6 @@ export default class SpiceModel {
|
|
|
1564
1612
|
)
|
|
1565
1613
|
);
|
|
1566
1614
|
|
|
1567
|
-
/* let returned_objects = await new Class().getMulti({
|
|
1568
|
-
ids: ids,
|
|
1569
|
-
}); */
|
|
1570
1615
|
_.each(data, (result) => {
|
|
1571
1616
|
if (_.isString(result[store_property])) {
|
|
1572
1617
|
result[store_property] = [result[store_property]];
|