topsyde-utils 1.0.149 → 1.0.151

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.
Files changed (74) hide show
  1. package/dist/application.js.map +1 -1
  2. package/dist/client/rxjs/index.js.map +1 -1
  3. package/dist/client/rxjs/rxjs.js.map +1 -1
  4. package/dist/client/rxjs/useRxjs.js.map +1 -1
  5. package/dist/client/vite/plugins/index.js.map +1 -1
  6. package/dist/client/vite/plugins/topsydeUtilsVitePlugin.js.map +1 -1
  7. package/dist/consts.js.map +1 -1
  8. package/dist/enums.js.map +1 -1
  9. package/dist/errors.js.map +1 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/initializable.js.map +1 -1
  12. package/dist/server/bun/index.js.map +1 -1
  13. package/dist/server/bun/router/controller-discovery.js.map +1 -1
  14. package/dist/server/bun/router/index.js.map +1 -1
  15. package/dist/server/bun/router/router.internal.js.map +1 -1
  16. package/dist/server/bun/router/router.js.map +1 -1
  17. package/dist/server/bun/router/routes.js.map +1 -1
  18. package/dist/server/bun/websocket/Channel.js.map +1 -1
  19. package/dist/server/bun/websocket/Client.js.map +1 -1
  20. package/dist/server/bun/websocket/Message.js.map +1 -1
  21. package/dist/server/bun/websocket/Websocket.js.map +1 -1
  22. package/dist/server/bun/websocket/index.js.map +1 -1
  23. package/dist/server/bun/websocket/websocket.enums.js.map +1 -1
  24. package/dist/server/bun/websocket/websocket.guards.js.map +1 -1
  25. package/dist/server/bun/websocket/websocket.types.js.map +1 -1
  26. package/dist/server/controller.js.map +1 -1
  27. package/dist/server/index.js.map +1 -1
  28. package/dist/server/service.js.map +1 -1
  29. package/dist/singleton.js.map +1 -1
  30. package/dist/throwable.js.map +1 -1
  31. package/dist/types.js.map +1 -1
  32. package/dist/utils/Console.js.map +1 -1
  33. package/dist/utils/Guards.js.map +1 -1
  34. package/dist/utils/Lib.js.map +1 -1
  35. package/dist/utils/index.js.map +1 -1
  36. package/package.json +4 -3
  37. package/src/__tests__/app.test.ts +205 -0
  38. package/src/__tests__/singleton.test.ts +402 -0
  39. package/src/__tests__/type-inference.test.ts +60 -0
  40. package/src/application.ts +43 -0
  41. package/src/client/rxjs/index.ts +5 -0
  42. package/src/client/rxjs/rxjs.ts +122 -0
  43. package/src/client/rxjs/useRxjs.ts +111 -0
  44. package/src/client/vite/plugins/index.ts +5 -0
  45. package/src/client/vite/plugins/topsydeUtilsVitePlugin.ts +80 -0
  46. package/src/consts.ts +48 -0
  47. package/src/enums.ts +14 -0
  48. package/src/errors.ts +56 -0
  49. package/src/index.ts +81 -0
  50. package/src/initializable.ts +375 -0
  51. package/src/server/bun/index.ts +6 -0
  52. package/src/server/bun/router/controller-discovery.ts +94 -0
  53. package/src/server/bun/router/index.ts +9 -0
  54. package/src/server/bun/router/router.internal.ts +64 -0
  55. package/src/server/bun/router/router.ts +51 -0
  56. package/src/server/bun/router/routes.ts +7 -0
  57. package/src/server/bun/websocket/Channel.ts +157 -0
  58. package/src/server/bun/websocket/Client.ts +129 -0
  59. package/src/server/bun/websocket/Message.ts +106 -0
  60. package/src/server/bun/websocket/Websocket.ts +221 -0
  61. package/src/server/bun/websocket/index.ts +14 -0
  62. package/src/server/bun/websocket/websocket.enums.ts +22 -0
  63. package/src/server/bun/websocket/websocket.guards.ts +6 -0
  64. package/src/server/bun/websocket/websocket.types.ts +186 -0
  65. package/src/server/controller.ts +121 -0
  66. package/src/server/index.ts +7 -0
  67. package/src/server/service.ts +36 -0
  68. package/src/singleton.ts +28 -0
  69. package/src/throwable.ts +87 -0
  70. package/src/types.ts +10 -0
  71. package/src/utils/Console.ts +85 -0
  72. package/src/utils/Guards.ts +61 -0
  73. package/src/utils/Lib.ts +506 -0
  74. package/src/utils/index.ts +9 -0
