@dongdev/fca-unofficial 3.0.31 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +191 -0
- package/README.md +224 -406
- package/dist/index.d.mts +1241 -0
- package/dist/index.d.ts +1241 -0
- package/dist/index.js +27749 -0
- package/dist/index.mjs +27713 -0
- package/docs/ARCHITECTURE.md +467 -0
- package/docs/DOCS.md +686 -0
- package/fca-config.example.json +33 -0
- package/package.json +32 -22
- package/test/fca.test.cjs +533 -0
- package/CHANGELOG.md +0 -296
- package/DOCS.md +0 -2712
- package/func/checkUpdate.js +0 -222
- package/func/logAdapter.js +0 -33
- package/func/logger.js +0 -48
- package/index.d.ts +0 -751
- package/index.js +0 -8
- package/module/config.js +0 -40
- package/module/login.js +0 -133
- package/module/loginHelper.js +0 -1296
- package/module/options.js +0 -44
- package/src/api/action/addExternalModule.js +0 -25
- package/src/api/action/changeAvatar.js +0 -137
- package/src/api/action/changeBio.js +0 -75
- package/src/api/action/enableAutoSaveAppState.js +0 -73
- package/src/api/action/getCurrentUserID.js +0 -7
- package/src/api/action/handleFriendRequest.js +0 -57
- package/src/api/action/logout.js +0 -76
- package/src/api/action/refreshFb_dtsg.js +0 -48
- package/src/api/action/setPostReaction.js +0 -106
- package/src/api/action/unfriend.js +0 -54
- package/src/api/http/httpGet.js +0 -46
- package/src/api/http/httpPost.js +0 -52
- package/src/api/http/postFormData.js +0 -47
- package/src/api/messaging/addUserToGroup.js +0 -68
- package/src/api/messaging/changeAdminStatus.js +0 -126
- package/src/api/messaging/changeArchivedStatus.js +0 -55
- package/src/api/messaging/changeBlockedStatus.js +0 -48
- package/src/api/messaging/changeGroupImage.js +0 -91
- package/src/api/messaging/changeNickname.js +0 -70
- package/src/api/messaging/changeThreadColor.js +0 -79
- package/src/api/messaging/changeThreadEmoji.js +0 -111
- package/src/api/messaging/createNewGroup.js +0 -88
- package/src/api/messaging/createPoll.js +0 -46
- package/src/api/messaging/createThemeAI.js +0 -98
- package/src/api/messaging/deleteMessage.js +0 -136
- package/src/api/messaging/deleteThread.js +0 -56
- package/src/api/messaging/editMessage.js +0 -68
- package/src/api/messaging/forwardAttachment.js +0 -57
- package/src/api/messaging/getEmojiUrl.js +0 -29
- package/src/api/messaging/getFriendsList.js +0 -82
- package/src/api/messaging/getMessage.js +0 -829
- package/src/api/messaging/getThemePictures.js +0 -62
- package/src/api/messaging/handleMessageRequest.js +0 -65
- package/src/api/messaging/markAsDelivered.js +0 -57
- package/src/api/messaging/markAsRead.js +0 -88
- package/src/api/messaging/markAsReadAll.js +0 -49
- package/src/api/messaging/markAsSeen.js +0 -61
- package/src/api/messaging/muteThread.js +0 -50
- package/src/api/messaging/removeUserFromGroup.js +0 -62
- package/src/api/messaging/resolvePhotoUrl.js +0 -43
- package/src/api/messaging/scheduler.js +0 -264
- package/src/api/messaging/searchForThread.js +0 -53
- package/src/api/messaging/sendMessage.js +0 -270
- package/src/api/messaging/sendTypingIndicator.js +0 -74
- package/src/api/messaging/setMessageReaction.js +0 -90
- package/src/api/messaging/setTitle.js +0 -124
- package/src/api/messaging/shareContact.js +0 -49
- package/src/api/messaging/threadColors.js +0 -128
- package/src/api/messaging/unsendMessage.js +0 -81
- package/src/api/messaging/uploadAttachment.js +0 -492
- package/src/api/socket/core/connectMqtt.js +0 -258
- package/src/api/socket/core/emitAuth.js +0 -103
- package/src/api/socket/core/getSeqID.js +0 -320
- package/src/api/socket/core/getTaskResponseData.js +0 -25
- package/src/api/socket/core/parseDelta.js +0 -377
- package/src/api/socket/detail/buildStream.js +0 -215
- package/src/api/socket/detail/constants.js +0 -28
- package/src/api/socket/listenMqtt.js +0 -377
- package/src/api/socket/middleware/index.js +0 -216
- package/src/api/threads/getThreadHistory.js +0 -664
- package/src/api/threads/getThreadInfo.js +0 -296
- package/src/api/threads/getThreadList.js +0 -293
- package/src/api/threads/getThreadPictures.js +0 -78
- package/src/api/users/getUserID.js +0 -65
- package/src/api/users/getUserInfo.js +0 -402
- package/src/api/users/getUserInfoV2.js +0 -134
- package/src/core/sendReqMqtt.js +0 -96
- package/src/database/helpers.js +0 -53
- package/src/database/models/index.js +0 -88
- package/src/database/models/thread.js +0 -50
- package/src/database/models/user.js +0 -46
- package/src/database/threadData.js +0 -94
- package/src/database/userData.js +0 -98
- package/src/remote/remoteClient.js +0 -123
- package/src/utils/broadcast.js +0 -51
- package/src/utils/client.js +0 -10
- package/src/utils/constants.js +0 -23
- package/src/utils/cookies.js +0 -68
- package/src/utils/format/attachment.js +0 -357
- package/src/utils/format/cookie.js +0 -9
- package/src/utils/format/date.js +0 -50
- package/src/utils/format/decode.js +0 -44
- package/src/utils/format/delta.js +0 -194
- package/src/utils/format/ids.js +0 -64
- package/src/utils/format/index.js +0 -64
- package/src/utils/format/message.js +0 -88
- package/src/utils/format/presence.js +0 -132
- package/src/utils/format/readTyp.js +0 -44
- package/src/utils/format/thread.js +0 -42
- package/src/utils/format/utils.js +0 -141
- package/src/utils/headers.js +0 -115
- package/src/utils/loginParser/autoLogin.js +0 -125
- package/src/utils/loginParser/helpers.js +0 -43
- package/src/utils/loginParser/index.js +0 -10
- package/src/utils/loginParser/parseAndCheckLogin.js +0 -220
- package/src/utils/loginParser/textUtils.js +0 -28
- package/src/utils/request/client.js +0 -26
- package/src/utils/request/config.js +0 -23
- package/src/utils/request/defaults.js +0 -46
- package/src/utils/request/helpers.js +0 -46
- package/src/utils/request/index.js +0 -17
- package/src/utils/request/methods.js +0 -163
- package/src/utils/request/proxy.js +0 -21
- package/src/utils/request/retry.js +0 -77
- package/src/utils/request/sanitize.js +0 -49
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Socket/MQTT entry: wires getSeqID -> listenMqtt, middleware, stopListening, and post guard.
|
|
4
|
-
* Exported as the function that attaches listenMqtt + middleware API to the login context.
|
|
5
|
-
*/
|
|
6
|
-
const mqtt = require("mqtt");
|
|
7
|
-
const WebSocket = require("ws");
|
|
8
|
-
const HttpsProxyAgent = require("https-proxy-agent");
|
|
9
|
-
const EventEmitter = require("events");
|
|
10
|
-
const logger = require("../../../func/logger");
|
|
11
|
-
const { parseAndCheckLogin } = require("../../utils/client");
|
|
12
|
-
const { buildProxy, buildStream } = require("./detail/buildStream");
|
|
13
|
-
const { topics } = require("./detail/constants");
|
|
14
|
-
const createParseDelta = require("./core/parseDelta");
|
|
15
|
-
const createListenMqtt = require("./core/connectMqtt");
|
|
16
|
-
const createGetSeqID = require("./core/getSeqID");
|
|
17
|
-
const getTaskResponseData = require("./core/getTaskResponseData");
|
|
18
|
-
const createEmitAuth = require("./core/emitAuth");
|
|
19
|
-
const createMiddlewareSystem = require("./middleware");
|
|
20
|
-
|
|
21
|
-
const CYCLE_MS_DEFAULT = 60 * 60 * 1000;
|
|
22
|
-
const RECONNECT_DELAY_MS_DEFAULT = 2000;
|
|
23
|
-
const UNSUB_ALL_TIMEOUT_MS = 5000;
|
|
24
|
-
|
|
25
|
-
const parseDelta = createParseDelta({ parseAndCheckLogin });
|
|
26
|
-
const emitAuth = createEmitAuth({ logger });
|
|
27
|
-
const listenMqtt = createListenMqtt({ WebSocket, mqtt, HttpsProxyAgent, buildStream, buildProxy, topics, parseDelta, getTaskResponseData, logger, emitAuth });
|
|
28
|
-
const getSeqIDFactory = createGetSeqID({ parseAndCheckLogin, listenMqtt, logger, emitAuth });
|
|
29
|
-
|
|
30
|
-
const MQTT_DEFAULTS = { cycleMs: CYCLE_MS_DEFAULT, reconnectDelayMs: RECONNECT_DELAY_MS_DEFAULT, autoReconnect: true, reconnectAfterStop: false };
|
|
31
|
-
function mqttConf(ctx, overrides) {
|
|
32
|
-
ctx._mqttOpt = Object.assign({}, MQTT_DEFAULTS, ctx._mqttOpt || {}, overrides || {});
|
|
33
|
-
if (typeof ctx._mqttOpt.autoReconnect === "boolean") ctx.globalOptions.autoReconnect = ctx._mqttOpt.autoReconnect;
|
|
34
|
-
return ctx._mqttOpt;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
module.exports = function (defaultFuncs, api, ctx, opts) {
|
|
38
|
-
const identity = function () { };
|
|
39
|
-
let globalCallback = identity;
|
|
40
|
-
|
|
41
|
-
// Initialize middleware system if not already initialized
|
|
42
|
-
if (!ctx._middleware) {
|
|
43
|
-
ctx._middleware = createMiddlewareSystem();
|
|
44
|
-
}
|
|
45
|
-
const middleware = ctx._middleware;
|
|
46
|
-
|
|
47
|
-
function installPostGuard() {
|
|
48
|
-
if (ctx._postGuarded) return defaultFuncs.post;
|
|
49
|
-
const rawPost = defaultFuncs.post && defaultFuncs.post.bind(defaultFuncs);
|
|
50
|
-
if (!rawPost) return defaultFuncs.post;
|
|
51
|
-
|
|
52
|
-
function postSafe(...args) {
|
|
53
|
-
return rawPost(...args).catch(err => {
|
|
54
|
-
const msg = (err && err.error) || (err && err.message) || String(err || "");
|
|
55
|
-
if (/Not logged in|blocked the login/i.test(msg)) {
|
|
56
|
-
emitAuth(
|
|
57
|
-
ctx,
|
|
58
|
-
api,
|
|
59
|
-
globalCallback,
|
|
60
|
-
/blocked/i.test(msg) ? "login_blocked" : "not_logged_in",
|
|
61
|
-
msg
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
throw err;
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
defaultFuncs.post = postSafe;
|
|
68
|
-
ctx._postGuarded = true;
|
|
69
|
-
return postSafe;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let conf = mqttConf(ctx, opts);
|
|
73
|
-
|
|
74
|
-
function getSeqIDWrapper() {
|
|
75
|
-
if (ctx._ending && !ctx._cycling) {
|
|
76
|
-
logger("mqtt getSeqID skipped - ending", "warn");
|
|
77
|
-
return Promise.resolve();
|
|
78
|
-
}
|
|
79
|
-
const form = {
|
|
80
|
-
av: ctx.globalOptions.pageID,
|
|
81
|
-
queries: JSON.stringify({
|
|
82
|
-
o0: {
|
|
83
|
-
doc_id: "3336396659757871",
|
|
84
|
-
query_params: {
|
|
85
|
-
limit: 1, before: null, tags: ["INBOX"],
|
|
86
|
-
includeDeliveryReceipts: false, includeSeqID: true
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
})
|
|
90
|
-
};
|
|
91
|
-
logger("mqtt getSeqID call", "info");
|
|
92
|
-
return getSeqIDFactory(defaultFuncs, api, ctx, globalCallback, form)
|
|
93
|
-
.then(() => {
|
|
94
|
-
logger("mqtt getSeqID done", "info");
|
|
95
|
-
ctx._cycling = false;
|
|
96
|
-
})
|
|
97
|
-
.catch(e => {
|
|
98
|
-
ctx._cycling = false;
|
|
99
|
-
const errMsg = e && e.message ? e.message : String(e || "Unknown error");
|
|
100
|
-
logger(`mqtt getSeqID error: ${errMsg}`, "error");
|
|
101
|
-
// Don't reconnect if we're ending
|
|
102
|
-
if (ctx._ending) return;
|
|
103
|
-
// Retry after delay if autoReconnect is enabled
|
|
104
|
-
if (ctx.globalOptions.autoReconnect) {
|
|
105
|
-
const d = conf.reconnectDelayMs;
|
|
106
|
-
logger(`mqtt getSeqID will retry in ${d}ms`, "warn");
|
|
107
|
-
setTimeout(() => {
|
|
108
|
-
if (!ctx._ending) getSeqIDWrapper();
|
|
109
|
-
}, d);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function isConnected() {
|
|
115
|
-
return !!(ctx.mqttClient && ctx.mqttClient.connected);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function unsubAll(cb) {
|
|
119
|
-
if (!isConnected()) {
|
|
120
|
-
if (cb) setTimeout(cb, 0);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
let pending = topics.length;
|
|
124
|
-
if (!pending) {
|
|
125
|
-
if (cb) setTimeout(cb, 0);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
let fired = false;
|
|
129
|
-
const timeout = setTimeout(() => {
|
|
130
|
-
if (!fired) {
|
|
131
|
-
fired = true;
|
|
132
|
-
logger("unsubAll timeout, proceeding anyway", "warn");
|
|
133
|
-
if (cb) cb();
|
|
134
|
-
}
|
|
135
|
-
}, UNSUB_ALL_TIMEOUT_MS);
|
|
136
|
-
|
|
137
|
-
topics.forEach(t => {
|
|
138
|
-
try {
|
|
139
|
-
ctx.mqttClient.unsubscribe(t, () => {
|
|
140
|
-
if (--pending === 0 && !fired) {
|
|
141
|
-
clearTimeout(timeout);
|
|
142
|
-
fired = true;
|
|
143
|
-
if (cb) cb();
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
} catch (err) {
|
|
147
|
-
logger(`unsubAll error for topic ${t}: ${err && err.message ? err.message : String(err)}`, "warn");
|
|
148
|
-
if (--pending === 0 && !fired) {
|
|
149
|
-
clearTimeout(timeout);
|
|
150
|
-
fired = true;
|
|
151
|
-
if (cb) cb();
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function endQuietly(next) {
|
|
158
|
-
const finish = () => {
|
|
159
|
-
try {
|
|
160
|
-
if (ctx.mqttClient) {
|
|
161
|
-
ctx.mqttClient.removeAllListeners();
|
|
162
|
-
}
|
|
163
|
-
} catch (_) { }
|
|
164
|
-
ctx.mqttClient = undefined;
|
|
165
|
-
ctx.lastSeqId = null;
|
|
166
|
-
ctx.syncToken = undefined;
|
|
167
|
-
ctx.t_mqttCalled = false;
|
|
168
|
-
ctx._ending = false;
|
|
169
|
-
ctx._cycling = false;
|
|
170
|
-
if (ctx._reconnectTimer) {
|
|
171
|
-
clearTimeout(ctx._reconnectTimer);
|
|
172
|
-
ctx._reconnectTimer = null;
|
|
173
|
-
}
|
|
174
|
-
if (ctx._rTimeout) {
|
|
175
|
-
clearTimeout(ctx._rTimeout);
|
|
176
|
-
ctx._rTimeout = null;
|
|
177
|
-
}
|
|
178
|
-
// Clean up tasks Map to prevent memory leak
|
|
179
|
-
if (ctx.tasks && ctx.tasks instanceof Map) {
|
|
180
|
-
ctx.tasks.clear();
|
|
181
|
-
}
|
|
182
|
-
// Clean up userInfo intervals
|
|
183
|
-
if (ctx._userInfoIntervals && Array.isArray(ctx._userInfoIntervals)) {
|
|
184
|
-
ctx._userInfoIntervals.forEach(interval => {
|
|
185
|
-
try {
|
|
186
|
-
clearInterval(interval);
|
|
187
|
-
} catch (_) { }
|
|
188
|
-
});
|
|
189
|
-
ctx._userInfoIntervals = [];
|
|
190
|
-
}
|
|
191
|
-
// Clean up autoSave intervals
|
|
192
|
-
if (ctx._autoSaveInterval && Array.isArray(ctx._autoSaveInterval)) {
|
|
193
|
-
ctx._autoSaveInterval.forEach(interval => {
|
|
194
|
-
try {
|
|
195
|
-
clearInterval(interval);
|
|
196
|
-
} catch (_) { }
|
|
197
|
-
});
|
|
198
|
-
ctx._autoSaveInterval = [];
|
|
199
|
-
}
|
|
200
|
-
// Clean up scheduler
|
|
201
|
-
if (ctx._scheduler && typeof ctx._scheduler.destroy === "function") {
|
|
202
|
-
try {
|
|
203
|
-
ctx._scheduler.destroy();
|
|
204
|
-
} catch (_) { }
|
|
205
|
-
ctx._scheduler = undefined;
|
|
206
|
-
}
|
|
207
|
-
next && next();
|
|
208
|
-
};
|
|
209
|
-
try {
|
|
210
|
-
if (ctx.mqttClient) {
|
|
211
|
-
if (isConnected()) {
|
|
212
|
-
try {
|
|
213
|
-
ctx.mqttClient.publish("/browser_close", "{}", { qos: 0 });
|
|
214
|
-
} catch (_) { }
|
|
215
|
-
}
|
|
216
|
-
ctx.mqttClient.end(true, finish);
|
|
217
|
-
} else finish();
|
|
218
|
-
} catch (_) { finish(); }
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function delayedReconnect() {
|
|
222
|
-
const d = conf.reconnectDelayMs;
|
|
223
|
-
logger(`mqtt reconnect in ${d}ms`, "info");
|
|
224
|
-
setTimeout(() => getSeqIDWrapper(), d);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function forceCycle() {
|
|
228
|
-
if (ctx._cycling) {
|
|
229
|
-
logger("mqtt force cycle already in progress", "warn");
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
ctx._cycling = true;
|
|
233
|
-
ctx._ending = true;
|
|
234
|
-
logger("mqtt force cycle begin", "warn");
|
|
235
|
-
unsubAll(() => endQuietly(() => delayedReconnect()));
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return function (callback) {
|
|
239
|
-
class MessageEmitter extends EventEmitter {
|
|
240
|
-
stopListening(callback2) {
|
|
241
|
-
const cb = callback2 || function () { };
|
|
242
|
-
logger("mqtt stop requested", "info");
|
|
243
|
-
globalCallback = identity;
|
|
244
|
-
|
|
245
|
-
if (ctx._autoCycleTimer) {
|
|
246
|
-
clearInterval(ctx._autoCycleTimer);
|
|
247
|
-
ctx._autoCycleTimer = null;
|
|
248
|
-
logger("mqtt auto-cycle cleared", "info");
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (ctx._reconnectTimer) {
|
|
252
|
-
clearTimeout(ctx._reconnectTimer);
|
|
253
|
-
ctx._reconnectTimer = null;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
ctx._ending = true;
|
|
257
|
-
unsubAll(() => endQuietly(() => {
|
|
258
|
-
logger("mqtt stopped", "info");
|
|
259
|
-
cb();
|
|
260
|
-
conf = mqttConf(ctx, conf);
|
|
261
|
-
if (conf.reconnectAfterStop) delayedReconnect();
|
|
262
|
-
}));
|
|
263
|
-
}
|
|
264
|
-
async stopListeningAsync() {
|
|
265
|
-
return new Promise(resolve => { this.stopListening(resolve); });
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const msgEmitter = new MessageEmitter();
|
|
270
|
-
|
|
271
|
-
// Original callback without middleware
|
|
272
|
-
const originalCallback = callback || function (error, message) {
|
|
273
|
-
if (error) { logger("mqtt emit error", "error"); return msgEmitter.emit("error", error); }
|
|
274
|
-
msgEmitter.emit("message", message);
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
// Only wrap callback with middleware if middleware exists
|
|
278
|
-
// If no middleware, use callback directly for better performance
|
|
279
|
-
if (middleware.count > 0) {
|
|
280
|
-
globalCallback = middleware.wrapCallback(originalCallback);
|
|
281
|
-
} else {
|
|
282
|
-
globalCallback = originalCallback;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
conf = mqttConf(ctx, conf);
|
|
286
|
-
|
|
287
|
-
installPostGuard();
|
|
288
|
-
|
|
289
|
-
if (!ctx.firstListen) ctx.lastSeqId = null;
|
|
290
|
-
ctx.syncToken = undefined;
|
|
291
|
-
ctx.t_mqttCalled = false;
|
|
292
|
-
|
|
293
|
-
if (ctx._autoCycleTimer) { clearInterval(ctx._autoCycleTimer); ctx._autoCycleTimer = null; }
|
|
294
|
-
if (conf.cycleMs && conf.cycleMs > 0) {
|
|
295
|
-
ctx._autoCycleTimer = setInterval(forceCycle, conf.cycleMs);
|
|
296
|
-
logger(`mqtt auto-cycle enabled ${conf.cycleMs}ms`, "info");
|
|
297
|
-
} else {
|
|
298
|
-
logger("mqtt auto-cycle disabled", "info");
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (!ctx.firstListen || !ctx.lastSeqId) getSeqIDWrapper();
|
|
302
|
-
else {
|
|
303
|
-
logger("mqtt starting listenMqtt", "info");
|
|
304
|
-
listenMqtt(defaultFuncs, api, ctx, globalCallback);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
api.stopListening = msgEmitter.stopListening;
|
|
308
|
-
api.stopListeningAsync = msgEmitter.stopListeningAsync;
|
|
309
|
-
|
|
310
|
-
// Store original callback for re-wrapping when middleware is added/removed
|
|
311
|
-
let currentOriginalCallback = originalCallback;
|
|
312
|
-
let currentGlobalCallback = globalCallback;
|
|
313
|
-
|
|
314
|
-
// Function to re-wrap callback when middleware changes
|
|
315
|
-
function rewrapCallbackIfNeeded() {
|
|
316
|
-
if (!ctx.mqttClient || ctx._ending) return; // Not listening or ending
|
|
317
|
-
|
|
318
|
-
const hasMiddleware = middleware.count > 0;
|
|
319
|
-
const isWrapped = currentGlobalCallback !== currentOriginalCallback;
|
|
320
|
-
|
|
321
|
-
// If middleware exists but callback is not wrapped, wrap it
|
|
322
|
-
if (hasMiddleware && !isWrapped) {
|
|
323
|
-
currentGlobalCallback = middleware.wrapCallback(currentOriginalCallback);
|
|
324
|
-
globalCallback = currentGlobalCallback;
|
|
325
|
-
logger("Middleware added - callback re-wrapped", "info");
|
|
326
|
-
}
|
|
327
|
-
// If no middleware but callback is wrapped, unwrap it
|
|
328
|
-
else if (!hasMiddleware && isWrapped) {
|
|
329
|
-
currentGlobalCallback = currentOriginalCallback;
|
|
330
|
-
globalCallback = currentGlobalCallback;
|
|
331
|
-
logger("All middleware removed - callback unwrapped", "info");
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Expose middleware API with re-wrapping support
|
|
336
|
-
api.useMiddleware = function (middlewareFn, fn) {
|
|
337
|
-
const result = middleware.use(middlewareFn, fn);
|
|
338
|
-
rewrapCallbackIfNeeded();
|
|
339
|
-
return result;
|
|
340
|
-
};
|
|
341
|
-
api.removeMiddleware = function (identifier) {
|
|
342
|
-
const result = middleware.remove(identifier);
|
|
343
|
-
rewrapCallbackIfNeeded();
|
|
344
|
-
return result;
|
|
345
|
-
};
|
|
346
|
-
api.clearMiddleware = function () {
|
|
347
|
-
const result = middleware.clear();
|
|
348
|
-
rewrapCallbackIfNeeded();
|
|
349
|
-
return result;
|
|
350
|
-
};
|
|
351
|
-
api.listMiddleware = function () {
|
|
352
|
-
return middleware.list();
|
|
353
|
-
};
|
|
354
|
-
api.setMiddlewareEnabled = function (name, enabled) {
|
|
355
|
-
const result = middleware.setEnabled(name, enabled);
|
|
356
|
-
rewrapCallbackIfNeeded();
|
|
357
|
-
return result;
|
|
358
|
-
};
|
|
359
|
-
// Avoid crashing on restart: defineProperty throws if already defined and non-configurable.
|
|
360
|
-
const existingMiddlewareCount = Object.getOwnPropertyDescriptor(api, "middlewareCount");
|
|
361
|
-
if (!existingMiddlewareCount) {
|
|
362
|
-
Object.defineProperty(api, "middlewareCount", {
|
|
363
|
-
configurable: true,
|
|
364
|
-
enumerable: false,
|
|
365
|
-
get: function () { return (ctx._middleware && ctx._middleware.count) || 0; }
|
|
366
|
-
});
|
|
367
|
-
} else if (existingMiddlewareCount.configurable) {
|
|
368
|
-
Object.defineProperty(api, "middlewareCount", {
|
|
369
|
-
configurable: true,
|
|
370
|
-
enumerable: existingMiddlewareCount.enumerable,
|
|
371
|
-
get: function () { return (ctx._middleware && ctx._middleware.count) || 0; }
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return msgEmitter;
|
|
376
|
-
};
|
|
377
|
-
};
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
const logger = require("../../../../func/logger");
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Middleware system for filtering and processing events before they are emitted
|
|
6
|
-
*
|
|
7
|
-
* Middleware functions receive (event, next) where:
|
|
8
|
-
* - event: The event object (can be modified)
|
|
9
|
-
* - next: Function to call to continue to next middleware
|
|
10
|
-
* - next() - continue to next middleware
|
|
11
|
-
* - next(false) or next(null) - stop processing, don't emit event
|
|
12
|
-
* - next(error) - emit error instead
|
|
13
|
-
*/
|
|
14
|
-
module.exports = function createMiddlewareSystem() {
|
|
15
|
-
const middlewareStack = [];
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Add middleware to the stack
|
|
19
|
-
* @param {Function|string} middleware - Middleware function or name for named middleware
|
|
20
|
-
* @param {Function} [fn] - Middleware function (if first param is name)
|
|
21
|
-
* @returns {Function} Unsubscribe function
|
|
22
|
-
*/
|
|
23
|
-
function use(middleware, fn) {
|
|
24
|
-
let middlewareFn, name;
|
|
25
|
-
|
|
26
|
-
if (typeof middleware === "string" && typeof fn === "function") {
|
|
27
|
-
name = middleware;
|
|
28
|
-
middlewareFn = fn;
|
|
29
|
-
} else if (typeof middleware === "function") {
|
|
30
|
-
middlewareFn = middleware;
|
|
31
|
-
name = `middleware_${middlewareStack.length}`;
|
|
32
|
-
} else {
|
|
33
|
-
throw new Error("Middleware must be a function or (name, function)");
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const wrapped = {
|
|
37
|
-
name,
|
|
38
|
-
fn: middlewareFn,
|
|
39
|
-
enabled: true
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
middlewareStack.push(wrapped);
|
|
43
|
-
logger(`Middleware "${name}" added`, "info");
|
|
44
|
-
|
|
45
|
-
// Return unsubscribe function
|
|
46
|
-
return function remove() {
|
|
47
|
-
const index = middlewareStack.indexOf(wrapped);
|
|
48
|
-
if (index !== -1) {
|
|
49
|
-
middlewareStack.splice(index, 1);
|
|
50
|
-
logger(`Middleware "${name}" removed`, "info");
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Remove middleware by name or function
|
|
57
|
-
* @param {string|Function} identifier - Name or function to remove
|
|
58
|
-
* @returns {boolean} True if removed
|
|
59
|
-
*/
|
|
60
|
-
function remove(identifier) {
|
|
61
|
-
if (typeof identifier === "string") {
|
|
62
|
-
const index = middlewareStack.findIndex(m => m.name === identifier);
|
|
63
|
-
if (index !== -1) {
|
|
64
|
-
const removed = middlewareStack.splice(index, 1)[0];
|
|
65
|
-
logger(`Middleware "${removed.name}" removed`, "info");
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
return false;
|
|
69
|
-
} else if (typeof identifier === "function") {
|
|
70
|
-
const index = middlewareStack.findIndex(m => m.fn === identifier);
|
|
71
|
-
if (index !== -1) {
|
|
72
|
-
const removed = middlewareStack.splice(index, 1)[0];
|
|
73
|
-
logger(`Middleware "${removed.name}" removed`, "info");
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Remove all middleware
|
|
83
|
-
*/
|
|
84
|
-
function clear() {
|
|
85
|
-
const count = middlewareStack.length;
|
|
86
|
-
middlewareStack.length = 0;
|
|
87
|
-
logger(`All middleware cleared (${count} removed)`, "info");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Get list of middleware names
|
|
92
|
-
* @returns {string[]} Array of middleware names
|
|
93
|
-
*/
|
|
94
|
-
function list() {
|
|
95
|
-
return middlewareStack.filter(m => m.enabled).map(m => m.name);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Enable/disable middleware by name
|
|
100
|
-
* @param {string} name - Middleware name
|
|
101
|
-
* @param {boolean} enabled - Enable or disable
|
|
102
|
-
* @returns {boolean} True if found and updated
|
|
103
|
-
*/
|
|
104
|
-
function setEnabled(name, enabled) {
|
|
105
|
-
const middleware = middlewareStack.find(m => m.name === name);
|
|
106
|
-
if (middleware) {
|
|
107
|
-
middleware.enabled = enabled;
|
|
108
|
-
logger(`Middleware "${name}" ${enabled ? "enabled" : "disabled"}`, "info");
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Process event through middleware stack
|
|
116
|
-
* @param {*} event - Event object
|
|
117
|
-
* @param {Function} finalCallback - Callback to call after all middleware
|
|
118
|
-
* @returns {Promise} Promise that resolves when processing is complete
|
|
119
|
-
*/
|
|
120
|
-
function process(event, finalCallback) {
|
|
121
|
-
if (!middlewareStack.length) {
|
|
122
|
-
return finalCallback(null, event);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
let index = 0;
|
|
126
|
-
const enabledMiddleware = middlewareStack.filter(m => m.enabled);
|
|
127
|
-
|
|
128
|
-
function next(err) {
|
|
129
|
-
// Error occurred, stop processing
|
|
130
|
-
if (err && err !== false && err !== null) {
|
|
131
|
-
return finalCallback(err, null);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Explicitly stopped (next(false) or next(null))
|
|
135
|
-
if (err === false || err === null) {
|
|
136
|
-
return finalCallback(null, null); // null event means don't emit
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// No more middleware, call final callback
|
|
140
|
-
if (index >= enabledMiddleware.length) {
|
|
141
|
-
return finalCallback(null, event);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Get next middleware
|
|
145
|
-
const middleware = enabledMiddleware[index++];
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
// Call middleware with event and next
|
|
149
|
-
const result = middleware.fn(event, next);
|
|
150
|
-
|
|
151
|
-
// If middleware returns a promise, handle it
|
|
152
|
-
if (result && typeof result.then === "function") {
|
|
153
|
-
result
|
|
154
|
-
.then(() => next())
|
|
155
|
-
.catch(err => next(err));
|
|
156
|
-
} else if (result === false || result === null) {
|
|
157
|
-
// Middleware returned false/null, stop processing
|
|
158
|
-
finalCallback(null, null);
|
|
159
|
-
}
|
|
160
|
-
// If middleware called next() synchronously, it will continue
|
|
161
|
-
} catch (err) {
|
|
162
|
-
logger(`Middleware "${middleware.name}" error: ${err && err.message ? err.message : String(err)}`, "error");
|
|
163
|
-
next(err);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Start processing
|
|
168
|
-
next();
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Wrap a callback with middleware processing
|
|
173
|
-
* @param {Function} callback - Original callback (err, event)
|
|
174
|
-
* @returns {Function} Wrapped callback
|
|
175
|
-
*/
|
|
176
|
-
function wrapCallback(callback) {
|
|
177
|
-
return function wrappedCallback(err, event) {
|
|
178
|
-
if (err) {
|
|
179
|
-
// Errors bypass middleware
|
|
180
|
-
return callback(err, null);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (!event) {
|
|
184
|
-
return callback(null, null);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Process event through middleware
|
|
188
|
-
process(event, (middlewareErr, processedEvent) => {
|
|
189
|
-
if (middlewareErr) {
|
|
190
|
-
return callback(middlewareErr, null);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// If processedEvent is null, middleware blocked the event
|
|
194
|
-
if (processedEvent === null) {
|
|
195
|
-
return; // Don't emit
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Emit processed event
|
|
199
|
-
callback(null, processedEvent);
|
|
200
|
-
});
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return {
|
|
205
|
-
use,
|
|
206
|
-
remove,
|
|
207
|
-
clear,
|
|
208
|
-
list,
|
|
209
|
-
setEnabled,
|
|
210
|
-
process,
|
|
211
|
-
wrapCallback,
|
|
212
|
-
get count() {
|
|
213
|
-
return middlewareStack.filter(m => m.enabled).length;
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
};
|