@tachybase/module-worker-thread 0.23.22 → 0.23.40
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/dist/client/index.js +1 -1
- package/dist/externalVersion.js +8 -8
- package/dist/locale/en-US.json +4 -0
- package/dist/locale/zh-CN.json +4 -0
- package/dist/server/worker.js +24 -6
- package/dist/server/workerManager.d.ts +3 -1
- package/dist/server/workerManager.js +41 -8
- package/dist/server/workerTypes.d.ts +2 -0
- package/dist/server/workerWebController.d.ts +1 -0
- package/dist/server/workerWebController.js +10 -2
- package/package.json +10 -10
package/dist/client/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(t,e){typeof exports=="object"&&typeof module!="undefined"?e(exports,require("@tachybase/client"),require("react/jsx-runtime"),require("@tachybase/schema"),require("antd")):typeof define=="function"&&define.amd?define(["exports","@tachybase/client","react/jsx-runtime","@tachybase/schema","antd"],e):(t=typeof globalThis!="undefined"?globalThis:t||self,e(t["@tachybase/module-worker-thread"]={},t["@tachybase/client"],t.jsxRuntime,t["@tachybase/schema"],t.antd))})(this,function(t,e,r,
|
|
1
|
+
(function(t,e){typeof exports=="object"&&typeof module!="undefined"?e(exports,require("@tachybase/client"),require("react/jsx-runtime"),require("@tachybase/schema"),require("antd")):typeof define=="function"&&define.amd?define(["exports","@tachybase/client","react/jsx-runtime","@tachybase/schema","antd"],e):(t=typeof globalThis!="undefined"?globalThis:t||self,e(t["@tachybase/module-worker-thread"]={},t["@tachybase/client"],t.jsxRuntime,t["@tachybase/schema"],t.antd))})(this,function(t,e,r,h,u){"use strict";var T=Object.defineProperty;var x=Object.getOwnPropertySymbols;var F=Object.prototype.hasOwnProperty,S=Object.prototype.propertyIsEnumerable;var b=(t,e,r)=>e in t?T(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,A=(t,e)=>{for(var r in e||(e={}))F.call(e,r)&&b(t,r,e[r]);if(x)for(var r of x(e))S.call(e,r)&&b(t,r,e[r]);return t};var c=(t,e,r)=>new Promise((h,u)=>{var p=n=>{try{o(r.next(n))}catch(l){u(l)}},d=n=>{try{o(r.throw(n))}catch(l){u(l)}},o=n=>n.done?h(n.value):Promise.resolve(n.value).then(p,d);o((r=r.apply(t,e)).next())});const p="worker-thread";function d(){return{t:(i,a={})=>e.i18n.t(i,A({ns:p},a))}}const o=s=>`{{t("${s}", { ns: ["${p}", "client"] })}}`,n=()=>{const s=e.useAPIClient();return{run(){return c(this,null,function*(){var a;(a=s.service("workerForm"))==null||a.refresh()})}}},l=s=>e.useRequest({url:"worker_thread:info"},s).data,k=()=>{const{setVisible:s}=e.useActionContext(),i=h.useForm(),{message:a}=u.App.useApp(),m=e.useAPIClient(),{t:C}=d();return{run(){return c(this,null,function*(){yield i.submit(),yield m.request({url:"worker_thread:preset",method:"post",data:{count:i.values.preset}}),a.success(C("Saved successfully")),s(!1)})}}},w=()=>{const{setVisible:s}=e.useActionContext(),{message:i}=u.App.useApp(),a=e.useAPIClient(),{t:m}=d();return{run(){return c(this,null,function*(){const I=i.loading(m("Restarting all..."),0);try{yield a.request({url:"worker_thread:restartAllForcely",method:"post",data:{}}),i.success(m("Restart all successfully"))}catch(g){i.error(g.message)}finally{I()}s(!1)})}}},v={type:"object",properties:{workerForm:{"x-decorator":"Form","x-uid":"workerForm","x-decorator-props":{useValues:"{{ useWorkerInfo }}"},"x-component":"div",type:"void",title:'{{t("Worker thread info")}}',properties:{preset:{type:"number",title:o("Preset count"),minimum:0,"x-decorator":"FormItem","x-component":"InputNumber","x-component-props":{precision:0},required:!0},current:{type:"digit",title:o("Current count"),"x-decorator":"FormItem","x-component":"InputNumber","x-disabled":!0},busy:{type:"digit",title:o("Busy count"),"x-decorator":"FormItem","x-component":"InputNumber","x-disabled":!0},env:{type:"digit",title:o("Environment count"),"x-decorator":"FormItem","x-component":"InputNumber","x-disabled":!0},footer1:{type:"void","x-component":"ActionBar","x-component-props":{layout:"one-column"},properties:{submit:{title:o("Submit"),"x-component":"Action","x-component-props":{type:"primary",htmlType:"submit",useAction:"{{ resetWorkerCount }}"}},refresh:{title:o("Refresh"),"x-component":"Action","x-component-props":{type:"default",useAction:"{{ useRefresh }}"}},restartAllForcely:{title:o("Restart all forcely"),"x-component":"Action","x-component-props":{type:"default",useAction:w,confirm:{title:o("Restart all forcely"),content:o("Are you sure you want to restart all worker threads? All worker threads that are currently processing main thread tasks will be immediately terminated")}}}}},helpText1:{type:"void","x-component":"div","x-component-props":{style:{color:"grey",fontSize:"12px",marginTop:"10px"}},"x-content":o("If the current preset count is less than the number of worker threads, it will wait for the worker threads to be idle before exiting")},helpText2:{type:"void","x-component":"div","x-component-props":{style:{color:"red",fontSize:"12px",marginTop:"10px"}},"x-content":o("The modification will not be refreshed immediately, please wait patiently for about 10 seconds, or click the refresh button to view the latest data")}}}}},y=()=>r.jsx(u.Card,{bordered:!1,children:r.jsx(e.SchemaComponent,{scope:{resetWorkerCount:k,useWorkerInfo:l,useRefresh:n},schema:v})});class f extends e.Plugin{afterAdd(){return c(this,null,function*(){})}beforeLoad(){return c(this,null,function*(){})}load(){return c(this,null,function*(){this.app.addComponents({WorkerInfoPane:y}),this.app.systemSettingsManager.add("system-services."+p,{title:this.t("Worker thread"),icon:"ControlOutlined",Component:y,aclSnippet:`pm.system-services.${p}`,sort:10})})}}t.ModuleWorkerThreadClient=f,t.default=f,Object.defineProperties(t,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
package/dist/externalVersion.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
"react": "18.3.1",
|
|
3
|
-
"@tachybase/client": "0.23.
|
|
4
|
-
"@tachybase/schema": "0.23.
|
|
3
|
+
"@tachybase/client": "0.23.40",
|
|
4
|
+
"@tachybase/schema": "0.23.40",
|
|
5
5
|
"antd": "5.22.5",
|
|
6
|
-
"@tachybase/server": "0.23.
|
|
7
|
-
"@tachybase/logger": "0.23.
|
|
8
|
-
"@tachybase/module-collection": "0.23.
|
|
9
|
-
"@tachybase/module-user": "0.23.
|
|
10
|
-
"@tachybase/utils": "0.23.
|
|
11
|
-
"@tachybase/actions": "0.23.
|
|
6
|
+
"@tachybase/server": "0.23.40",
|
|
7
|
+
"@tachybase/logger": "0.23.40",
|
|
8
|
+
"@tachybase/module-collection": "0.23.40",
|
|
9
|
+
"@tachybase/module-user": "0.23.40",
|
|
10
|
+
"@tachybase/utils": "0.23.40",
|
|
11
|
+
"@tachybase/actions": "0.23.40"
|
|
12
12
|
};
|
package/dist/locale/en-US.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
+
"Are you sure you want to restart all worker threads? All worker threads that are currently processing main thread tasks will be immediately terminated": "Are you sure you want to restart all worker threads? All worker threads that are currently processing main thread tasks will be immediately terminated",
|
|
2
3
|
"Busy count": "Busy count",
|
|
3
4
|
"Current count": "Current count",
|
|
4
5
|
"Environment count": "Environment count",
|
|
6
|
+
"If the current preset count is less than the number of worker threads, it will wait for the worker threads to be idle before exiting": "If the current preset count is less than the number of worker threads, it will wait for the worker threads to be idle before exiting",
|
|
5
7
|
"Invalid worker count": "Invalid worker count",
|
|
6
8
|
"No worker thread": "No worker thread",
|
|
7
9
|
"Preset count": "Preset count",
|
|
10
|
+
"Restart all forcely": "Restart all forcely",
|
|
11
|
+
"The modification will not be refreshed immediately, please wait patiently for about 10 seconds, or click the refresh button to view the latest data": "The modification will not be refreshed immediately, please wait patiently for about 10 seconds, or click the refresh button to view the latest data",
|
|
8
12
|
"The system is currently processing other tasks. Please try again later": "The system is currently processing other tasks. Please try again later",
|
|
9
13
|
"Too many workers": "Too many workers",
|
|
10
14
|
"Worker thread": "Worker thread",
|
package/dist/locale/zh-CN.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
+
"Are you sure you want to restart all worker threads? All worker threads that are currently processing main thread tasks will be immediately terminated": "你确定要重启所有工作线程吗?会立即终止正在处理主线程任务的所有工作线程",
|
|
2
3
|
"Busy count": "繁忙数量",
|
|
3
4
|
"Current count": "当前数量",
|
|
4
5
|
"Environment count": "环境变量预设数量",
|
|
6
|
+
"If the current preset count is less than the number of worker threads, it will wait for the worker threads to be idle before exiting": "提交修改如果当前预设数量小于工作线程数量,则会等待工作线程空闲后再退出",
|
|
5
7
|
"Invalid worker count": "无效的线程数",
|
|
6
8
|
"No worker thread": "当前没有工作线程",
|
|
7
9
|
"Preset count": "预设数量",
|
|
10
|
+
"Restart all forcely": "强制重启所有",
|
|
11
|
+
"The modification will not be refreshed immediately, please wait patiently for about 10 seconds, or click the refresh button to view the latest data": "修改后不会立即刷新,耐心等待10秒左右,也可以点击刷新按钮查看最新数据",
|
|
8
12
|
"The system is currently processing other tasks. Please try again later": "当前系统处理其他事务,请稍后重试",
|
|
9
13
|
"Too many workers": "设置工作线程过多",
|
|
10
14
|
"Worker thread": "工作线程",
|
package/dist/server/worker.js
CHANGED
|
@@ -40,14 +40,24 @@ var import_workerTypes = require("./workerTypes");
|
|
|
40
40
|
const loggerOptions = {
|
|
41
41
|
system: {
|
|
42
42
|
transports: (0, import_logger.getLoggerTransport)(),
|
|
43
|
-
level:
|
|
44
|
-
// TODO: 后续考虑是否提到info
|
|
43
|
+
level: process.env.LOGGER_LEVEL || "info"
|
|
45
44
|
},
|
|
46
45
|
request: {
|
|
47
46
|
transports: (0, import_logger.getLoggerTransport)(),
|
|
48
|
-
level:
|
|
47
|
+
level: process.env.LOGGER_LEVEL || "info"
|
|
49
48
|
}
|
|
50
49
|
};
|
|
50
|
+
async function reloadCustomCollections(app) {
|
|
51
|
+
const customCollections = await app.db.getRepository("collections").load();
|
|
52
|
+
for (const [key, collection] of app.db.collections) {
|
|
53
|
+
const dumpRules = collection.options.dumpRules;
|
|
54
|
+
if ((dumpRules == null ? void 0 : dumpRules.group) === "custom") {
|
|
55
|
+
if (!customCollections.includes(collection.name)) {
|
|
56
|
+
app.db.collections.delete(collection.name);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
51
61
|
const handleWorkerMessages = (app) => {
|
|
52
62
|
if (import_worker_threads.isMainThread) {
|
|
53
63
|
return;
|
|
@@ -56,14 +66,22 @@ const handleWorkerMessages = (app) => {
|
|
|
56
66
|
event: import_workerTypes.WorkerEvent.Started
|
|
57
67
|
});
|
|
58
68
|
import_worker_threads.parentPort.on("message", async (message) => {
|
|
59
|
-
|
|
69
|
+
var _a;
|
|
70
|
+
if ((_a = message.values) == null ? void 0 : _a.inputLog) {
|
|
71
|
+
app.logger.info("[worker] input", message.values.inputLog);
|
|
72
|
+
} else {
|
|
73
|
+
app.logger.info("[worker] input", message);
|
|
74
|
+
}
|
|
60
75
|
const { reqId, event } = message;
|
|
61
76
|
if (event === import_workerTypes.WorkerEvent.PluginMethod) {
|
|
62
77
|
try {
|
|
63
|
-
const { plugin, method, params } = message.values;
|
|
78
|
+
const { plugin, method, params, reloadCols = true } = message.values;
|
|
64
79
|
const appPlugin = app.pm.get(plugin);
|
|
80
|
+
if (reloadCols) {
|
|
81
|
+
await reloadCustomCollections(app);
|
|
82
|
+
}
|
|
65
83
|
const result = await appPlugin[method](params);
|
|
66
|
-
app.logger.info(`[worker] output ${result
|
|
84
|
+
app.logger.info(`[worker] output for ${plugin}.${method}:`, result);
|
|
67
85
|
import_worker_threads.parentPort.postMessage({
|
|
68
86
|
event,
|
|
69
87
|
reqId,
|
|
@@ -23,6 +23,8 @@ export declare class WorkerManager {
|
|
|
23
23
|
private addWorker;
|
|
24
24
|
clear(): Promise<void>;
|
|
25
25
|
resetWorkerNum(targetNum: number): Promise<void>;
|
|
26
|
+
/** 强制重启所有线程 */
|
|
27
|
+
restartAllForcely(): Promise<void>;
|
|
26
28
|
/**
|
|
27
29
|
* 检查并更新并发计数
|
|
28
30
|
* @param key - 用于缓存的键
|
|
@@ -40,7 +42,7 @@ export declare class WorkerManager {
|
|
|
40
42
|
/**
|
|
41
43
|
* 运行插件方法
|
|
42
44
|
*/
|
|
43
|
-
callPluginMethod(values: WorkerEventInputPluginMethod): Promise<
|
|
45
|
+
callPluginMethod(values: WorkerEventInputPluginMethod): Promise<any>;
|
|
44
46
|
private handleWorkerCompletion;
|
|
45
47
|
/**
|
|
46
48
|
* 运行通用方法
|
|
@@ -125,6 +125,7 @@ class WorkerManager {
|
|
|
125
125
|
});
|
|
126
126
|
worker.on("exit", async (code) => {
|
|
127
127
|
if (code === 0 || code === 1) {
|
|
128
|
+
this.app.logger.debug(`[worker] terminated with code ${code}`);
|
|
128
129
|
return;
|
|
129
130
|
}
|
|
130
131
|
this.errorRecoveryTimes++;
|
|
@@ -178,6 +179,16 @@ class WorkerManager {
|
|
|
178
179
|
}
|
|
179
180
|
}
|
|
180
181
|
}
|
|
182
|
+
/** 强制重启所有线程 */
|
|
183
|
+
async restartAllForcely() {
|
|
184
|
+
if (!import_worker_threads.isMainThread) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const workerNum = this.workerNum;
|
|
188
|
+
await this.clear();
|
|
189
|
+
this.workerNum = workerNum;
|
|
190
|
+
await this.initWorkers();
|
|
191
|
+
}
|
|
181
192
|
/**
|
|
182
193
|
* 检查并更新并发计数
|
|
183
194
|
* @param key - 用于缓存的键
|
|
@@ -245,15 +256,25 @@ class WorkerManager {
|
|
|
245
256
|
throw new Error("The system is currently processing other tasks. Please try again later");
|
|
246
257
|
}
|
|
247
258
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
259
|
+
let result;
|
|
260
|
+
let error;
|
|
261
|
+
try {
|
|
262
|
+
result = await this.callMethod(import_workerTypes.WorkerEvent.PluginMethod, {
|
|
263
|
+
...values,
|
|
264
|
+
plugin: this.app.pm.get(values.plugin).name
|
|
265
|
+
});
|
|
266
|
+
} catch (err) {
|
|
267
|
+
error = err;
|
|
268
|
+
} finally {
|
|
269
|
+
if (values.globalConcurrency) {
|
|
270
|
+
this.decrementConcurrencyCount(this.getGlobalKey(), this.cache);
|
|
271
|
+
}
|
|
272
|
+
if (values.concurrency) {
|
|
273
|
+
this.decrementConcurrencyCount(this.getPluginMethodKey(values), this.cache);
|
|
274
|
+
}
|
|
254
275
|
}
|
|
255
|
-
if (
|
|
256
|
-
|
|
276
|
+
if (error) {
|
|
277
|
+
throw error;
|
|
257
278
|
}
|
|
258
279
|
return result;
|
|
259
280
|
}
|
|
@@ -298,11 +319,13 @@ class WorkerManager {
|
|
|
298
319
|
let timeout;
|
|
299
320
|
let handleMessage;
|
|
300
321
|
let handleError;
|
|
322
|
+
let handleExit;
|
|
301
323
|
handleMessage = (message) => {
|
|
302
324
|
if (message.reqId === reqId) {
|
|
303
325
|
clearTimeout(timeout);
|
|
304
326
|
worker.off("message", handleMessage);
|
|
305
327
|
worker.off("error", handleError);
|
|
328
|
+
worker.off("exit", handleExit);
|
|
306
329
|
this.handleWorkerCompletion(worker, reqId, resolve, message.result);
|
|
307
330
|
}
|
|
308
331
|
};
|
|
@@ -310,6 +333,15 @@ class WorkerManager {
|
|
|
310
333
|
clearTimeout(timeout);
|
|
311
334
|
worker.off("message", handleMessage);
|
|
312
335
|
worker.off("error", handleError);
|
|
336
|
+
worker.off("exit", handleExit);
|
|
337
|
+
this.handleWorkerCompletion(worker, reqId, resolve, void 0, reject, error);
|
|
338
|
+
};
|
|
339
|
+
handleExit = (code) => {
|
|
340
|
+
clearTimeout(timeout);
|
|
341
|
+
worker.off("message", handleMessage);
|
|
342
|
+
worker.off("error", handleError);
|
|
343
|
+
worker.off("exit", handleExit);
|
|
344
|
+
const error = new Error(`Worker stopped with exit code ${code}`);
|
|
313
345
|
this.handleWorkerCompletion(worker, reqId, resolve, void 0, reject, error);
|
|
314
346
|
};
|
|
315
347
|
timeout = setTimeout(
|
|
@@ -329,6 +361,7 @@ class WorkerManager {
|
|
|
329
361
|
);
|
|
330
362
|
worker.on("message", handleMessage);
|
|
331
363
|
worker.on("error", handleError);
|
|
364
|
+
worker.on("exit", handleExit);
|
|
332
365
|
worker.postMessage({
|
|
333
366
|
reqId,
|
|
334
367
|
event: workerEvent,
|
|
@@ -12,6 +12,8 @@ export type WorkerEventInputPluginMethod<T = any> = WorkerEventInputDefault & {
|
|
|
12
12
|
plugin: Constructor<Plugin> | string;
|
|
13
13
|
method: string;
|
|
14
14
|
params: T;
|
|
15
|
+
reloadCols?: boolean;
|
|
16
|
+
inputLog?: object | string;
|
|
15
17
|
};
|
|
16
18
|
export type WorkerEventInput = WorkerEventInputPluginMethod;
|
|
17
19
|
export type WorkerWebInfo = {
|
|
@@ -68,8 +68,8 @@ module.exports = __toCommonJS(workerWebController_exports);
|
|
|
68
68
|
var import_utils = require("@tachybase/utils");
|
|
69
69
|
var import_constants = require("../constants");
|
|
70
70
|
var import_constants2 = require("./constants");
|
|
71
|
-
var _preset_dec, _info_dec, _WorkerWebController_decorators, _init;
|
|
72
|
-
_WorkerWebController_decorators = [(0, import_utils.Controller)("worker_thread")], _info_dec = [(0, import_utils.Action)("info", { acl: "blocked" })], _preset_dec = [(0, import_utils.Action)("preset", { acl: "blocked" })];
|
|
71
|
+
var _resetAllForcely_dec, _preset_dec, _info_dec, _WorkerWebController_decorators, _init;
|
|
72
|
+
_WorkerWebController_decorators = [(0, import_utils.Controller)("worker_thread")], _info_dec = [(0, import_utils.Action)("info", { acl: "blocked" })], _preset_dec = [(0, import_utils.Action)("preset", { acl: "blocked" })], _resetAllForcely_dec = [(0, import_utils.Action)("restartAllForcely", { acl: "blocked" })];
|
|
73
73
|
class WorkerWebController {
|
|
74
74
|
constructor() {
|
|
75
75
|
__runInitializers(_init, 5, this);
|
|
@@ -112,10 +112,18 @@ class WorkerWebController {
|
|
|
112
112
|
success: true
|
|
113
113
|
};
|
|
114
114
|
}
|
|
115
|
+
async resetAllForcely(ctx, next) {
|
|
116
|
+
const app = ctx.app;
|
|
117
|
+
await app.worker.restartAllForcely();
|
|
118
|
+
ctx.body = {
|
|
119
|
+
success: true
|
|
120
|
+
};
|
|
121
|
+
}
|
|
115
122
|
}
|
|
116
123
|
_init = __decoratorStart(null);
|
|
117
124
|
__decorateElement(_init, 1, "info", _info_dec, WorkerWebController);
|
|
118
125
|
__decorateElement(_init, 1, "preset", _preset_dec, WorkerWebController);
|
|
126
|
+
__decorateElement(_init, 1, "resetAllForcely", _resetAllForcely_dec, WorkerWebController);
|
|
119
127
|
WorkerWebController = __decorateElement(_init, 0, "WorkerWebController", _WorkerWebController_decorators, WorkerWebController);
|
|
120
128
|
__runInitializers(_init, 1, WorkerWebController);
|
|
121
129
|
// Annotate the CommonJS export names for ESM import in node:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tachybase/module-worker-thread",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.40",
|
|
4
4
|
"description": "Worker thread for executing time-consuming tasks",
|
|
5
5
|
"main": "dist/server/index.js",
|
|
6
6
|
"dependencies": {},
|
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
"tsx": "^4.19.2"
|
|
10
10
|
},
|
|
11
11
|
"peerDependencies": {
|
|
12
|
-
"@tachybase/
|
|
13
|
-
"@tachybase/
|
|
14
|
-
"@tachybase/logger": "0.23.
|
|
15
|
-
"@tachybase/module-
|
|
16
|
-
"@tachybase/module-
|
|
17
|
-
"@tachybase/server": "0.23.
|
|
18
|
-
"@tachybase/schema": "0.23.
|
|
19
|
-
"@tachybase/test": "0.23.
|
|
20
|
-
"@tachybase/utils": "0.23.
|
|
12
|
+
"@tachybase/client": "0.23.40",
|
|
13
|
+
"@tachybase/actions": "0.23.40",
|
|
14
|
+
"@tachybase/logger": "0.23.40",
|
|
15
|
+
"@tachybase/module-collection": "0.23.40",
|
|
16
|
+
"@tachybase/module-user": "0.23.40",
|
|
17
|
+
"@tachybase/server": "0.23.40",
|
|
18
|
+
"@tachybase/schema": "0.23.40",
|
|
19
|
+
"@tachybase/test": "0.23.40",
|
|
20
|
+
"@tachybase/utils": "0.23.40"
|
|
21
21
|
},
|
|
22
22
|
"description.zh-CN": "工作线程,用于执行耗时任务",
|
|
23
23
|
"displayName.zh-CN": "工作线程",
|