@@ -0,0 +1,402 @@
1
+ import Singleton from "../singleton";
2
+ import Channel from "../server/bun/websocket/Channel";
3
+ import { BroadcastOptions, WebsocketStructuredMessage } from "../server/bun/websocket/websocket.types";
4
+ import * as app from "../server/bun/websocket";
5
+ import { Client } from "../server/bun/websocket";
6
+ import { Server } from "bun";
7
+
8
+ // Base class that extends Singleton
9
+ class BaseClass extends Singleton {
10
+ protected constructor() {
11
+ super();
12
+ }
13
+ public value: string = "base";
14
+
15
+ public getValue(): string {
16
+ return this.value;
17
+ }
18
+
19
+ public static staticMethod(): string {
20
+ return "base-static";
21
+ }
22
+ }
23
+
24
+ // Derived class that extends BaseClass
25
+ class DerivedClass extends BaseClass {
26
+ protected constructor() {
27
+ super();
28
+ }
29
+ public override value: string = "derived";
30
+
31
+ public override getValue(): string {
32
+ return "derived-" + this.value;
33
+ }
34
+
35
+ public static override staticMethod(): string {
36
+ return "derived-static";
37
+ }
38
+ }
39
+
40
+ // Another class that extends BaseClass
41
+ class AnotherDerivedClass extends BaseClass {
42
+ public override value: string = "another";
43
+
44
+ public static testStaticMethod(): string {
45
+ // This should call the staticMethod on the class it's called on
46
+ return this.staticMethod();
47
+ }
48
+ }
49
+
50
+ // Class with constructor parameters
51
+ class ParameterizedSingleton extends Singleton {
52
+ public readonly config: { name: string; id: number };
53
+
54
+ constructor(name: string, id: number) {
55
+ super();
56
+ this.config = { name, id };
57
+ }
58
+
59
+ public getConfig(): { name: string; id: number } {
60
+ return this.config;
61
+ }
62
+ }
63
+
64
+ // Multiple level inheritance
65
+ class Level1 extends Singleton {
66
+ protected constructor() {
67
+ super();
68
+ }
69
+ public level: number = 1;
70
+ }
71
+
72
+ class Level2 extends Level1 {
73
+ protected constructor() {
74
+ super();
75
+ }
76
+ public override level: number = 2;
77
+ }
78
+
79
+ class Level3 extends Level2 {
80
+ protected constructor() {
81
+ super();
82
+ }
83
+ public override level: number = 3;
84
+ }
85
+
86
+ describe("Singleton", () => {
87
+ beforeEach(() => {
88
+ // Reset the singleton instances before each test
89
+ Singleton.ResetInstances();
90
+ });
91
+
92
+ test("BaseClass should return a singleton instance", () => {
93
+ const instance1 = BaseClass.GetInstance<BaseClass>();
94
+ const instance2 = BaseClass.GetInstance<BaseClass>();
95
+
96
+ expect(instance1).toBe(instance2);
97
+ expect(instance1.getValue()).toBe("base");
98
+ });
99
+
100
+ test("Each class should have its own singleton instance", () => {
101
+ const baseInstance = BaseClass.GetInstance<BaseClass>();
102
+ const derivedInstance = DerivedClass.GetInstance<DerivedClass>();
103
+
104
+ // Each class should have its own instance
105
+ expect(derivedInstance).not.toBe(baseInstance);
106
+
107
+ // But the instances should have the correct class behavior
108
+ expect(baseInstance.getValue()).toBe("base");
109
+ expect(derivedInstance.getValue()).toBe("derived-derived");
110
+ });
111
+
112
+ test("Static methods should be called on the correct class", () => {
113
+ expect(BaseClass.staticMethod()).toBe("base-static");
114
+ expect(DerivedClass.staticMethod()).toBe("derived-static");
115
+
116
+ // In JavaScript, 'this' in a static method refers to the class it was called on
117
+ expect(AnotherDerivedClass.testStaticMethod()).toBe("base-static");
118
+ });
119
+
120
+ test("Multiple level inheritance should maintain separate instances", () => {
121
+ const level1 = Level1.GetInstance<Level1>();
122
+ const level2 = Level2.GetInstance<Level2>();
123
+ const level3 = Level3.GetInstance<Level3>();
124
+
125
+ // Each level should have its own instance
126
+ expect(level1).not.toBe(level2);
127
+ expect(level2).not.toBe(level3);
128
+ expect(level1).not.toBe(level3);
129
+
130
+ // Each instance should have the correct level value
131
+ expect(level1.level).toBe(1);
132
+ expect(level2.level).toBe(2);
133
+ expect(level3.level).toBe(3);
134
+ });
135
+
136
+ test("Singleton instances should be created only once per class", () => {
137
+ // Create a mock class that extends Singleton and counts constructor calls
138
+ class CounterSingleton extends Singleton {
139
+ public static constructorCallCount = 0;
140
+
141
+ constructor() {
142
+ super();
143
+ CounterSingleton.constructorCallCount++;
144
+ }
145
+ }
146
+
147
+ // Get the instance multiple times
148
+ const instance1 = CounterSingleton.GetInstance();
149
+ const instance2 = CounterSingleton.GetInstance();
150
+ const instance3 = CounterSingleton.GetInstance();
151
+
152
+ // Constructor should be called only once
153
+ expect(CounterSingleton.constructorCallCount).toBe(1);
154
+
155
+ // All instances should be the same
156
+ expect(instance1).toBe(instance2);
157
+ expect(instance2).toBe(instance3);
158
+ });
159
+
160
+ test("Singleton should handle property modifications correctly", () => {
161
+ const instance1 = BaseClass.GetInstance<BaseClass>();
162
+
163
+ // Modify a property
164
+ instance1.value = "modified";
165
+
166
+ // Get the instance again
167
+ const instance2 = BaseClass.GetInstance<BaseClass>();
168
+
169
+ // The modification should persist
170
+ expect(instance2.value).toBe("modified");
171
+
172
+ // Reset for other tests
173
+ instance1.value = "base";
174
+ });
175
+
176
+ test("ResetInstances should clear all singleton instances", () => {
177
+ // Get instances of multiple classes
178
+ const baseInstance1 = BaseClass.GetInstance<BaseClass>();
179
+ const derivedInstance1 = DerivedClass.GetInstance<DerivedClass>();
180
+
181
+ // Reset all instances
182
+ Singleton.ResetInstances();
183
+
184
+ // Get new instances
185
+ const baseInstance2 = BaseClass.GetInstance<BaseClass>();
186
+ const derivedInstance2 = DerivedClass.GetInstance<DerivedClass>();
187
+
188
+ // The new instances should be different from the original ones
189
+ expect(baseInstance2).not.toBe(baseInstance1);
190
+ expect(derivedInstance2).not.toBe(derivedInstance1);
191
+ });
192
+
193
+ test("ResetInstance should clear a specific singleton instance", () => {
194
+ // Get instances of multiple classes
195
+ const baseInstance1 = BaseClass.GetInstance<BaseClass>();
196
+ const derivedInstance1 = DerivedClass.GetInstance<DerivedClass>();
197
+
198
+ // Reset only the BaseClass instance
199
+ Singleton.ResetInstance("BaseClass");
200
+
201
+ // Get new instances
202
+ const baseInstance2 = BaseClass.GetInstance<BaseClass>();
203
+ const derivedInstance2 = DerivedClass.GetInstance<DerivedClass>();
204
+
205
+ // Only the BaseClass instance should be different
206
+ expect(baseInstance2).not.toBe(baseInstance1);
207
+ expect(derivedInstance2).toBe(derivedInstance1);
208
+ });
209
+
210
+ test("Special handling for Websocket classes", () => {
211
+ // Create classes that include "Websocket" in their name
212
+ class MockWebsocket extends Singleton {
213
+ constructor() {
214
+ super();
215
+ }
216
+ }
217
+ class MockWebsocketExtended extends MockWebsocket {}
218
+ class OtherSingleton extends Singleton {
219
+ constructor() {
220
+ super();
221
+ }
222
+ }
223
+
224
+ // Get instances
225
+ const instance1 = MockWebsocket.GetInstance();
226
+ const instance2 = MockWebsocketExtended.GetInstance();
227
+ const instance3 = OtherSingleton.GetInstance();
228
+
229
+ // Each class should have its own singleton instance
230
+ expect(instance1).toBe(instance1); // Same class gets same instance
231
+ expect(instance2).toBe(instance2); // Same class gets same instance
232
+ expect(instance1).not.toBe(instance3); // Different classes get different instances
233
+ expect(instance2).not.toBe(instance3); // Different classes get different instances
234
+ });
235
+
236
+ test("GetInstance should infer consturctor params for the class", () => {
237
+ class ClassWithConstructorParams extends Singleton {
238
+ public readonly config: { name: string; id: number };
239
+
240
+ protected constructor(name: string, id: number) {
241
+ super();
242
+ this.config = { name, id };
243
+ }
244
+ }
245
+
246
+ // Passing required parameters
247
+ const instance = ClassWithConstructorParams.GetInstance<ClassWithConstructorParams>("test", 1);
248
+ expect(instance.config).toEqual({ name: "test", id: 1 });
249
+ });
250
+
251
+ test("GetInstance can be overriden with a custom implementation", () => {
252
+ class CustomSingleton extends Singleton {
253
+ protected constructor(param1: string, param2: number) {
254
+ super();
255
+ }
256
+
257
+ public static override GetInstance<T extends CustomSingleton>(param1: string, param2: number): T {
258
+ return super.GetInstance<T>(param1, param2);
259
+ }
260
+ }
261
+
262
+ const instance = CustomSingleton.GetInstance("test", 2);
263
+ expect(instance).toBeDefined();
264
+ });
265
+
266
+ it("should allow custom channel map implementation", () => {
267
+ class CustomChannel extends Channel {
268
+ public broadcast(message: WebsocketStructuredMessage) {
269
+ console.log("CONSOLE LOG");
270
+ }
271
+ }
272
+
273
+ // Create a map with our custom channel
274
+ // Create a new Websocket instance with our custom channels
275
+ const ws = app.Websocket.GetInstance<app.Websocket>({
276
+ channelClass: CustomChannel, // Explicitly set the channel class
277
+ });
278
+
279
+ // Create a new channel - it should be a CustomChannel instance
280
+ const newChannel = ws.createChannel("test", "Test Channel", 5);
281
+ expect(newChannel).toBeInstanceOf(CustomChannel);
282
+
283
+ // Verify that broadcast uses our custom implementation
284
+ const spy = jest.spyOn(console, "log");
285
+ newChannel.broadcast({ type: "test", content: { message: "test message" } });
286
+ expect(spy).toHaveBeenCalledWith("CONSOLE LOG");
287
+ spy.mockRestore();
288
+
289
+ // Verify that the global channel is also a CustomChannel
290
+ const globalChannel = app.Websocket.GetChannel("global");
291
+ expect(globalChannel).toBeInstanceOf(CustomChannel);
292
+ });
293
+ it("should allow custom client implementation", () => {
294
+ class CustomClient extends Client {
295
+ public send(message: WebsocketStructuredMessage) {
296
+ console.log("CONSOLE LOG");
297
+ }
298
+ }
299
+ const ws = app.Websocket.GetInstance<app.Websocket>({ clientClass: CustomClient });
300
+ const client = app.Websocket.CreateClient({ id: "test", name: "Test Client", ws: {} as any });
301
+ expect(client).toBeInstanceOf(CustomClient);
302
+ });
303
+ it("should allow custom channel implementation", () => {
304
+ class CustomChannel extends Channel {
305
+ public broadcast(message: WebsocketStructuredMessage) {
306
+ console.log("CONSOLE LOG");
307
+ }
308
+ }
309
+
310
+ // Create a map with our custom channel
311
+ const ws = app.Websocket.GetInstance<app.Websocket>({ channelClass: CustomChannel });
312
+
313
+ // Create a new channel - it should be a CustomChannel instance
314
+ const newChannel = ws.createChannel("test", "Test Channel", 5);
315
+ expect(newChannel).toBeInstanceOf(CustomChannel);
316
+
317
+ // Verify that broadcast uses our custom implementation
318
+ const spy = jest.spyOn(console, "log");
319
+ newChannel.broadcast({ type: "test", content: { message: "test message" } });
320
+ expect(spy).toHaveBeenCalledWith("CONSOLE LOG");
321
+ spy.mockRestore();
322
+
323
+ // Verify that the global channel is also a CustomChannel
324
+ const globalChannel = app.Websocket.GetChannel("global");
325
+ expect(globalChannel).toBeInstanceOf(CustomChannel);
326
+ });
327
+
328
+ it("should handle derived channel broadcast with server correctly", () => {
329
+ // Mock server setup
330
+ const mockPublish = jest.fn();
331
+ const server = {
332
+ publish: mockPublish,
333
+ } as unknown as Server;
334
+
335
+ // Create derived channel class
336
+ class GameChannel<T extends app.Websocket = GameWebsocket> extends Channel<T> {
337
+ public override broadcast(message: WebsocketStructuredMessage, options?: BroadcastOptions): void {
338
+ // Log the message and options for testing
339
+ console.log("GameChannel");
340
+ // Call the parent implementation
341
+ super.broadcast(message, options);
342
+ }
343
+ }
344
+
345
+ class GameWebsocket extends app.Websocket {
346
+ constructor() {
347
+ super({
348
+ channelClass: GameChannel,
349
+ });
350
+ }
351
+ }
352
+
353
+ // Create and configure the Websocket instance
354
+ const ws = GameWebsocket.GetInstance<GameWebsocket>({
355
+ channelClass: GameChannel,
356
+ });
357
+
358
+ ws.set(server);
359
+
360
+ // Create and test channel
361
+ const channel = ws.createChannel("game", "Game Channel");
362
+ expect(channel).toBeInstanceOf(GameChannel);
363
+
364
+ // Test broadcast
365
+ const spy = jest.spyOn(console, "log");
366
+ const message = { type: "test", content: { message: "test message" } };
367
+ const extraData = { param1: "param1", extra: { data: "data" } };
368
+
369
+ // Call broadcast with the new options-based API
370
+ channel.broadcast(message, { data: extraData, client: { id: "test", name: "Test Client" } });
371
+
372
+ // Verify console.log was called
373
+ expect(spy).toHaveBeenCalled();
374
+
375
+ // Verify server publish was called correctly
376
+ expect(mockPublish).toHaveBeenCalledWith(channel.id, expect.any(String));
377
+
378
+ // Verify the JSON structure
379
+ const lastCall = mockPublish.mock.calls[0];
380
+ const parsedJson = JSON.parse(lastCall[1]);
381
+
382
+ // Update expectations to match actual structure - we don't care about exact format
383
+ // as long as it contains the message
384
+ expect(parsedJson).toHaveProperty("type", message.type);
385
+ expect(parsedJson).toHaveProperty("channel", channel.id);
386
+
387
+ spy.mockRestore();
388
+ });
389
+ it("should make sure broadcast structured message is correct", () => {
390
+ const mockPublish = jest.fn();
391
+ const server = {
392
+ publish: mockPublish,
393
+ } as unknown as Server;
394
+ const ws = app.Websocket.GetInstance<app.Websocket>();
395
+ ws.set(server);
396
+
397
+ const channel = ws.createChannel("test", "Test Channel");
398
+ const message = { type: "test", content: { message: "test message" }, };
399
+ channel.broadcast(message, { debug: true, client: { id: "test", name: "Test Client" } });
400
+ expect(mockPublish).toHaveBeenCalledWith(channel.id, expect.any(String));
401
+ });
402
+ });
@@ -0,0 +1,60 @@
1
+ import Singleton from "../singleton";
2
+
3
+ // This file is not meant to be run as a test, but to demonstrate
4
+ // type inference capabilities of the GetInstance method
5
+
6
+ // Class with typed constructor parameters
7
+ class TypedSingleton extends Singleton {
8
+ public readonly name: string;
9
+ public readonly id: number;
10
+ public readonly options: { debug: boolean };
11
+
12
+ constructor(name: string, id: number, options: { debug: boolean }) {
13
+ super();
14
+ this.name = name;
15
+ this.id = id;
16
+ this.options = options;
17
+ }
18
+ }
19
+
20
+ // TypeScript should infer the parameter types correctly
21
+ // This would cause a type error if the parameters don't match
22
+ const instance = TypedSingleton.GetInstance<TypedSingleton>(
23
+ "test-name", // string
24
+ 123, // number
25
+ { debug: true } // { debug: boolean }
26
+ );
27
+
28
+ // This would cause a type error (wrong type for id)
29
+ // const badInstance = TypedSingleton.GetInstance(
30
+ // "test-name",
31
+ // "not-a-number", // Type error: Argument of type 'string' is not assignable to parameter of type 'number'
32
+ // { debug: true }
33
+ // );
34
+
35
+ // This would cause a type error (missing parameter)
36
+ // const missingParamInstance = TypedSingleton.GetInstance(
37
+ // "test-name",
38
+ // 123
39
+ // // Missing options parameter
40
+ // );
41
+
42
+ // This would cause a type error (extra parameter)
43
+ // const extraParamInstance = TypedSingleton.GetInstance(
44
+ // "test-name",
45
+ // 123,
46
+ // { debug: true },
47
+ // "extra" // Extra parameter not expected
48
+ // );
49
+
50
+ // The instance should have the correct types for its properties
51
+ const name: string = instance.name;
52
+ const id: number = instance.id;
53
+ const debug: boolean = instance.options.debug;
54
+
55
+ // This test file doesn't contain actual tests, just type checking
56
+ describe("Type Inference", () => {
57
+ test("This is a placeholder test to avoid test runner errors", () => {
58
+ expect(true).toBe(true);
59
+ });
60
+ });
@@ -0,0 +1,43 @@
1
+ import { I_ApplicationResponse } from "./types";
2
+
3
+ export const RESPONSE_INIT = (status?: number, headers?: HeadersInit): ResponseInit => {
4
+ return {
5
+ status: status ?? 200,
6
+ headers: {
7
+ "Access-Control-Allow-Origin": "*",
8
+ "Content-Type": "application/json",
9
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
10
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
11
+ "Access-Control-Allow-Credentials": "true",
12
+ "Access-Control-Max-Age": "86400", // 24 hours
13
+ },
14
+ };
15
+ };
16
+
17
+ export const RESPONSE_METHOD_OPTIONS = {
18
+ name: "Access-Control-Max-Age",
19
+ value: "86400",
20
+ };
21
+
22
+ function isApplicationResponse<T>(data: T | I_ApplicationResponse<T>): data is I_ApplicationResponse<T> {
23
+ return typeof data === "object" && data !== null && "status" in data;
24
+ }
25
+
26
+ class Application {
27
+ public static Response<T>(data: T | I_ApplicationResponse<T>, status = 200, headers?: HeadersInit): Response {
28
+ const response = isApplicationResponse(data) ? data : { status: true, data };
29
+ return Response.json(response, RESPONSE_INIT(status, headers));
30
+ }
31
+
32
+ public static Error<T extends BodyInit | unknown | Error>(error: T | I_ApplicationResponse<T>, status = 200, headers?: HeadersInit): Response {
33
+ const response = isApplicationResponse(error) ? error : { status: false, data: error, error };
34
+ return Response.json(response, RESPONSE_INIT(status, headers));
35
+ }
36
+
37
+ public static Throw<T extends BodyInit | unknown | Error>(error: T | I_ApplicationResponse<T>, status = 400, headers?: HeadersInit): Response {
38
+ const response = isApplicationResponse(error) ? error : { status: false, data: error, error };
39
+ return Response.json(response, RESPONSE_INIT(status, headers));
40
+ }
41
+ }
42
+
43
+ export default Application;
@@ -0,0 +1,5 @@
1
+ // This file is auto-generated by scripts/generate-indexes.ts
2
+ // Do not edit this file directly
3
+
4
+ export * from './rxjs';
5
+ export * from './useRxjs';
@@ -0,0 +1,122 @@
1
+ import { AsyncSubject, BehaviorSubject, ReplaySubject, Subject, Subscription } from "rxjs";
2
+ import { Lib } from "../../utils";
3
+ import { Singleton } from "../..";
4
+
5
+ export enum E_SUBJET_TYPE {
6
+ SUBJECT = "subject",
7
+ ASYNC_SUBJECT = "asyncSubject",
8
+ BEHAVIOR_SUBJECT = "behaviorSubject",
9
+ REPLAY_SUBJECT = "replaySubject",
10
+ }
11
+
12
+ export type I_RxjsPayload<T> = {
13
+ cta: string;
14
+ data: T;
15
+ source?: string;
16
+ };
17
+
18
+ export type RxjsNamespaces<T extends string> = T;
19
+
20
+ export class Rxjs<T extends string> extends Singleton {
21
+ public namespaces = new Map<string, RxjsInstance>();
22
+
23
+ create(namespace: string, subjectType: E_SUBJET_TYPE = E_SUBJET_TYPE.SUBJECT) {
24
+ console.log("Creating namespace", namespace, subjectType);
25
+ if (!this.namespaces.has(namespace)) {
26
+ this.namespaces.set(namespace, new RxjsInstance(subjectType));
27
+ }
28
+ }
29
+
30
+ has(namespace: string) {
31
+ return this.namespaces.has(namespace);
32
+ }
33
+
34
+ next<U>(namespace: RxjsNamespaces<T>, rxjsPayload: I_RxjsPayload<U>): void {
35
+ this.namespaces.get(namespace)?.next(rxjsPayload);
36
+ }
37
+
38
+ subscribe(namespace: RxjsNamespaces<T>, listener: (payload: I_RxjsPayload<T>) => any): Subscription {
39
+ const instance = this.namespaces.get(namespace);
40
+ if (!instance) {
41
+ throw new Error(`Namespace ${namespace} not found`);
42
+ }
43
+ return instance.subscribe((payload: I_RxjsPayload<T>) => listener(payload));
44
+ }
45
+
46
+ clear(namespace: RxjsNamespaces<T>) {
47
+ this.namespaces.get(namespace)?.clear();
48
+ }
49
+ }
50
+
51
+ export class RxjsInstance {
52
+ subscriptions: Subscription;
53
+ subject: Subject<any>;
54
+
55
+ constructor(subjectType: E_SUBJET_TYPE) {
56
+ this.subscriptions = new Subscription();
57
+ switch (subjectType) {
58
+ case E_SUBJET_TYPE.SUBJECT:
59
+ this.subject = new Subject();
60
+ break;
61
+ case E_SUBJET_TYPE.ASYNC_SUBJECT:
62
+ this.subject = new AsyncSubject();
63
+ break;
64
+ case E_SUBJET_TYPE.BEHAVIOR_SUBJECT:
65
+ this.subject = new BehaviorSubject(null);
66
+ break;
67
+ case E_SUBJET_TYPE.REPLAY_SUBJECT:
68
+ this.subject = new ReplaySubject();
69
+ break;
70
+ default:
71
+ this.subject = new Subject();
72
+ }
73
+ }
74
+
75
+ next(data: any) {
76
+ this.subject.next(data);
77
+ }
78
+
79
+ asObservable() {
80
+ return this.subject.asObservable();
81
+ }
82
+
83
+ clear() {
84
+ if (this.subject instanceof ReplaySubject) {
85
+ // Get the current subscribers
86
+ const currentSubscribers = (this.subject as any)._subscribers;
87
+
88
+ // Create a new ReplaySubject with same buffer config
89
+ const bufferSize = (this.subject as any)._bufferSize;
90
+ const windowTime = (this.subject as any)._windowTime;
91
+ const newSubject = new ReplaySubject(bufferSize, windowTime);
92
+
93
+ // Transfer subscribers to new subject
94
+ newSubject.subscribe(currentSubscribers);
95
+
96
+ // Replace the old subject
97
+ this.subject = newSubject;
98
+ }
99
+ }
100
+
101
+ set add(subscription: Subscription) {
102
+ this.subscriptions.add(subscription);
103
+ }
104
+
105
+ subscribe<U>(callback: (payload: I_RxjsPayload<U>) => void): Subscription {
106
+ if (typeof callback !== "function") {
107
+ throw "Failed to subscribe. Please provide a callback function";
108
+ }
109
+
110
+ const subscriber = this.asObservable().subscribe((payload: I_RxjsPayload<U>) => {
111
+ if (Lib.IsNumpty(payload) || !payload.hasOwnProperty("cta")) return;
112
+ callback(payload);
113
+ });
114
+
115
+ this.subscriptions.add(subscriber);
116
+ return subscriber;
117
+ }
118
+
119
+ unsubscribe() {
120
+ this.subscriptions.unsubscribe();
121
+ }
122
+ }