@akanjs/server 0.9.42 → 0.9.44
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/cjs/src/boot.js +7 -3
- package/cjs/src/processor.js +1 -1
- package/cjs/src/schedule.js +231 -0
- package/cjs/src/schema.js +34 -5
- package/cjs/src/searchDaemon.js +1 -3
- package/esm/src/boot.js +7 -3
- package/esm/src/processor.js +1 -1
- package/esm/src/schedule.js +210 -0
- package/esm/src/schema.js +35 -6
- package/esm/src/searchDaemon.js +1 -3
- package/package.json +2 -1
- package/src/module.d.ts +1 -1
- package/src/schedule.d.ts +4 -0
- package/src/searchDaemon.d.ts +25 -0
package/cjs/src/boot.js
CHANGED
|
@@ -56,10 +56,12 @@ var import_dgram = __toESM(require("dgram"));
|
|
|
56
56
|
var import_events = __toESM(require("events"));
|
|
57
57
|
var import_graphql_upload = require("graphql-upload");
|
|
58
58
|
var import_meilisearch = require("meilisearch");
|
|
59
|
+
var import_mongoose2 = __toESM(require("mongoose"));
|
|
59
60
|
var import_redis = require("redis");
|
|
60
61
|
var import_base2 = require("./base.module");
|
|
61
62
|
var import_gql = require("./gql");
|
|
62
63
|
var import_module = require("./module");
|
|
64
|
+
var import_schedule2 = require("./schedule");
|
|
63
65
|
var import_searchDaemon = require("./searchDaemon");
|
|
64
66
|
const createNestApp = async ({ registerModules, serverMode = "federation", env, log = true }) => {
|
|
65
67
|
const backendLogger = new import_common.Logger("Backend");
|
|
@@ -109,9 +111,10 @@ const createNestApp = async ({ registerModules, serverMode = "federation", env,
|
|
|
109
111
|
provide: "MEILI_CLIENT",
|
|
110
112
|
useFactory: () => new import_meilisearch.MeiliSearch({ host: meiliUri, apiKey: (0, import_nest.generateMeiliKey)(env) })
|
|
111
113
|
},
|
|
112
|
-
{ provide: "GLOBAL_ENV", useValue: env }
|
|
114
|
+
{ provide: "GLOBAL_ENV", useValue: env },
|
|
115
|
+
{ provide: "MONGO_CLIENT", useValue: import_mongoose2.default.connection }
|
|
113
116
|
],
|
|
114
|
-
exports: ["REDIS_CLIENT", "MEILI_CLIENT", "GLOBAL_ENV"]
|
|
117
|
+
exports: ["REDIS_CLIENT", "MEILI_CLIENT", "GLOBAL_ENV", "MONGO_CLIENT"]
|
|
115
118
|
})
|
|
116
119
|
], GlobalProvideModule);
|
|
117
120
|
let AppModule = class {
|
|
@@ -145,7 +148,8 @@ const createNestApp = async ({ registerModules, serverMode = "federation", env,
|
|
|
145
148
|
injects: { SearchClient: import_nest.SearchClient, DatabaseClient: import_nest.DatabaseClient, CacheClient: import_nest.CacheClient }
|
|
146
149
|
}),
|
|
147
150
|
...["batch", "all"].includes(serverMode) && import_base.baseEnv.operationMode !== "edge" ? [import_searchDaemon.SearchDaemonModule] : [],
|
|
148
|
-
...[(0, import_base2.registerBaseModule)(env), ...registerModules(env)].filter((m) => !!m)
|
|
151
|
+
...[(0, import_base2.registerBaseModule)(env), ...registerModules(env)].filter((m) => !!m),
|
|
152
|
+
(0, import_schedule2.makeScheduleModule)(serverMode, env)
|
|
149
153
|
],
|
|
150
154
|
providers: [import_gql.DateScalar]
|
|
151
155
|
})
|
package/cjs/src/processor.js
CHANGED
|
@@ -51,7 +51,7 @@ const processorOf = (sigRef, allSrvs) => {
|
|
|
51
51
|
const sigMeta = (0, import_signal.getSigMeta)(sigRef);
|
|
52
52
|
const serverMode = process.env.SERVER_MODE ?? "federation";
|
|
53
53
|
const gqlMetas = (0, import_signal.getGqlMetas)(sigRef).filter((gqlMeta) => gqlMeta.type === "Process").filter(
|
|
54
|
-
(gqlMeta) => gqlMeta.signalOption.
|
|
54
|
+
(gqlMeta) => gqlMeta.signalOption.serverMode === "all" || serverMode === "all" || gqlMeta.signalOption.serverMode === serverMode
|
|
55
55
|
);
|
|
56
56
|
class QueueProcessor {
|
|
57
57
|
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
20
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
21
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
22
|
+
if (decorator = decorators[i])
|
|
23
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
24
|
+
if (kind && result)
|
|
25
|
+
__defProp(target, key, result);
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
var __publicField = (obj, key, value) => {
|
|
29
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
30
|
+
return value;
|
|
31
|
+
};
|
|
32
|
+
var __accessCheck = (obj, member, msg) => {
|
|
33
|
+
if (!member.has(obj))
|
|
34
|
+
throw TypeError("Cannot " + msg);
|
|
35
|
+
};
|
|
36
|
+
var __privateGet = (obj, member, getter) => {
|
|
37
|
+
__accessCheck(obj, member, "read from private field");
|
|
38
|
+
return getter ? getter.call(obj) : member.get(obj);
|
|
39
|
+
};
|
|
40
|
+
var __privateAdd = (obj, member, value) => {
|
|
41
|
+
if (member.has(obj))
|
|
42
|
+
throw TypeError("Cannot add the same private member more than once");
|
|
43
|
+
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
44
|
+
};
|
|
45
|
+
var schedule_exports = {};
|
|
46
|
+
__export(schedule_exports, {
|
|
47
|
+
makeScheduleModule: () => makeScheduleModule
|
|
48
|
+
});
|
|
49
|
+
module.exports = __toCommonJS(schedule_exports);
|
|
50
|
+
var import_common = require("@akanjs/common");
|
|
51
|
+
var import_service = require("@akanjs/service");
|
|
52
|
+
var import_signal = require("@akanjs/signal");
|
|
53
|
+
var import_common2 = require("@nestjs/common");
|
|
54
|
+
var import_cron = require("cron");
|
|
55
|
+
const makeScheduleModule = (serverMode, backendEnv) => {
|
|
56
|
+
var _cronMap, _timeoutMap, _intervalMap, _lockMap;
|
|
57
|
+
const srvRefs = (0, import_service.getAllServiceRefs)();
|
|
58
|
+
const sigRefs = (0, import_signal.getAllSignalRefs)();
|
|
59
|
+
const initMetas = [];
|
|
60
|
+
const cronMetas = [];
|
|
61
|
+
const intervalMetas = [];
|
|
62
|
+
const timeoutMetas = [];
|
|
63
|
+
const destroyMetas = [];
|
|
64
|
+
sigRefs.forEach((sigRef) => {
|
|
65
|
+
const gqlMetas = (0, import_signal.getGqlMetas)(sigRef);
|
|
66
|
+
gqlMetas.forEach((gqlMeta) => {
|
|
67
|
+
const { enabled, operationMode, serverMode: targetServerMode, scheduleType } = gqlMeta.signalOption;
|
|
68
|
+
if (gqlMeta.type !== "Schedule")
|
|
69
|
+
return;
|
|
70
|
+
else if (!enabled)
|
|
71
|
+
return;
|
|
72
|
+
else if (operationMode && !operationMode.includes(backendEnv.operationMode))
|
|
73
|
+
return;
|
|
74
|
+
else if (targetServerMode && targetServerMode !== "all" && serverMode !== "all" && targetServerMode !== serverMode)
|
|
75
|
+
return;
|
|
76
|
+
switch (scheduleType) {
|
|
77
|
+
case "init":
|
|
78
|
+
initMetas.push(gqlMeta);
|
|
79
|
+
break;
|
|
80
|
+
case "cron":
|
|
81
|
+
cronMetas.push(gqlMeta);
|
|
82
|
+
break;
|
|
83
|
+
case "interval":
|
|
84
|
+
intervalMetas.push(gqlMeta);
|
|
85
|
+
break;
|
|
86
|
+
case "timeout":
|
|
87
|
+
timeoutMetas.push(gqlMeta);
|
|
88
|
+
break;
|
|
89
|
+
case "destroy":
|
|
90
|
+
destroyMetas.push(gqlMeta);
|
|
91
|
+
break;
|
|
92
|
+
default:
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
let Schedule = class {
|
|
98
|
+
constructor() {
|
|
99
|
+
__publicField(this, "logger", new import_common2.Logger("Schedule"));
|
|
100
|
+
__privateAdd(this, _cronMap, /* @__PURE__ */ new Map());
|
|
101
|
+
__privateAdd(this, _timeoutMap, /* @__PURE__ */ new Map());
|
|
102
|
+
__privateAdd(this, _intervalMap, /* @__PURE__ */ new Map());
|
|
103
|
+
__privateAdd(this, _lockMap, /* @__PURE__ */ new Map());
|
|
104
|
+
}
|
|
105
|
+
async onModuleInit() {
|
|
106
|
+
await Promise.all(
|
|
107
|
+
initMetas.map(async (gqlMeta) => {
|
|
108
|
+
const fn = gqlMeta.descriptor.value;
|
|
109
|
+
const before = Date.now();
|
|
110
|
+
this.logger.debug(`Init Before ${gqlMeta.key} / ${before}`);
|
|
111
|
+
await fn.apply(this);
|
|
112
|
+
const after = Date.now();
|
|
113
|
+
this.logger.debug(`Init After ${gqlMeta.key} / ${after} (${after - before}ms)`);
|
|
114
|
+
})
|
|
115
|
+
);
|
|
116
|
+
timeoutMetas.forEach((gqlMeta) => {
|
|
117
|
+
const fn = gqlMeta.descriptor.value;
|
|
118
|
+
const timeout = gqlMeta.signalOption.scheduleTime;
|
|
119
|
+
const timer = setTimeout(async () => {
|
|
120
|
+
const before = Date.now();
|
|
121
|
+
this.logger.debug(`Timemout Before ${gqlMeta.key} / ${before}`);
|
|
122
|
+
await fn.apply(this);
|
|
123
|
+
const after = Date.now();
|
|
124
|
+
this.logger.debug(`Timemout After ${gqlMeta.key} / ${after} (${after - before}ms)`);
|
|
125
|
+
__privateGet(this, _timeoutMap).delete(fn);
|
|
126
|
+
}, timeout);
|
|
127
|
+
__privateGet(this, _timeoutMap).set(fn, timer);
|
|
128
|
+
});
|
|
129
|
+
intervalMetas.forEach((gqlMeta) => {
|
|
130
|
+
const lock = gqlMeta.signalOption.lock;
|
|
131
|
+
const fn = gqlMeta.descriptor.value;
|
|
132
|
+
const interval = gqlMeta.signalOption.scheduleTime;
|
|
133
|
+
const timer = setInterval(async () => {
|
|
134
|
+
if (lock) {
|
|
135
|
+
if (__privateGet(this, _lockMap).get(fn)) {
|
|
136
|
+
this.logger.warn(`${gqlMeta.key} is locked, skipping...`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
__privateGet(this, _lockMap).set(fn, true);
|
|
140
|
+
}
|
|
141
|
+
const before = Date.now();
|
|
142
|
+
this.logger.debug(`Interval Before ${gqlMeta.key} / ${before}`);
|
|
143
|
+
await fn.apply(this);
|
|
144
|
+
const after = Date.now();
|
|
145
|
+
this.logger.debug(`Interval After ${gqlMeta.key} / ${after} (${after - before}ms)`);
|
|
146
|
+
if (lock)
|
|
147
|
+
__privateGet(this, _lockMap).set(fn, false);
|
|
148
|
+
}, interval);
|
|
149
|
+
__privateGet(this, _intervalMap).set(fn, timer);
|
|
150
|
+
});
|
|
151
|
+
cronMetas.forEach((gqlMeta) => {
|
|
152
|
+
const lock = gqlMeta.signalOption.lock;
|
|
153
|
+
const fn = gqlMeta.descriptor.value;
|
|
154
|
+
const cronTime = gqlMeta.signalOption.scheduleCron;
|
|
155
|
+
if (!cronTime)
|
|
156
|
+
throw new Error(`Cron time is not found for ${gqlMeta.key}`);
|
|
157
|
+
const cronJob = import_cron.CronJob.from({
|
|
158
|
+
cronTime,
|
|
159
|
+
onTick: async () => {
|
|
160
|
+
if (lock) {
|
|
161
|
+
if (__privateGet(this, _lockMap).get(fn)) {
|
|
162
|
+
this.logger.warn(`${gqlMeta.key} is locked, skipping...`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
__privateGet(this, _lockMap).set(fn, true);
|
|
166
|
+
}
|
|
167
|
+
const before = Date.now();
|
|
168
|
+
this.logger.debug(`Cron Before ${gqlMeta.key} / ${before}`);
|
|
169
|
+
await fn.apply(this);
|
|
170
|
+
const after = Date.now();
|
|
171
|
+
this.logger.debug(`Cron After ${gqlMeta.key} / ${after} (${after - before}ms)`);
|
|
172
|
+
if (lock)
|
|
173
|
+
__privateGet(this, _lockMap).set(fn, false);
|
|
174
|
+
},
|
|
175
|
+
start: true
|
|
176
|
+
});
|
|
177
|
+
__privateGet(this, _cronMap).set(fn, cronJob);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
async onModuleDestroy() {
|
|
181
|
+
__privateGet(this, _timeoutMap).forEach((timer, fn) => {
|
|
182
|
+
clearTimeout(timer);
|
|
183
|
+
__privateGet(this, _timeoutMap).delete(fn);
|
|
184
|
+
});
|
|
185
|
+
__privateGet(this, _intervalMap).forEach((timer, fn) => {
|
|
186
|
+
clearInterval(timer);
|
|
187
|
+
__privateGet(this, _intervalMap).delete(fn);
|
|
188
|
+
});
|
|
189
|
+
await Promise.all(
|
|
190
|
+
[...__privateGet(this, _cronMap).entries()].map(async ([fn, cronJob]) => {
|
|
191
|
+
await cronJob.stop();
|
|
192
|
+
__privateGet(this, _cronMap).delete(fn);
|
|
193
|
+
})
|
|
194
|
+
);
|
|
195
|
+
await Promise.all(
|
|
196
|
+
destroyMetas.map(async (gqlMeta) => {
|
|
197
|
+
const fn = gqlMeta.descriptor.value;
|
|
198
|
+
const before = Date.now();
|
|
199
|
+
this.logger.debug(`Destroy Before ${gqlMeta.key} / ${before}`);
|
|
200
|
+
await fn.apply(this);
|
|
201
|
+
const after = Date.now();
|
|
202
|
+
this.logger.debug(`Destroy After ${gqlMeta.key} / ${after} (${after - before}ms)`);
|
|
203
|
+
})
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
_cronMap = new WeakMap();
|
|
208
|
+
_timeoutMap = new WeakMap();
|
|
209
|
+
_intervalMap = new WeakMap();
|
|
210
|
+
_lockMap = new WeakMap();
|
|
211
|
+
Schedule = __decorateClass([
|
|
212
|
+
(0, import_common2.Injectable)()
|
|
213
|
+
], Schedule);
|
|
214
|
+
srvRefs.forEach((srvRef) => {
|
|
215
|
+
const serviceMeta = (0, import_service.getServiceMeta)(srvRef);
|
|
216
|
+
if (!serviceMeta)
|
|
217
|
+
throw new Error(`Service ${srvRef.name} is not found`);
|
|
218
|
+
(0, import_common2.Inject)(srvRef)(Schedule.prototype, (0, import_common.lowerlize)(serviceMeta.name));
|
|
219
|
+
});
|
|
220
|
+
let ScheduleModule = class {
|
|
221
|
+
};
|
|
222
|
+
ScheduleModule = __decorateClass([
|
|
223
|
+
(0, import_common2.Global)(),
|
|
224
|
+
(0, import_common2.Module)({ providers: [Schedule] })
|
|
225
|
+
], ScheduleModule);
|
|
226
|
+
return ScheduleModule;
|
|
227
|
+
};
|
|
228
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
229
|
+
0 && (module.exports = {
|
|
230
|
+
makeScheduleModule
|
|
231
|
+
});
|
package/cjs/src/schema.js
CHANGED
|
@@ -206,13 +206,42 @@ const schemaOf = (modelRef, docRef, middleware) => {
|
|
|
206
206
|
schema.methods[name] = Object.getOwnPropertyDescriptor(docRef.prototype, name)?.value;
|
|
207
207
|
});
|
|
208
208
|
schema.pre("save", async function(next) {
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
209
|
+
const saveType = this.isNew ? "create" : this.isModified("removedAt") ? this.removedAt ? "remove" : "create" : "update";
|
|
210
|
+
const saveListeners = [
|
|
211
|
+
...this.constructor.preSaveListenerSet,
|
|
212
|
+
...saveType === "create" ? [...this.constructor.preCreateListenerSet] : saveType === "update" ? [...this.constructor.preUpdateListenerSet] : [...this.constructor.preRemoveListenerSet]
|
|
213
|
+
];
|
|
214
|
+
await Promise.all(
|
|
215
|
+
saveListeners.map(async (listener) => {
|
|
216
|
+
try {
|
|
217
|
+
await listener(this, saveType);
|
|
218
|
+
} catch (e) {
|
|
219
|
+
import_common.Logger.error(
|
|
220
|
+
`Pre Save Listener Error ${this.constructor.modelName}: ${e instanceof Error ? e.message : typeof e === "string" ? e : "unknown error"}`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
);
|
|
214
225
|
next();
|
|
215
226
|
});
|
|
227
|
+
schema.post("save", async function() {
|
|
228
|
+
const saveType = this.isNew ? "create" : this.isModified("removedAt") ? this.removedAt ? "remove" : "create" : "update";
|
|
229
|
+
const saveListeners = [
|
|
230
|
+
...this.constructor.postSaveListenerSet,
|
|
231
|
+
...saveType === "create" ? [...this.constructor.postCreateListenerSet] : saveType === "update" ? [...this.constructor.postUpdateListenerSet] : [...this.constructor.postRemoveListenerSet]
|
|
232
|
+
];
|
|
233
|
+
await Promise.all(
|
|
234
|
+
saveListeners.map(async (listener) => {
|
|
235
|
+
try {
|
|
236
|
+
await listener(this, saveType);
|
|
237
|
+
} catch (e) {
|
|
238
|
+
import_common.Logger.error(
|
|
239
|
+
`Post Save Listener Error ${this.constructor.modelName}: ${e instanceof Error ? e.message : typeof e === "string" ? e : "unknown error"}`
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
);
|
|
244
|
+
});
|
|
216
245
|
const onSchema = Object.getOwnPropertyDescriptor(middleware.prototype, "onSchema")?.value;
|
|
217
246
|
onSchema?.(schema);
|
|
218
247
|
schema.index({ removedAt: -1 });
|
package/cjs/src/searchDaemon.js
CHANGED
|
@@ -217,9 +217,7 @@ let SearchDaemonModule = class {
|
|
|
217
217
|
};
|
|
218
218
|
SearchDaemonModule = __decorateClass([
|
|
219
219
|
(0, import_common2.Global)(),
|
|
220
|
-
(0, import_common2.Module)({
|
|
221
|
-
providers: [SearchDaemon]
|
|
222
|
-
})
|
|
220
|
+
(0, import_common2.Module)({ providers: [SearchDaemon] })
|
|
223
221
|
], SearchDaemonModule);
|
|
224
222
|
// Annotate the CommonJS export names for ESM import in node:
|
|
225
223
|
0 && (module.exports = {
|
package/esm/src/boot.js
CHANGED
|
@@ -47,10 +47,12 @@ import dgram from "dgram";
|
|
|
47
47
|
import events from "events";
|
|
48
48
|
import { graphqlUploadExpress } from "graphql-upload";
|
|
49
49
|
import { MeiliSearch } from "meilisearch";
|
|
50
|
+
import mongoose from "mongoose";
|
|
50
51
|
import { createClient } from "redis";
|
|
51
52
|
import { registerBaseModule } from "./base.module";
|
|
52
53
|
import { DateScalar } from "./gql";
|
|
53
54
|
import { useGlobals } from "./module";
|
|
55
|
+
import { makeScheduleModule } from "./schedule";
|
|
54
56
|
import { SearchDaemonModule } from "./searchDaemon";
|
|
55
57
|
const createNestApp = async ({ registerModules, serverMode = "federation", env, log = true }) => {
|
|
56
58
|
const backendLogger = new Logger("Backend");
|
|
@@ -100,9 +102,10 @@ const createNestApp = async ({ registerModules, serverMode = "federation", env,
|
|
|
100
102
|
provide: "MEILI_CLIENT",
|
|
101
103
|
useFactory: () => new MeiliSearch({ host: meiliUri, apiKey: generateMeiliKey(env) })
|
|
102
104
|
},
|
|
103
|
-
{ provide: "GLOBAL_ENV", useValue: env }
|
|
105
|
+
{ provide: "GLOBAL_ENV", useValue: env },
|
|
106
|
+
{ provide: "MONGO_CLIENT", useValue: mongoose.connection }
|
|
104
107
|
],
|
|
105
|
-
exports: ["REDIS_CLIENT", "MEILI_CLIENT", "GLOBAL_ENV"]
|
|
108
|
+
exports: ["REDIS_CLIENT", "MEILI_CLIENT", "GLOBAL_ENV", "MONGO_CLIENT"]
|
|
106
109
|
})
|
|
107
110
|
], GlobalProvideModule);
|
|
108
111
|
let AppModule = class {
|
|
@@ -136,7 +139,8 @@ const createNestApp = async ({ registerModules, serverMode = "federation", env,
|
|
|
136
139
|
injects: { SearchClient, DatabaseClient, CacheClient }
|
|
137
140
|
}),
|
|
138
141
|
...["batch", "all"].includes(serverMode) && baseEnv.operationMode !== "edge" ? [SearchDaemonModule] : [],
|
|
139
|
-
...[registerBaseModule(env), ...registerModules(env)].filter((m) => !!m)
|
|
142
|
+
...[registerBaseModule(env), ...registerModules(env)].filter((m) => !!m),
|
|
143
|
+
makeScheduleModule(serverMode, env)
|
|
140
144
|
],
|
|
141
145
|
providers: [DateScalar]
|
|
142
146
|
})
|
package/esm/src/processor.js
CHANGED
|
@@ -33,7 +33,7 @@ const processorOf = (sigRef, allSrvs) => {
|
|
|
33
33
|
const sigMeta = getSigMeta(sigRef);
|
|
34
34
|
const serverMode = process.env.SERVER_MODE ?? "federation";
|
|
35
35
|
const gqlMetas = getGqlMetas(sigRef).filter((gqlMeta) => gqlMeta.type === "Process").filter(
|
|
36
|
-
(gqlMeta) => gqlMeta.signalOption.
|
|
36
|
+
(gqlMeta) => gqlMeta.signalOption.serverMode === "all" || serverMode === "all" || gqlMeta.signalOption.serverMode === serverMode
|
|
37
37
|
);
|
|
38
38
|
class QueueProcessor {
|
|
39
39
|
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
5
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
6
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
7
|
+
if (decorator = decorators[i])
|
|
8
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
9
|
+
if (kind && result)
|
|
10
|
+
__defProp(target, key, result);
|
|
11
|
+
return result;
|
|
12
|
+
};
|
|
13
|
+
var __publicField = (obj, key, value) => {
|
|
14
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
15
|
+
return value;
|
|
16
|
+
};
|
|
17
|
+
var __accessCheck = (obj, member, msg) => {
|
|
18
|
+
if (!member.has(obj))
|
|
19
|
+
throw TypeError("Cannot " + msg);
|
|
20
|
+
};
|
|
21
|
+
var __privateGet = (obj, member, getter) => {
|
|
22
|
+
__accessCheck(obj, member, "read from private field");
|
|
23
|
+
return getter ? getter.call(obj) : member.get(obj);
|
|
24
|
+
};
|
|
25
|
+
var __privateAdd = (obj, member, value) => {
|
|
26
|
+
if (member.has(obj))
|
|
27
|
+
throw TypeError("Cannot add the same private member more than once");
|
|
28
|
+
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
29
|
+
};
|
|
30
|
+
import { lowerlize } from "@akanjs/common";
|
|
31
|
+
import { getAllServiceRefs, getServiceMeta } from "@akanjs/service";
|
|
32
|
+
import { getAllSignalRefs, getGqlMetas } from "@akanjs/signal";
|
|
33
|
+
import { Global, Inject, Injectable, Logger, Module } from "@nestjs/common";
|
|
34
|
+
import { CronJob } from "cron";
|
|
35
|
+
const makeScheduleModule = (serverMode, backendEnv) => {
|
|
36
|
+
var _cronMap, _timeoutMap, _intervalMap, _lockMap;
|
|
37
|
+
const srvRefs = getAllServiceRefs();
|
|
38
|
+
const sigRefs = getAllSignalRefs();
|
|
39
|
+
const initMetas = [];
|
|
40
|
+
const cronMetas = [];
|
|
41
|
+
const intervalMetas = [];
|
|
42
|
+
const timeoutMetas = [];
|
|
43
|
+
const destroyMetas = [];
|
|
44
|
+
sigRefs.forEach((sigRef) => {
|
|
45
|
+
const gqlMetas = getGqlMetas(sigRef);
|
|
46
|
+
gqlMetas.forEach((gqlMeta) => {
|
|
47
|
+
const { enabled, operationMode, serverMode: targetServerMode, scheduleType } = gqlMeta.signalOption;
|
|
48
|
+
if (gqlMeta.type !== "Schedule")
|
|
49
|
+
return;
|
|
50
|
+
else if (!enabled)
|
|
51
|
+
return;
|
|
52
|
+
else if (operationMode && !operationMode.includes(backendEnv.operationMode))
|
|
53
|
+
return;
|
|
54
|
+
else if (targetServerMode && targetServerMode !== "all" && serverMode !== "all" && targetServerMode !== serverMode)
|
|
55
|
+
return;
|
|
56
|
+
switch (scheduleType) {
|
|
57
|
+
case "init":
|
|
58
|
+
initMetas.push(gqlMeta);
|
|
59
|
+
break;
|
|
60
|
+
case "cron":
|
|
61
|
+
cronMetas.push(gqlMeta);
|
|
62
|
+
break;
|
|
63
|
+
case "interval":
|
|
64
|
+
intervalMetas.push(gqlMeta);
|
|
65
|
+
break;
|
|
66
|
+
case "timeout":
|
|
67
|
+
timeoutMetas.push(gqlMeta);
|
|
68
|
+
break;
|
|
69
|
+
case "destroy":
|
|
70
|
+
destroyMetas.push(gqlMeta);
|
|
71
|
+
break;
|
|
72
|
+
default:
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
let Schedule = class {
|
|
78
|
+
constructor() {
|
|
79
|
+
__publicField(this, "logger", new Logger("Schedule"));
|
|
80
|
+
__privateAdd(this, _cronMap, /* @__PURE__ */ new Map());
|
|
81
|
+
__privateAdd(this, _timeoutMap, /* @__PURE__ */ new Map());
|
|
82
|
+
__privateAdd(this, _intervalMap, /* @__PURE__ */ new Map());
|
|
83
|
+
__privateAdd(this, _lockMap, /* @__PURE__ */ new Map());
|
|
84
|
+
}
|
|
85
|
+
async onModuleInit() {
|
|
86
|
+
await Promise.all(
|
|
87
|
+
initMetas.map(async (gqlMeta) => {
|
|
88
|
+
const fn = gqlMeta.descriptor.value;
|
|
89
|
+
const before = Date.now();
|
|
90
|
+
this.logger.debug(`Init Before ${gqlMeta.key} / ${before}`);
|
|
91
|
+
await fn.apply(this);
|
|
92
|
+
const after = Date.now();
|
|
93
|
+
this.logger.debug(`Init After ${gqlMeta.key} / ${after} (${after - before}ms)`);
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
timeoutMetas.forEach((gqlMeta) => {
|
|
97
|
+
const fn = gqlMeta.descriptor.value;
|
|
98
|
+
const timeout = gqlMeta.signalOption.scheduleTime;
|
|
99
|
+
const timer = setTimeout(async () => {
|
|
100
|
+
const before = Date.now();
|
|
101
|
+
this.logger.debug(`Timemout Before ${gqlMeta.key} / ${before}`);
|
|
102
|
+
await fn.apply(this);
|
|
103
|
+
const after = Date.now();
|
|
104
|
+
this.logger.debug(`Timemout After ${gqlMeta.key} / ${after} (${after - before}ms)`);
|
|
105
|
+
__privateGet(this, _timeoutMap).delete(fn);
|
|
106
|
+
}, timeout);
|
|
107
|
+
__privateGet(this, _timeoutMap).set(fn, timer);
|
|
108
|
+
});
|
|
109
|
+
intervalMetas.forEach((gqlMeta) => {
|
|
110
|
+
const lock = gqlMeta.signalOption.lock;
|
|
111
|
+
const fn = gqlMeta.descriptor.value;
|
|
112
|
+
const interval = gqlMeta.signalOption.scheduleTime;
|
|
113
|
+
const timer = setInterval(async () => {
|
|
114
|
+
if (lock) {
|
|
115
|
+
if (__privateGet(this, _lockMap).get(fn)) {
|
|
116
|
+
this.logger.warn(`${gqlMeta.key} is locked, skipping...`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
__privateGet(this, _lockMap).set(fn, true);
|
|
120
|
+
}
|
|
121
|
+
const before = Date.now();
|
|
122
|
+
this.logger.debug(`Interval Before ${gqlMeta.key} / ${before}`);
|
|
123
|
+
await fn.apply(this);
|
|
124
|
+
const after = Date.now();
|
|
125
|
+
this.logger.debug(`Interval After ${gqlMeta.key} / ${after} (${after - before}ms)`);
|
|
126
|
+
if (lock)
|
|
127
|
+
__privateGet(this, _lockMap).set(fn, false);
|
|
128
|
+
}, interval);
|
|
129
|
+
__privateGet(this, _intervalMap).set(fn, timer);
|
|
130
|
+
});
|
|
131
|
+
cronMetas.forEach((gqlMeta) => {
|
|
132
|
+
const lock = gqlMeta.signalOption.lock;
|
|
133
|
+
const fn = gqlMeta.descriptor.value;
|
|
134
|
+
const cronTime = gqlMeta.signalOption.scheduleCron;
|
|
135
|
+
if (!cronTime)
|
|
136
|
+
throw new Error(`Cron time is not found for ${gqlMeta.key}`);
|
|
137
|
+
const cronJob = CronJob.from({
|
|
138
|
+
cronTime,
|
|
139
|
+
onTick: async () => {
|
|
140
|
+
if (lock) {
|
|
141
|
+
if (__privateGet(this, _lockMap).get(fn)) {
|
|
142
|
+
this.logger.warn(`${gqlMeta.key} is locked, skipping...`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
__privateGet(this, _lockMap).set(fn, true);
|
|
146
|
+
}
|
|
147
|
+
const before = Date.now();
|
|
148
|
+
this.logger.debug(`Cron Before ${gqlMeta.key} / ${before}`);
|
|
149
|
+
await fn.apply(this);
|
|
150
|
+
const after = Date.now();
|
|
151
|
+
this.logger.debug(`Cron After ${gqlMeta.key} / ${after} (${after - before}ms)`);
|
|
152
|
+
if (lock)
|
|
153
|
+
__privateGet(this, _lockMap).set(fn, false);
|
|
154
|
+
},
|
|
155
|
+
start: true
|
|
156
|
+
});
|
|
157
|
+
__privateGet(this, _cronMap).set(fn, cronJob);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
async onModuleDestroy() {
|
|
161
|
+
__privateGet(this, _timeoutMap).forEach((timer, fn) => {
|
|
162
|
+
clearTimeout(timer);
|
|
163
|
+
__privateGet(this, _timeoutMap).delete(fn);
|
|
164
|
+
});
|
|
165
|
+
__privateGet(this, _intervalMap).forEach((timer, fn) => {
|
|
166
|
+
clearInterval(timer);
|
|
167
|
+
__privateGet(this, _intervalMap).delete(fn);
|
|
168
|
+
});
|
|
169
|
+
await Promise.all(
|
|
170
|
+
[...__privateGet(this, _cronMap).entries()].map(async ([fn, cronJob]) => {
|
|
171
|
+
await cronJob.stop();
|
|
172
|
+
__privateGet(this, _cronMap).delete(fn);
|
|
173
|
+
})
|
|
174
|
+
);
|
|
175
|
+
await Promise.all(
|
|
176
|
+
destroyMetas.map(async (gqlMeta) => {
|
|
177
|
+
const fn = gqlMeta.descriptor.value;
|
|
178
|
+
const before = Date.now();
|
|
179
|
+
this.logger.debug(`Destroy Before ${gqlMeta.key} / ${before}`);
|
|
180
|
+
await fn.apply(this);
|
|
181
|
+
const after = Date.now();
|
|
182
|
+
this.logger.debug(`Destroy After ${gqlMeta.key} / ${after} (${after - before}ms)`);
|
|
183
|
+
})
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
_cronMap = new WeakMap();
|
|
188
|
+
_timeoutMap = new WeakMap();
|
|
189
|
+
_intervalMap = new WeakMap();
|
|
190
|
+
_lockMap = new WeakMap();
|
|
191
|
+
Schedule = __decorateClass([
|
|
192
|
+
Injectable()
|
|
193
|
+
], Schedule);
|
|
194
|
+
srvRefs.forEach((srvRef) => {
|
|
195
|
+
const serviceMeta = getServiceMeta(srvRef);
|
|
196
|
+
if (!serviceMeta)
|
|
197
|
+
throw new Error(`Service ${srvRef.name} is not found`);
|
|
198
|
+
Inject(srvRef)(Schedule.prototype, lowerlize(serviceMeta.name));
|
|
199
|
+
});
|
|
200
|
+
let ScheduleModule = class {
|
|
201
|
+
};
|
|
202
|
+
ScheduleModule = __decorateClass([
|
|
203
|
+
Global(),
|
|
204
|
+
Module({ providers: [Schedule] })
|
|
205
|
+
], ScheduleModule);
|
|
206
|
+
return ScheduleModule;
|
|
207
|
+
};
|
|
208
|
+
export {
|
|
209
|
+
makeScheduleModule
|
|
210
|
+
};
|
package/esm/src/schema.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
Int,
|
|
9
9
|
JSON
|
|
10
10
|
} from "@akanjs/base";
|
|
11
|
-
import { isDayjs } from "@akanjs/common";
|
|
11
|
+
import { isDayjs, Logger } from "@akanjs/common";
|
|
12
12
|
import { getClassMeta, getFieldMetas, getFullModelRef, getInputModelRef } from "@akanjs/constant";
|
|
13
13
|
import { getDefaultSchemaOptions, ObjectId } from "@akanjs/document";
|
|
14
14
|
import { makeDefault } from "@akanjs/signal";
|
|
@@ -191,13 +191,42 @@ const schemaOf = (modelRef, docRef, middleware) => {
|
|
|
191
191
|
schema.methods[name] = Object.getOwnPropertyDescriptor(docRef.prototype, name)?.value;
|
|
192
192
|
});
|
|
193
193
|
schema.pre("save", async function(next) {
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
194
|
+
const saveType = this.isNew ? "create" : this.isModified("removedAt") ? this.removedAt ? "remove" : "create" : "update";
|
|
195
|
+
const saveListeners = [
|
|
196
|
+
...this.constructor.preSaveListenerSet,
|
|
197
|
+
...saveType === "create" ? [...this.constructor.preCreateListenerSet] : saveType === "update" ? [...this.constructor.preUpdateListenerSet] : [...this.constructor.preRemoveListenerSet]
|
|
198
|
+
];
|
|
199
|
+
await Promise.all(
|
|
200
|
+
saveListeners.map(async (listener) => {
|
|
201
|
+
try {
|
|
202
|
+
await listener(this, saveType);
|
|
203
|
+
} catch (e) {
|
|
204
|
+
Logger.error(
|
|
205
|
+
`Pre Save Listener Error ${this.constructor.modelName}: ${e instanceof Error ? e.message : typeof e === "string" ? e : "unknown error"}`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
);
|
|
199
210
|
next();
|
|
200
211
|
});
|
|
212
|
+
schema.post("save", async function() {
|
|
213
|
+
const saveType = this.isNew ? "create" : this.isModified("removedAt") ? this.removedAt ? "remove" : "create" : "update";
|
|
214
|
+
const saveListeners = [
|
|
215
|
+
...this.constructor.postSaveListenerSet,
|
|
216
|
+
...saveType === "create" ? [...this.constructor.postCreateListenerSet] : saveType === "update" ? [...this.constructor.postUpdateListenerSet] : [...this.constructor.postRemoveListenerSet]
|
|
217
|
+
];
|
|
218
|
+
await Promise.all(
|
|
219
|
+
saveListeners.map(async (listener) => {
|
|
220
|
+
try {
|
|
221
|
+
await listener(this, saveType);
|
|
222
|
+
} catch (e) {
|
|
223
|
+
Logger.error(
|
|
224
|
+
`Post Save Listener Error ${this.constructor.modelName}: ${e instanceof Error ? e.message : typeof e === "string" ? e : "unknown error"}`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
);
|
|
229
|
+
});
|
|
201
230
|
const onSchema = Object.getOwnPropertyDescriptor(middleware.prototype, "onSchema")?.value;
|
|
202
231
|
onSchema?.(schema);
|
|
203
232
|
schema.index({ removedAt: -1 });
|
package/esm/src/searchDaemon.js
CHANGED
|
@@ -202,9 +202,7 @@ let SearchDaemonModule = class {
|
|
|
202
202
|
};
|
|
203
203
|
SearchDaemonModule = __decorateClass([
|
|
204
204
|
Global(),
|
|
205
|
-
Module({
|
|
206
|
-
providers: [SearchDaemon]
|
|
207
|
-
})
|
|
205
|
+
Module({ providers: [SearchDaemon] })
|
|
208
206
|
], SearchDaemonModule);
|
|
209
207
|
export {
|
|
210
208
|
SearchDaemonModule,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/server",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.44",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"@nestjs/websockets": "^10.4.15",
|
|
31
31
|
"body-parser": "^1.20.3",
|
|
32
32
|
"cookie-parser": "^1.4.7",
|
|
33
|
+
"cron": "^4.3.3",
|
|
33
34
|
"dayjs": "^1.11.13",
|
|
34
35
|
"graphql": "^16.10.0",
|
|
35
36
|
"graphql-type-json": "^0.3.2",
|
package/src/module.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Database } from "@akanjs/document";
|
|
|
4
4
|
import { DynamicModule } from "@nestjs/common";
|
|
5
5
|
interface DatabaseModuleCreateOptions {
|
|
6
6
|
constant: ConstantModel<string, any, any, any, any, any>;
|
|
7
|
-
database: Database<string, any, any, any, any, any, any, any
|
|
7
|
+
database: Database<string, any, any, any, any, any, any, any>;
|
|
8
8
|
signal: Type;
|
|
9
9
|
service: Type;
|
|
10
10
|
}
|
package/src/searchDaemon.d.ts
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
import type { Type } from "@akanjs/base";
|
|
2
2
|
import { type TextDoc } from "@akanjs/constant";
|
|
3
|
+
import type { Types } from "mongoose";
|
|
4
|
+
export interface ChangedData {
|
|
5
|
+
_id: {
|
|
6
|
+
_data: string;
|
|
7
|
+
};
|
|
8
|
+
operationType: "update" | "insert" | "delete";
|
|
9
|
+
clusterTime: {
|
|
10
|
+
t: number;
|
|
11
|
+
i: number;
|
|
12
|
+
};
|
|
13
|
+
wallTime: Date;
|
|
14
|
+
ns: {
|
|
15
|
+
db: string;
|
|
16
|
+
coll: string;
|
|
17
|
+
};
|
|
18
|
+
documentKey: {
|
|
19
|
+
_id: Types.ObjectId;
|
|
20
|
+
};
|
|
21
|
+
updateDescription?: {
|
|
22
|
+
updatedFields: Record<string, any>;
|
|
23
|
+
removedFields: string[];
|
|
24
|
+
truncatedArrays: any[];
|
|
25
|
+
};
|
|
26
|
+
fullDocument?: Record<string, any>;
|
|
27
|
+
}
|
|
3
28
|
export declare const makeTextFilter: (modelRef: Type) => (data: Record<string, any>, assignObj?: {
|
|
4
29
|
[key: string]: string;
|
|
5
30
|
}) => TextDoc;
|