redux-cluster 1.9.2 → 2.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 +21 -21
- package/README.md +345 -471
- package/dist/cjs/core/backup.d.ts +10 -0
- package/dist/cjs/core/backup.d.ts.map +1 -0
- package/dist/cjs/core/backup.js +166 -0
- package/dist/cjs/core/redux-cluster.d.ts +47 -0
- package/dist/cjs/core/redux-cluster.d.ts.map +1 -0
- package/dist/cjs/core/redux-cluster.js +367 -0
- package/dist/cjs/index.d.ts +22 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +43 -0
- package/dist/cjs/network/client.d.ts +23 -0
- package/dist/cjs/network/client.d.ts.map +1 -0
- package/dist/cjs/network/client.js +251 -0
- package/dist/cjs/network/server.d.ts +39 -0
- package/dist/cjs/network/server.d.ts.map +1 -0
- package/dist/cjs/network/server.js +439 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/types/index.d.ts +125 -0
- package/dist/cjs/types/index.d.ts.map +1 -0
- package/dist/cjs/types/index.js +20 -0
- package/dist/cjs/utils/crypto.d.ts +22 -0
- package/dist/cjs/utils/crypto.d.ts.map +1 -0
- package/dist/cjs/utils/crypto.js +404 -0
- package/dist/esm/core/backup.d.ts +10 -0
- package/dist/esm/core/backup.d.ts.map +1 -0
- package/dist/esm/core/backup.js +134 -0
- package/dist/esm/core/redux-cluster.d.ts +47 -0
- package/dist/esm/core/redux-cluster.d.ts.map +1 -0
- package/dist/esm/core/redux-cluster.js +376 -0
- package/dist/esm/index.d.ts +22 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +25 -0
- package/dist/esm/network/client.d.ts +23 -0
- package/dist/esm/network/client.d.ts.map +1 -0
- package/dist/esm/network/client.js +221 -0
- package/dist/esm/network/server.d.ts +39 -0
- package/dist/esm/network/server.d.ts.map +1 -0
- package/dist/esm/network/server.js +408 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/types/index.d.ts +125 -0
- package/dist/esm/types/index.d.ts.map +1 -0
- package/dist/esm/types/index.js +17 -0
- package/dist/esm/utils/crypto.d.ts +22 -0
- package/dist/esm/utils/crypto.d.ts.map +1 -0
- package/dist/esm/utils/crypto.js +351 -0
- package/package.json +115 -42
- package/index.js +0 -678
- package/test.auto.js +0 -94
- package/test.auto.proc1.js +0 -97
- package/test.auto.proc2.js +0 -85
- package/test.visual.client.highload.js +0 -102
- package/test.visual.client.js +0 -103
- package/test.visual.error.js +0 -45
- package/test.visual.js +0 -97
- package/test.visual.server.highload.js +0 -102
- package/test.visual.server.js +0 -103
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.hasher = hasher;
|
|
37
|
+
exports.createCipher = createCipher;
|
|
38
|
+
exports.createDecipher = createDecipher;
|
|
39
|
+
exports.encrypter = encrypter;
|
|
40
|
+
exports.decrypter = decrypter;
|
|
41
|
+
exports.deepClone = deepClone;
|
|
42
|
+
exports.universalClone = universalClone;
|
|
43
|
+
exports.protoObjectClone = protoObjectClone;
|
|
44
|
+
exports.universalSerialize = universalSerialize;
|
|
45
|
+
exports.universalDeserialize = universalDeserialize;
|
|
46
|
+
exports.serializeProtoObject = serializeProtoObject;
|
|
47
|
+
exports.deserializeProtoObject = deserializeProtoObject;
|
|
48
|
+
exports.createClassRegistry = createClassRegistry;
|
|
49
|
+
exports.createObjectStreamParser = createObjectStreamParser;
|
|
50
|
+
exports.createObjectStreamStringifier = createObjectStreamStringifier;
|
|
51
|
+
exports.createObjectStream = createObjectStream;
|
|
52
|
+
exports.createDeserializationStream = createDeserializationStream;
|
|
53
|
+
exports.createSerializationStream = createSerializationStream;
|
|
54
|
+
const crypto = __importStar(require("crypto"));
|
|
55
|
+
const stream_1 = require("stream");
|
|
56
|
+
const types_1 = require("../types");
|
|
57
|
+
// Import ObjectStream components - optional dependency
|
|
58
|
+
let Stringifer = null;
|
|
59
|
+
let Parser = null;
|
|
60
|
+
// Initialize ObjectStream if available
|
|
61
|
+
async function initializeObjectStream() {
|
|
62
|
+
try {
|
|
63
|
+
const objectStreamModule = await Promise.resolve().then(() => __importStar(require("@sergdudko/objectstream")));
|
|
64
|
+
Stringifer = objectStreamModule.Stringifer;
|
|
65
|
+
Parser = objectStreamModule.Parser;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// ObjectStream is optional
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Initialize once
|
|
72
|
+
initializeObjectStream();
|
|
73
|
+
// Import ProtoObject - optional dependency
|
|
74
|
+
let protoObjectConstructor = null;
|
|
75
|
+
// Initialize ProtoObject if available
|
|
76
|
+
async function initializeProtoObject() {
|
|
77
|
+
try {
|
|
78
|
+
const protoObjectModule = await Promise.resolve().then(() => __importStar(require("protoobject")));
|
|
79
|
+
protoObjectConstructor = protoObjectModule.ProtoObject ||
|
|
80
|
+
protoObjectModule.default ||
|
|
81
|
+
protoObjectModule;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// ProtoObject is optional
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Initialize once
|
|
88
|
+
initializeProtoObject();
|
|
89
|
+
// Check if ProtoObject is available
|
|
90
|
+
function isProtoObjectAvailable() {
|
|
91
|
+
return protoObjectConstructor !== null;
|
|
92
|
+
}
|
|
93
|
+
// Get ProtoObject class (proper TypeScript way)
|
|
94
|
+
function getProtoObjectClass() {
|
|
95
|
+
return protoObjectConstructor;
|
|
96
|
+
}
|
|
97
|
+
// Generate hash for reducer names
|
|
98
|
+
function hasher(input) {
|
|
99
|
+
return crypto.createHash("md5").update(input).digest("hex");
|
|
100
|
+
}
|
|
101
|
+
// Create cipher for encryption
|
|
102
|
+
function createCipher(key) {
|
|
103
|
+
const iv = crypto.randomBytes(16);
|
|
104
|
+
return crypto.createCipheriv("aes-256-ctr", Buffer.from(key), iv);
|
|
105
|
+
}
|
|
106
|
+
// Create decipher for decryption
|
|
107
|
+
function createDecipher(key, iv) {
|
|
108
|
+
return crypto.createDecipheriv("aes-256-ctr", Buffer.from(key), iv);
|
|
109
|
+
}
|
|
110
|
+
// Encryption function for backup
|
|
111
|
+
function encrypter(data, password) {
|
|
112
|
+
const key = crypto.scryptSync(password, "salt", 32);
|
|
113
|
+
const iv = crypto.randomBytes(16);
|
|
114
|
+
const cipher = crypto.createCipheriv("aes-256-ctr", key, iv);
|
|
115
|
+
let encrypted = cipher.update(data, "utf8", "hex");
|
|
116
|
+
encrypted += cipher.final("hex");
|
|
117
|
+
return iv.toString("hex") + ":" + encrypted;
|
|
118
|
+
}
|
|
119
|
+
// Decryption function for backup
|
|
120
|
+
function decrypter(encryptedData, password) {
|
|
121
|
+
const [ivHex, encrypted] = encryptedData.split(":");
|
|
122
|
+
const key = crypto.scryptSync(password, "salt", 32);
|
|
123
|
+
const iv = Buffer.from(ivHex, "hex");
|
|
124
|
+
const decipher = crypto.createDecipheriv("aes-256-ctr", key, iv);
|
|
125
|
+
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
126
|
+
decrypted += decipher.final("utf8");
|
|
127
|
+
return decrypted;
|
|
128
|
+
}
|
|
129
|
+
// Utility function for deep cloning objects
|
|
130
|
+
function deepClone(obj) {
|
|
131
|
+
if (typeof structuredClone === "function") {
|
|
132
|
+
return structuredClone(obj);
|
|
133
|
+
}
|
|
134
|
+
return JSON.parse(JSON.stringify(obj));
|
|
135
|
+
}
|
|
136
|
+
// Universal cloning function based on mode
|
|
137
|
+
function universalClone(obj, mode, _classRegistry) {
|
|
138
|
+
if (mode === types_1.SerializationMode.PROTOOBJECT && isProtoObjectAvailable()) {
|
|
139
|
+
return protoObjectClone(obj);
|
|
140
|
+
}
|
|
141
|
+
return deepClone(obj);
|
|
142
|
+
}
|
|
143
|
+
// ProtoObject cloning function that handles nested ProtoObject instances
|
|
144
|
+
function protoObjectClone(obj) {
|
|
145
|
+
const ProtoObjectClass = getProtoObjectClass();
|
|
146
|
+
if (!ProtoObjectClass) {
|
|
147
|
+
return deepClone(obj);
|
|
148
|
+
}
|
|
149
|
+
// If it's not a ProtoObject, use regular deep clone
|
|
150
|
+
if (!(obj instanceof ProtoObjectClass)) {
|
|
151
|
+
return deepClone(obj);
|
|
152
|
+
}
|
|
153
|
+
// Create a new ProtoObject with all properties
|
|
154
|
+
const clonedData = {};
|
|
155
|
+
// Copy all enumerable properties
|
|
156
|
+
for (const key of Object.keys(obj)) {
|
|
157
|
+
const value = obj[key];
|
|
158
|
+
if (value instanceof ProtoObjectClass) {
|
|
159
|
+
// Recursively clone nested ProtoObject
|
|
160
|
+
clonedData[key] = protoObjectClone(value);
|
|
161
|
+
}
|
|
162
|
+
else if (Array.isArray(value)) {
|
|
163
|
+
// Clone arrays with potential ProtoObject elements
|
|
164
|
+
clonedData[key] = value.map((item) => item instanceof ProtoObjectClass
|
|
165
|
+
? protoObjectClone(item)
|
|
166
|
+
: deepClone(item));
|
|
167
|
+
}
|
|
168
|
+
else if (value !== null && typeof value === "object") {
|
|
169
|
+
// Clone nested objects
|
|
170
|
+
clonedData[key] = deepClone(value);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// Copy primitive values
|
|
174
|
+
clonedData[key] = value;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return new ProtoObjectClass(clonedData);
|
|
178
|
+
}
|
|
179
|
+
// Universal serialization function
|
|
180
|
+
function universalSerialize(obj, mode, classRegistry) {
|
|
181
|
+
if (mode === types_1.SerializationMode.PROTOOBJECT && isProtoObjectAvailable()) {
|
|
182
|
+
return serializeProtoObject(obj, classRegistry);
|
|
183
|
+
}
|
|
184
|
+
// Default JSON serialization
|
|
185
|
+
return JSON.stringify(obj);
|
|
186
|
+
}
|
|
187
|
+
// Universal deserialization function
|
|
188
|
+
function universalDeserialize(str, mode, classRegistry) {
|
|
189
|
+
if (mode === types_1.SerializationMode.PROTOOBJECT && isProtoObjectAvailable()) {
|
|
190
|
+
return deserializeProtoObject(str, classRegistry);
|
|
191
|
+
}
|
|
192
|
+
// Default JSON deserialization
|
|
193
|
+
return JSON.parse(str);
|
|
194
|
+
}
|
|
195
|
+
// ProtoObject serialization for IPC with class information
|
|
196
|
+
function serializeProtoObject(obj, classRegistry) {
|
|
197
|
+
const ProtoObjectClass = getProtoObjectClass();
|
|
198
|
+
if (!ProtoObjectClass) {
|
|
199
|
+
return JSON.stringify(obj);
|
|
200
|
+
}
|
|
201
|
+
const serialize = (value) => {
|
|
202
|
+
if (value instanceof ProtoObjectClass) {
|
|
203
|
+
const data = {
|
|
204
|
+
__isProtoObject: true,
|
|
205
|
+
__className: value.constructor.name,
|
|
206
|
+
};
|
|
207
|
+
// Store class information if registry is provided
|
|
208
|
+
if (classRegistry && value.constructor.name !== "ProtoObject") {
|
|
209
|
+
classRegistry.set(value.constructor.name, value.constructor);
|
|
210
|
+
}
|
|
211
|
+
for (const key of Object.keys(value)) {
|
|
212
|
+
data[key] = serialize(value[key]);
|
|
213
|
+
}
|
|
214
|
+
return data;
|
|
215
|
+
}
|
|
216
|
+
else if (Array.isArray(value)) {
|
|
217
|
+
return value.map(serialize);
|
|
218
|
+
}
|
|
219
|
+
else if (value !== null && typeof value === "object") {
|
|
220
|
+
const result = {};
|
|
221
|
+
for (const [key, val] of Object.entries(value)) {
|
|
222
|
+
result[key] = serialize(val);
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
return value;
|
|
227
|
+
};
|
|
228
|
+
return JSON.stringify(serialize(obj));
|
|
229
|
+
}
|
|
230
|
+
// ProtoObject deserialization for IPC with class reconstruction
|
|
231
|
+
function deserializeProtoObject(str, classRegistry) {
|
|
232
|
+
const ProtoObjectClass = getProtoObjectClass();
|
|
233
|
+
if (!ProtoObjectClass) {
|
|
234
|
+
return JSON.parse(str);
|
|
235
|
+
}
|
|
236
|
+
const deserialize = (value) => {
|
|
237
|
+
if (value && typeof value === "object" && value.__isProtoObject) {
|
|
238
|
+
const className = value.__className;
|
|
239
|
+
const data = {};
|
|
240
|
+
for (const [key, val] of Object.entries(value)) {
|
|
241
|
+
if (key !== "__isProtoObject" && key !== "__className") {
|
|
242
|
+
data[key] = deserialize(val);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Try to use registered class, fallback to ProtoObject
|
|
246
|
+
if (className && classRegistry && classRegistry.has(className)) {
|
|
247
|
+
const ClassConstructor = classRegistry.get(className);
|
|
248
|
+
return new ClassConstructor(data);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
return new ProtoObjectClass(data);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
else if (Array.isArray(value)) {
|
|
255
|
+
return value.map(deserialize);
|
|
256
|
+
}
|
|
257
|
+
else if (value !== null && typeof value === "object") {
|
|
258
|
+
const result = {};
|
|
259
|
+
for (const [key, val] of Object.entries(value)) {
|
|
260
|
+
result[key] = deserialize(val);
|
|
261
|
+
}
|
|
262
|
+
return result;
|
|
263
|
+
}
|
|
264
|
+
return value;
|
|
265
|
+
};
|
|
266
|
+
return deserialize(JSON.parse(str));
|
|
267
|
+
}
|
|
268
|
+
// Helper function to create a shared class registry
|
|
269
|
+
function createClassRegistry() {
|
|
270
|
+
return new Map();
|
|
271
|
+
}
|
|
272
|
+
// Stream pipeline functions for buffer->json->class->json->buffer processing
|
|
273
|
+
// Create ObjectStream Parser for JSON parsing (Buffer -> Object)
|
|
274
|
+
function createObjectStreamParser() {
|
|
275
|
+
if (Parser) {
|
|
276
|
+
return new Parser();
|
|
277
|
+
}
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
// Create ObjectStream Stringifier for JSON serialization (Object -> Buffer)
|
|
281
|
+
function createObjectStreamStringifier() {
|
|
282
|
+
if (Stringifer) {
|
|
283
|
+
return new Stringifer();
|
|
284
|
+
}
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
// Legacy function for backward compatibility
|
|
288
|
+
function createObjectStream() {
|
|
289
|
+
return createObjectStreamParser();
|
|
290
|
+
}
|
|
291
|
+
// Create transform stream that converts JSON objects to ProtoObject instances
|
|
292
|
+
function createDeserializationStream(mode, classRegistry) {
|
|
293
|
+
return new stream_1.Transform({
|
|
294
|
+
objectMode: true,
|
|
295
|
+
transform(chunk, encoding, callback) {
|
|
296
|
+
try {
|
|
297
|
+
if (mode === types_1.SerializationMode.PROTOOBJECT &&
|
|
298
|
+
isProtoObjectAvailable()) {
|
|
299
|
+
const deserialized = deserializeFromObject(chunk, classRegistry);
|
|
300
|
+
callback(null, deserialized);
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
callback(null, chunk);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
callback(error instanceof Error ? error : new Error(String(error)));
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
// Create transform stream that converts ProtoObject instances to JSON objects
|
|
313
|
+
function createSerializationStream(mode, classRegistry) {
|
|
314
|
+
return new stream_1.Transform({
|
|
315
|
+
objectMode: true,
|
|
316
|
+
transform(chunk, encoding, callback) {
|
|
317
|
+
try {
|
|
318
|
+
if (mode === types_1.SerializationMode.PROTOOBJECT &&
|
|
319
|
+
isProtoObjectAvailable()) {
|
|
320
|
+
const serialized = serializeToObject(chunk, classRegistry);
|
|
321
|
+
callback(null, serialized);
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
callback(null, chunk);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
callback(error instanceof Error ? error : new Error(String(error)));
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
// Helper functions for object-level serialization/deserialization (without JSON.stringify)
|
|
334
|
+
function serializeToObject(obj, classRegistry) {
|
|
335
|
+
const ProtoObjectClass = getProtoObjectClass();
|
|
336
|
+
if (!ProtoObjectClass) {
|
|
337
|
+
return obj;
|
|
338
|
+
}
|
|
339
|
+
const serialize = (value) => {
|
|
340
|
+
if (value instanceof ProtoObjectClass) {
|
|
341
|
+
const data = {
|
|
342
|
+
__isProtoObject: true,
|
|
343
|
+
__className: value.constructor.name,
|
|
344
|
+
};
|
|
345
|
+
// Store class information if registry is provided
|
|
346
|
+
if (classRegistry && value.constructor.name !== "ProtoObject") {
|
|
347
|
+
classRegistry.set(value.constructor.name, value.constructor);
|
|
348
|
+
}
|
|
349
|
+
for (const key of Object.keys(value)) {
|
|
350
|
+
data[key] = serialize(value[key]);
|
|
351
|
+
}
|
|
352
|
+
return data;
|
|
353
|
+
}
|
|
354
|
+
else if (Array.isArray(value)) {
|
|
355
|
+
return value.map(serialize);
|
|
356
|
+
}
|
|
357
|
+
else if (value !== null && typeof value === "object") {
|
|
358
|
+
const result = {};
|
|
359
|
+
for (const [key, val] of Object.entries(value)) {
|
|
360
|
+
result[key] = serialize(val);
|
|
361
|
+
}
|
|
362
|
+
return result;
|
|
363
|
+
}
|
|
364
|
+
return value;
|
|
365
|
+
};
|
|
366
|
+
return serialize(obj);
|
|
367
|
+
}
|
|
368
|
+
function deserializeFromObject(obj, classRegistry) {
|
|
369
|
+
const ProtoObjectClass = getProtoObjectClass();
|
|
370
|
+
if (!ProtoObjectClass) {
|
|
371
|
+
return obj;
|
|
372
|
+
}
|
|
373
|
+
const deserialize = (value) => {
|
|
374
|
+
if (value && typeof value === "object" && value.__isProtoObject) {
|
|
375
|
+
const className = value.__className;
|
|
376
|
+
const data = {};
|
|
377
|
+
for (const [key, val] of Object.entries(value)) {
|
|
378
|
+
if (key !== "__isProtoObject" && key !== "__className") {
|
|
379
|
+
data[key] = deserialize(val);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// Try to use registered class, fallback to ProtoObject
|
|
383
|
+
if (className && classRegistry && classRegistry.has(className)) {
|
|
384
|
+
const ClassConstructor = classRegistry.get(className);
|
|
385
|
+
return new ClassConstructor(data);
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
return new ProtoObjectClass(data);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
else if (Array.isArray(value)) {
|
|
392
|
+
return value.map(deserialize);
|
|
393
|
+
}
|
|
394
|
+
else if (value !== null && typeof value === "object") {
|
|
395
|
+
const result = {};
|
|
396
|
+
for (const [key, val] of Object.entries(value)) {
|
|
397
|
+
result[key] = deserialize(val);
|
|
398
|
+
}
|
|
399
|
+
return result;
|
|
400
|
+
}
|
|
401
|
+
return value;
|
|
402
|
+
};
|
|
403
|
+
return deserialize(obj);
|
|
404
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BackupSettings, ReduxClusterStore } from "../types";
|
|
2
|
+
export declare class BackupManager<S = any> {
|
|
3
|
+
private store;
|
|
4
|
+
private settings;
|
|
5
|
+
private createBackupInstance?;
|
|
6
|
+
constructor(store: ReduxClusterStore<S>, settings: BackupSettings);
|
|
7
|
+
initialize(): Promise<boolean>;
|
|
8
|
+
private loadBackup;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=backup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../../src/core/backup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAe,MAAM,UAAU,CAAC;AAG1E,qBAAa,aAAa,CAAC,CAAC,GAAG,GAAG;IAI9B,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;IAJlB,OAAO,CAAC,oBAAoB,CAAC,CAAoB;gBAGvC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAC3B,QAAQ,EAAE,cAAc;IAGrB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;YAiB7B,UAAU;CA+CzB"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import { MessageType } from "../types";
|
|
3
|
+
import { encrypter, decrypter } from "../utils/crypto";
|
|
4
|
+
export class BackupManager {
|
|
5
|
+
store;
|
|
6
|
+
settings;
|
|
7
|
+
createBackupInstance;
|
|
8
|
+
constructor(store, settings) {
|
|
9
|
+
this.store = store;
|
|
10
|
+
this.settings = settings;
|
|
11
|
+
}
|
|
12
|
+
async initialize() {
|
|
13
|
+
try {
|
|
14
|
+
await this.loadBackup();
|
|
15
|
+
this.createBackupInstance = new BackupInstance(this.store, this.settings);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
if (err.message.toLowerCase().includes("no such file or directory")) {
|
|
20
|
+
this.createBackupInstance = new BackupInstance(this.store, this.settings);
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async loadBackup() {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
fs.readFile(this.settings.path, (err, data) => {
|
|
29
|
+
if (err) {
|
|
30
|
+
reject(new Error(`ReduxCluster.backup load error: ${err.message}`));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
let content = data.toString();
|
|
35
|
+
// Decrypt if key is provided
|
|
36
|
+
if (this.settings.key) {
|
|
37
|
+
content = decrypter(content, this.settings.key);
|
|
38
|
+
}
|
|
39
|
+
const state = JSON.parse(content);
|
|
40
|
+
// Restore state using internal method
|
|
41
|
+
if ("_internalSync" in this.store &&
|
|
42
|
+
typeof this.store._internalSync === "function") {
|
|
43
|
+
this.store._internalSync(state);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Fallback: use dispatchNEW if available, otherwise skip restore
|
|
47
|
+
if ("dispatchNEW" in this.store &&
|
|
48
|
+
typeof this.store.dispatchNEW === "function") {
|
|
49
|
+
this.store.dispatchNEW({
|
|
50
|
+
type: MessageType.SYNC,
|
|
51
|
+
payload: state,
|
|
52
|
+
_internal: true,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
setTimeout(() => resolve(), 500);
|
|
57
|
+
}
|
|
58
|
+
catch (parseErr) {
|
|
59
|
+
reject(new Error(`ReduxCluster.backup decoding error: ${parseErr.message}`));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
class BackupInstance {
|
|
66
|
+
store;
|
|
67
|
+
settings;
|
|
68
|
+
count = 0;
|
|
69
|
+
allowed = true;
|
|
70
|
+
unsubscribe = null;
|
|
71
|
+
constructor(store, settings) {
|
|
72
|
+
this.store = store;
|
|
73
|
+
this.settings = settings;
|
|
74
|
+
this.unsubscribe = this.store.subscribe(() => {
|
|
75
|
+
this.handleStateChange();
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
handleStateChange() {
|
|
79
|
+
if (typeof this.settings.timeout === "number") {
|
|
80
|
+
// Priority setting - timeout based backup
|
|
81
|
+
if (this.allowed) {
|
|
82
|
+
this.allowed = false;
|
|
83
|
+
setTimeout(() => {
|
|
84
|
+
this.write(true);
|
|
85
|
+
}, this.settings.timeout * 1000);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else if (typeof this.settings.count === "number") {
|
|
89
|
+
// Count based backup
|
|
90
|
+
this.count++;
|
|
91
|
+
if (this.count >= this.settings.count) {
|
|
92
|
+
this.count = 0;
|
|
93
|
+
this.write();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
write(restart = false, callback) {
|
|
98
|
+
if (this.allowed || restart) {
|
|
99
|
+
try {
|
|
100
|
+
let content = JSON.stringify(this.store.getState());
|
|
101
|
+
// Encrypt if key is provided
|
|
102
|
+
if (this.settings.key) {
|
|
103
|
+
content = encrypter(content, this.settings.key);
|
|
104
|
+
}
|
|
105
|
+
this.writeToFile(content, callback);
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
this.store.stderr(`ReduxCluster.backup write error: ${err.message}`);
|
|
109
|
+
this.allowed = false;
|
|
110
|
+
setTimeout(() => this.write(true, callback), 1000);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
writeToFile(content, callback) {
|
|
115
|
+
try {
|
|
116
|
+
fs.writeFileSync(this.settings.path, content);
|
|
117
|
+
this.allowed = true;
|
|
118
|
+
if (callback) {
|
|
119
|
+
callback(true);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
this.store.stderr(`ReduxCluster.backup write error: ${err.message}`);
|
|
124
|
+
this.allowed = false;
|
|
125
|
+
setTimeout(() => this.write(true, callback), 1000);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
dispose() {
|
|
129
|
+
if (this.unsubscribe) {
|
|
130
|
+
this.unsubscribe();
|
|
131
|
+
this.unsubscribe = null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Store, Reducer, Action } from "redux";
|
|
2
|
+
import { ReduxClusterStore, SyncMode, Role, ClusterMessage, ServerSettings, ClientSettings, BackupSettings, ErrorHandler, ReduxClusterConfig } from "../types";
|
|
3
|
+
import { ClusterServer } from "../network/server";
|
|
4
|
+
import { ClusterClient } from "../network/client";
|
|
5
|
+
export declare class ReduxCluster<S = any, A extends Action = Action> implements ReduxClusterStore<S, A> {
|
|
6
|
+
readonly dispatch: Store<S, A>["dispatch"];
|
|
7
|
+
readonly getState: Store<S, A>["getState"];
|
|
8
|
+
readonly subscribe: Store<S, A>["subscribe"];
|
|
9
|
+
readonly replaceReducer: Store<S, A>["replaceReducer"];
|
|
10
|
+
readonly [Symbol.observable]: Store<S, A>[typeof Symbol.observable];
|
|
11
|
+
readonly RCHash: string;
|
|
12
|
+
readonly version: string;
|
|
13
|
+
readonly homepage: string;
|
|
14
|
+
readonly role: Role[];
|
|
15
|
+
connected: boolean;
|
|
16
|
+
mode: SyncMode;
|
|
17
|
+
resync: number;
|
|
18
|
+
stderr: ErrorHandler;
|
|
19
|
+
readonly config: ReduxClusterConfig;
|
|
20
|
+
private readonly altReducer;
|
|
21
|
+
private readonly defaultState;
|
|
22
|
+
private readonly store;
|
|
23
|
+
private readonly allsock;
|
|
24
|
+
private counter?;
|
|
25
|
+
private dispatchNEW?;
|
|
26
|
+
private unsubscribe?;
|
|
27
|
+
private classRegistry;
|
|
28
|
+
constructor(reducer: Reducer<S, A>, config?: ReduxClusterConfig);
|
|
29
|
+
private internalSync;
|
|
30
|
+
_internalSync(payload: S): void;
|
|
31
|
+
private createNewReducer;
|
|
32
|
+
private updateCounter;
|
|
33
|
+
private sendActionsToNodes;
|
|
34
|
+
private initializeClusterRole;
|
|
35
|
+
private initializeMaster;
|
|
36
|
+
private initializeWorker;
|
|
37
|
+
private handleMasterMessage;
|
|
38
|
+
private handleWorkerMessage;
|
|
39
|
+
sendtoall(message?: ClusterMessage): void;
|
|
40
|
+
sendtoallsock(message?: ClusterMessage): void;
|
|
41
|
+
createServer(settings?: ServerSettings): ClusterServer;
|
|
42
|
+
createClient(settings?: ClientSettings): ClusterClient;
|
|
43
|
+
backup(settings: BackupSettings): Promise<boolean>;
|
|
44
|
+
registerClass(name: string, classConstructor: any): void;
|
|
45
|
+
getRegisteredClasses(): string[];
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=redux-cluster.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redux-cluster.d.ts","sourceRoot":"","sources":["../../../src/core/redux-cluster.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAW5D,OAAO,EACL,iBAAiB,EACjB,QAAQ,EACR,IAAI,EACJ,cAAc,EAEd,cAAc,EACd,cAAc,EACd,cAAc,EACd,YAAY,EACZ,kBAAkB,EAEnB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAMlD,qBAAa,YAAY,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,CAC1D,YAAW,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC;IAGlC,SAAgB,QAAQ,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAClD,SAAgB,QAAQ,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAClD,SAAgB,SAAS,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IACpD,SAAgB,cAAc,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAC9D,SAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,MAAM,CAAC,UAAU,CAAC,CAAC;IAG3E,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,OAAO,EAAE,MAAM,CAAC;IAChC,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,IAAI,EAAE,IAAI,EAAE,CAAM;IAC3B,SAAS,UAAS;IAClB,IAAI,EAAE,QAAQ,CAAY;IAC1B,MAAM,SAAQ;IACd,MAAM,EAAE,YAAY,CAAiB;IAC5C,SAAgB,MAAM,EAAE,kBAAkB,CAAC;IAE3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;IAC3C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAI;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;IACnD,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,WAAW,CAAC,CAA0B;IAC9C,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,aAAa,CAAyB;gBAElC,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,GAAE,kBAAuB;IA4EnE,OAAO,CAAC,YAAY;IASb,aAAa,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI;IAItC,OAAO,CAAC,gBAAgB;IA2BxB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,kBAAkB;IA0B1B,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,gBAAgB;IA0BxB,OAAO,CAAC,gBAAgB;IAiCxB,OAAO,CAAC,mBAAmB;IAwD3B,OAAO,CAAC,mBAAmB;IAoCpB,SAAS,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI;IA6BzC,aAAa,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI;IAW7C,YAAY,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa;IAetD,YAAY,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa;IAetD,MAAM,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAKlD,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,GAAG,IAAI;IASxD,oBAAoB,IAAI,MAAM,EAAE;CAMxC"}
|