topsyde-utils 1.0.150 → 1.0.152

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 (78) hide show
  1. package/dist/application.d.ts +1 -0
  2. package/dist/application.js +12 -8
  3. package/dist/application.js.map +1 -1
  4. package/dist/client/rxjs/index.js.map +1 -1
  5. package/dist/client/rxjs/rxjs.js.map +1 -1
  6. package/dist/client/rxjs/useRxjs.js.map +1 -1
  7. package/dist/client/vite/plugins/index.js.map +1 -1
  8. package/dist/client/vite/plugins/topsydeUtilsVitePlugin.js.map +1 -1
  9. package/dist/consts.js.map +1 -1
  10. package/dist/enums.js.map +1 -1
  11. package/dist/errors.js.map +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +1 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/initializable.js.map +1 -1
  16. package/dist/server/bun/index.js.map +1 -1
  17. package/dist/server/bun/router/controller-discovery.js.map +1 -1
  18. package/dist/server/bun/router/index.js.map +1 -1
  19. package/dist/server/bun/router/router.internal.js.map +1 -1
  20. package/dist/server/bun/router/router.js.map +1 -1
  21. package/dist/server/bun/router/routes.js.map +1 -1
  22. package/dist/server/bun/websocket/Channel.js.map +1 -1
  23. package/dist/server/bun/websocket/Client.js.map +1 -1
  24. package/dist/server/bun/websocket/Message.js.map +1 -1
  25. package/dist/server/bun/websocket/Websocket.js.map +1 -1
  26. package/dist/server/bun/websocket/index.js.map +1 -1
  27. package/dist/server/bun/websocket/websocket.enums.js.map +1 -1
  28. package/dist/server/bun/websocket/websocket.guards.js.map +1 -1
  29. package/dist/server/bun/websocket/websocket.types.js.map +1 -1
  30. package/dist/server/controller.js.map +1 -1
  31. package/dist/server/index.js.map +1 -1
  32. package/dist/server/service.js.map +1 -1
  33. package/dist/singleton.js.map +1 -1
  34. package/dist/throwable.js.map +1 -1
  35. package/dist/types.js.map +1 -1
  36. package/dist/utils/Console.js.map +1 -1
  37. package/dist/utils/Guards.js.map +1 -1
  38. package/dist/utils/Lib.js.map +1 -1
  39. package/dist/utils/index.js.map +1 -1
  40. package/package.json +5 -3
  41. package/src/__tests__/app.test.ts +205 -0
  42. package/src/__tests__/singleton.test.ts +402 -0
  43. package/src/__tests__/type-inference.test.ts +60 -0
  44. package/src/application.ts +48 -0
  45. package/src/client/rxjs/index.ts +5 -0
  46. package/src/client/rxjs/rxjs.ts +122 -0
  47. package/src/client/rxjs/useRxjs.ts +111 -0
  48. package/src/client/vite/plugins/index.ts +5 -0
  49. package/src/client/vite/plugins/topsydeUtilsVitePlugin.ts +80 -0
  50. package/src/consts.ts +48 -0
  51. package/src/enums.ts +14 -0
  52. package/src/errors.ts +56 -0
  53. package/src/index.ts +81 -0
  54. package/src/initializable.ts +375 -0
  55. package/src/server/bun/index.ts +6 -0
  56. package/src/server/bun/router/controller-discovery.ts +94 -0
  57. package/src/server/bun/router/index.ts +9 -0
  58. package/src/server/bun/router/router.internal.ts +64 -0
  59. package/src/server/bun/router/router.ts +51 -0
  60. package/src/server/bun/router/routes.ts +7 -0
  61. package/src/server/bun/websocket/Channel.ts +157 -0
  62. package/src/server/bun/websocket/Client.ts +129 -0
  63. package/src/server/bun/websocket/Message.ts +106 -0
  64. package/src/server/bun/websocket/Websocket.ts +221 -0
  65. package/src/server/bun/websocket/index.ts +14 -0
  66. package/src/server/bun/websocket/websocket.enums.ts +22 -0
  67. package/src/server/bun/websocket/websocket.guards.ts +6 -0
  68. package/src/server/bun/websocket/websocket.types.ts +186 -0
  69. package/src/server/controller.ts +121 -0
  70. package/src/server/index.ts +7 -0
  71. package/src/server/service.ts +36 -0
  72. package/src/singleton.ts +28 -0
  73. package/src/throwable.ts +87 -0
  74. package/src/types.ts +10 -0
  75. package/src/utils/Console.ts +85 -0
  76. package/src/utils/Guards.ts +61 -0
  77. package/src/utils/Lib.ts +506 -0
  78. package/src/utils/index.ts +9 -0
@@ -0,0 +1,186 @@
1
+ import { ServerWebSocket, WebSocketHandler } from "bun";
2
+ import Channel from "./Channel";
3
+ import Websocket from "./Websocket";
4
+
5
+ export type BunWebsocketMessage = string | Buffer<ArrayBufferLike>;
6
+
7
+ export type WebsocketChannel<T extends I_WebsocketChannel = Channel> = Map<string, T>;
8
+ export type WebsocketClients = Map<string, I_WebsocketClient>;
9
+ export type WebsocketMessageOptions = {
10
+ /**
11
+ * Additional data to include in the message content
12
+ * If an object is provided, it will be merged with the content
13
+ * If a primitive value is provided, it will be added as content.data
14
+ */
15
+ data?: any;
16
+
17
+ /**
18
+ * Client information to include in the message
19
+ * Will be added as content.client
20
+ */
21
+ client?: Partial<WebsocketEntityData> & {
22
+ [key: string]: any;
23
+ };
24
+
25
+ /**
26
+ * Channel metadata to include in the message
27
+ * If true, all metadata will be included
28
+ * If an array of strings, only the specified keys will be included
29
+ */
30
+ includeMetadata?: boolean | string[];
31
+
32
+ /**
33
+ * Client IDs to exclude from receiving the broadcast
34
+ * Useful for sending messages to all clients except the sender
35
+ */
36
+ excludeClients?: string[];
37
+
38
+ /**
39
+ * Channel to include in the message
40
+ * Defaults to the channel of the message
41
+ */
42
+ channel?: string;
43
+
44
+ /**
45
+ * Whether to include timestamp in the message
46
+ * Defaults to true
47
+ */
48
+ includeTimestamp?: boolean;
49
+
50
+ /**
51
+ * Custom fields to add to the root of the message
52
+ * These will be merged with the message object
53
+ */
54
+ customFields?: Record<string, any>;
55
+
56
+ /**
57
+ * Transform function to modify the final message before sending
58
+ * This is applied after all other processing
59
+ */
60
+ transform?: (message: any) => any;
61
+
62
+ /**
63
+ * Priority of the message (higher numbers = higher priority)
64
+ * Can be used by clients to determine processing order
65
+ */
66
+ priority?: number;
67
+
68
+ /**
69
+ * Message expiration time in milliseconds since epoch
70
+ * Can be used by clients to ignore outdated messages
71
+ */
72
+ expiresAt?: number;
73
+
74
+ /**
75
+ * Metadata to include in the message
76
+ * If an array of strings, only the specified keys will be included
77
+ */
78
+ metadata?: boolean | string[] | Record<string, string>;
79
+ };
80
+
81
+ export type WebsocketMessage<T extends Record<string, any> = Record<string, any>> = {
82
+ /**
83
+ * Message type identifier used for client-side routing
84
+ */
85
+ type: string;
86
+ /**
87
+ * Message content - can be any data structure
88
+ * If a string is provided, it will be wrapped in {message: string}
89
+ */
90
+ content: T;
91
+ /**
92
+ * Channel ID
93
+ */
94
+ channel?: string;
95
+ /**
96
+ * Timestamp of the message
97
+ */
98
+ timestamp?: string;
99
+ /**
100
+ * Any additional custom fields
101
+ */
102
+ [key: string]: any;
103
+ };
104
+
105
+ export type WebsocketStructuredMessage<T extends Record<string, any> = Record<string, any>> = WebsocketMessage<T> & WebsocketMessageOptions;
106
+
107
+ export type WebsocketEntityId = string;
108
+ export type WebsocketEntityName = string;
109
+ export type WebsocketEntityData = { id: WebsocketEntityId; name: WebsocketEntityName };
110
+
111
+ export interface I_WebsocketEntity extends WebsocketEntityData {
112
+ ws: ServerWebSocket<WebsocketEntityData>;
113
+ }
114
+
115
+ export interface I_WebsocketClient extends I_WebsocketEntity {
116
+ channels: WebsocketChannel<I_WebsocketChannel>;
117
+ send(message: WebsocketStructuredMessage): any;
118
+ subscribe(channel: string): any;
119
+ joinChannel(channel: I_WebsocketChannel, send?: boolean): void;
120
+ leaveChannel(channel: I_WebsocketChannel, send?: boolean): void;
121
+ joinChannels(channels: I_WebsocketChannel[], send?: boolean): void;
122
+ leaveChannels(channels?: I_WebsocketChannel[], send?: boolean): void;
123
+ unsubscribe(channel: string): any;
124
+ whoami(): WebsocketEntityData;
125
+ }
126
+
127
+ export interface I_WebsocketChannelEntity<T extends Websocket = Websocket> extends WebsocketEntityData {
128
+ ws: T;
129
+ }
130
+
131
+ // New types for the broadcast method
132
+ export type BroadcastOptions = WebsocketMessageOptions & {
133
+ debug?: boolean;
134
+ };
135
+ export interface I_WebsocketChannel<T extends Websocket = Websocket> extends I_WebsocketChannelEntity<T> {
136
+ limit: number;
137
+ members: Map<string, I_WebsocketClient>;
138
+ metadata: Record<string, string>;
139
+ createdAt: Date;
140
+ broadcast(message: WebsocketStructuredMessage, options?: BroadcastOptions): void;
141
+ hasMember(client: I_WebsocketEntity | string): boolean;
142
+ addMember(entity: I_WebsocketClient): I_WebsocketClient | false;
143
+ removeMember(entity: I_WebsocketEntity): I_WebsocketClient | false;
144
+ getMember(client: I_WebsocketEntity | string): I_WebsocketClient | undefined;
145
+ getMembers(clients?: string[] | I_WebsocketEntity[]): I_WebsocketClient[];
146
+ getMetadata(): Record<string, string>;
147
+ getCreatedAt(): Date;
148
+ getId(): string;
149
+ getSize(): number;
150
+ getLimit(): number;
151
+ getName(): string;
152
+ canAddMember(): boolean;
153
+ }
154
+
155
+ /**
156
+ * Interface for implementing custom WebSocket behavior.
157
+ *
158
+ * @interface I_WebsocketInterface
159
+ *
160
+ * @property {Function} setup - Initializes the WebSocket handler with channels and clients
161
+ *
162
+ * The interface supports three optional handler methods:
163
+ *
164
+ * - `message`: Custom message handler that replaces the default handler
165
+ * - `open`: Connection handler that runs after the default open handler
166
+ * - `close`: Disconnection handler that runs before the default close handler
167
+ */
168
+ export type WebsocketInterfaceHandlers = Partial<WebSocketHandler<WebsocketEntityData>>;
169
+
170
+ /**
171
+ * Interface for implementing custom WebSocket behavior.
172
+ *
173
+ * @interface I_WebsocketInterface
174
+ *
175
+ * @property {Function} setup - Initializes the WebSocket handler with channels and clients
176
+ *
177
+ * The interface supports three optional handler methods:
178
+ *
179
+ * - `message`: Custom message handler that replaces the default handler
180
+ * - `open`: Connection handler that runs after the default open handler
181
+ * - `close`: Disconnection handler that runs before the default close handler
182
+ */
183
+ export interface I_WebsocketInterface {
184
+ handlers: (channels: WebsocketChannel, clients: WebsocketClients) => WebsocketInterfaceHandlers;
185
+ }
186
+
@@ -0,0 +1,121 @@
1
+ import { ERROR_CODE } from "../errors";
2
+ import Initializable, { InitializableOptions } from "../initializable";
3
+ import { I_ApplicationResponse } from "../types";
4
+ import { Guards } from "../utils";
5
+
6
+ export type ControllerResponse<T = unknown> = Promise<I_ApplicationResponse<T> | PromiseLike<I_ApplicationResponse<T>>>;
7
+ export type ControllerAction<T> = (req: Request) => ControllerResponse<T>;
8
+
9
+ export type ControllerMap<T = unknown> = Map<string, ControllerAction<T>>;
10
+
11
+ export type ControllerOptions<T = unknown> = InitializableOptions & {
12
+ path: string | undefined;
13
+ post: Map<string, ControllerAction<T>>;
14
+ get: ControllerMap<T>;
15
+ controllerMap: Map<string, ControllerMap<T>>;
16
+ };
17
+
18
+ export default abstract class Controller extends Initializable {
19
+ path: string | undefined;
20
+ post: Map<string, ControllerAction<any>> = new Map();
21
+ get: ControllerMap = new Map();
22
+ controllerMap: Map<string, ControllerMap> = new Map();
23
+
24
+ constructor() {
25
+ super();
26
+ this.POST();
27
+ this.GET();
28
+ this.setControllerMap();
29
+ }
30
+
31
+ public static Create<T>(this: new () => T): T {
32
+ return new this();
33
+ }
34
+
35
+ public static async AsyncCreate<T>(this: new () => T): Promise<T> {
36
+ return new this();
37
+ }
38
+
39
+ public async call<T>(request: Request): Promise<T> {
40
+ await this.isInitialized();
41
+ const action = this.resolve(request);
42
+ return await action(request);
43
+ }
44
+
45
+ public success<T>(data: T): I_ApplicationResponse<T> {
46
+ return { status: true, data };
47
+ }
48
+
49
+ public failure<T>(data: T): I_ApplicationResponse<T> {
50
+ return { status: false, data };
51
+ }
52
+
53
+ private setControllerMap() {
54
+ this.controllerMap.set("GET", this.get);
55
+ this.controllerMap.set("POST", this.post);
56
+ }
57
+
58
+ private resolve(req: Request): Function {
59
+ const url = new URL(req.url);
60
+ const endpointArray = url.pathname.split("/");
61
+ const actionName = endpointArray.slice(2).join("/");
62
+
63
+ if (!Guards.IsString(actionName, true)) throw ERROR_CODE.INVALID_ACTION;
64
+
65
+ const methodMap = this.controllerMap.get(req.method);
66
+
67
+ if (Guards.IsNil(methodMap)) {
68
+ throw ERROR_CODE.INVALID_METHOD;
69
+ }
70
+
71
+ return this.resolveAction(methodMap, actionName);
72
+ }
73
+
74
+ private resolveAction(methodMap: ControllerMap, actionName: string) {
75
+ const action = methodMap.get(actionName) as Function;
76
+
77
+ if (!Guards.IsFunction(action, true)) {
78
+ throw ERROR_CODE.NO_ACTION_IN_MAP;
79
+ }
80
+
81
+ return action;
82
+ }
83
+
84
+ /**
85
+ * Abstract method that needs to be implemented in derived classes.
86
+ * This method is used to map post actions to their corresponding handler methods.
87
+ *
88
+ * Each action is a string that represents a specific operation, and the handler is a function that performs this operation.
89
+ * The handler is bound to the current context (`this`) to ensure it has access to the class's properties and methods.
90
+ *
91
+ * Example implementation:
92
+ *
93
+ * ```typescript
94
+ * setActionsMap() {
95
+ * this.post.set("action", this.controllerMethod.bind(this));
96
+ * }
97
+ * ```
98
+ *
99
+ * In this example, when "https://webserver_url:port/controller/action" is called by the router, the `controllerMethod` method will be called.
100
+ */
101
+ abstract POST(): void;
102
+
103
+ /**
104
+ * Abstract method that needs to be implemented in derived classes.
105
+ * This method is used to map actions to their corresponding handler methods.
106
+ *
107
+ * Each action is a string that represents a specific operation, and the handler is a function that performs this operation.
108
+ * The handler is bound to the current context (`this`) to ensure it has access to the class's properties and methods.
109
+ *
110
+ * Example implementation:
111
+ *
112
+ * ```typescript
113
+ * setActionsMap() {
114
+ * this.get.set("action", this.controllerMethod.bind(this));
115
+ * }
116
+ * ```
117
+ *
118
+ * In this example, when "https://webserver_url:port/controller/action" is called by the router, the `controllerMethod` method will be called.
119
+ */
120
+ abstract GET(): void;
121
+ }
@@ -0,0 +1,7 @@
1
+ // This file is auto-generated by scripts/generate-indexes.ts
2
+ // Do not edit this file directly
3
+
4
+ export * from './controller';
5
+ export * from './service';
6
+ export { default as Controller } from './controller';
7
+ export { default as Service } from './service';
@@ -0,0 +1,36 @@
1
+ import { ERROR_CODE } from "../errors";
2
+ import Initializable from "../initializable";
3
+ import { Guards } from "../utils";
4
+
5
+ export default abstract class Service extends Initializable {
6
+ async validateRequestBody<T>(request: Request, keys: (keyof T)[]): Promise<T> {
7
+ return await Service.validateRequestBody(request, keys);
8
+ }
9
+
10
+ static async validateRequestBody<T>(request: Request, keys: (keyof T)[]): Promise<T> {
11
+ try {
12
+ let body;
13
+ try {
14
+ body = await request.json();
15
+ } catch (e) {
16
+ body = await request.text();
17
+ const cleanedBody = body
18
+ .replace(/\/\/.*$/gm, "") // Remove comments
19
+ .replace(/\n/g, "") // Remove newlines
20
+ .replace(/\s+/g, " ") // Replace multiple spaces with single space
21
+ .trim(); // Remove leading/trailing whitespace
22
+
23
+ body = JSON.parse(cleanedBody);
24
+ }
25
+ if (Guards.IsNil(body)) throw ERROR_CODE.REQ_BODY_EMPTY;
26
+ if (!Guards.IsType<T>(body, keys)) {
27
+ console.error(`Expected keys: ${keys.join(", ")}`);
28
+ throw ERROR_CODE.INVALID_METHOD_INPUT;
29
+ }
30
+ return body;
31
+ } catch (e) {
32
+ if (e instanceof Error || e instanceof SyntaxError) throw "Could not parse request body: " + e;
33
+ throw e;
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,28 @@
1
+ export default abstract class Singleton {
2
+ private static readonly _instances = new Map<string, Singleton>();
3
+
4
+ protected timestamp: Date;
5
+
6
+ protected constructor() {
7
+ this.timestamp = new Date();
8
+ }
9
+
10
+ public static GetInstance<T extends Singleton>(...args: any[]): T {
11
+ const className = this.name;
12
+
13
+ if (!Singleton._instances.has(className)) {
14
+ const instance = Reflect.construct(this, args) as T;
15
+ Singleton._instances.set(className, instance);
16
+ }
17
+
18
+ return Singleton._instances.get(className) as T;
19
+ }
20
+
21
+ public static ResetInstances(): void {
22
+ Singleton._instances.clear();
23
+ }
24
+
25
+ public static ResetInstance(className: string): void {
26
+ Singleton._instances.delete(className);
27
+ }
28
+ }
@@ -0,0 +1,87 @@
1
+ import { Guards, Lib } from "./utils";
2
+
3
+
4
+ /**
5
+ * @description Custom error class for errors that are expected to be thrown
6
+ * and should not trigger retry mechanisms or be reported to error monitoring services.
7
+ *
8
+ * Use this class when you want to throw an error that:
9
+ * 1. Is an expected part of the application flow
10
+ * 2. Should not be retried by retry handlers
11
+ * 3. Should not be reported to error monitoring services like Sentry
12
+ */
13
+ class Throwable extends Error {
14
+ /** Flag indicating this is a deliberate error that shouldn't be retried */
15
+ public readonly isDeliberate: boolean = true;
16
+
17
+ /** Original error if this Throwable wraps another error */
18
+ public readonly originalError?: Error;
19
+
20
+ /** Additional context that might be helpful for debugging */
21
+ public readonly context?: Record<string, unknown>;
22
+
23
+ /**
24
+ * Create a new Throwable error
25
+ * @param message Error message or existing Error object to wrap
26
+ * @param options Configuration options
27
+ */
28
+ constructor(
29
+ message: unknown,
30
+ options: {
31
+ /** Whether to log this error to console (default: true) */
32
+ logError?: boolean;
33
+ /** Additional context data to attach to the error */
34
+ context?: Record<string, unknown>;
35
+ } = {},
36
+ ) {
37
+ const { logError = true, context } = options;
38
+
39
+ // Format the message appropriately based on type
40
+ const _message = Guards.IsString(message) ? message : message instanceof Error ? message.message : JSON.stringify(message);
41
+
42
+ super(_message);
43
+ this.name = "Throwable";
44
+ this.context = context;
45
+
46
+ // Log the error if requested
47
+ if (logError) {
48
+ if (context) {
49
+ Lib.$Log("Throwable: ", _message, "Context:", context);
50
+ } else {
51
+ Lib.$Log("Throwable: ", message);
52
+ }
53
+ }
54
+
55
+ // Capture stack trace and cause if wrapping an existing error
56
+ if (message instanceof Error) {
57
+ this.stack = message.stack;
58
+ this.cause = message.cause;
59
+ this.originalError = message;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Check if an error is a Throwable
65
+ * @param e Any error to check
66
+ * @returns True if the error is a Throwable
67
+ */
68
+ static IsThrowable(e: any): e is Throwable {
69
+ return e instanceof Throwable;
70
+ }
71
+
72
+ /**
73
+ * Create a Throwable from any error or message
74
+ * @param error Error or message to convert
75
+ * @param context Additional context to attach
76
+ * @returns A new Throwable instance
77
+ */
78
+ static from(error: unknown, context?: Record<string, unknown>): Throwable {
79
+ if (error instanceof Throwable) {
80
+ return error;
81
+ }
82
+
83
+ return new Throwable(error, { context });
84
+ }
85
+ }
86
+
87
+ export default Throwable;
package/src/types.ts ADDED
@@ -0,0 +1,10 @@
1
+ export type ClassConstructor<T> = new (...args: any[]) => T;
2
+ export type NonNullableType<T> = Exclude<T, null | undefined>;
3
+ type ObjectKeysValues = string | number | string[] | number[] | null;
4
+ export type ObjectKeys<T, U = ObjectKeysValues> = Partial<Record<keyof T, U>>;
5
+ export type KVObj<T> = { key: string; value: T };
6
+ export interface I_ApplicationResponse<T = unknown> {
7
+ status: boolean | number;
8
+ data: T;
9
+ error?: T;
10
+ }
@@ -0,0 +1,85 @@
1
+ import { LOG_COLORS, LOG_ICONS } from "../consts";
2
+
3
+ export class Console {
4
+ /**
5
+ * Print a blank line
6
+ */
7
+ static blank(): void {
8
+ console.log("");
9
+ }
10
+
11
+ /**
12
+ * Print a success message
13
+ */
14
+ static success(message: string): void {
15
+ console.log(` ${LOG_COLORS.text.green}${LOG_ICONS.success}${LOG_COLORS.reset} ${message}`);
16
+ }
17
+
18
+ /**
19
+ * Print an info message
20
+ */
21
+ static info(message: string): void {
22
+ console.log(` ${LOG_COLORS.text.blue}${LOG_ICONS.info}${LOG_COLORS.reset} ${message}`);
23
+ }
24
+
25
+ /**
26
+ * Print a warning message
27
+ */
28
+ static warning(message: string): void {
29
+ console.log(` ${LOG_COLORS.text.yellow}${LOG_ICONS.warning}${LOG_COLORS.reset} ${message}`);
30
+ }
31
+
32
+ /**
33
+ * Print an error message
34
+ */
35
+ static error(message: string): void {
36
+ console.log(` ${LOG_COLORS.text.red}${LOG_ICONS.error}${LOG_COLORS.reset} ${message}`);
37
+ }
38
+
39
+ /**
40
+ * Print a debug message
41
+ */
42
+ static debug(message: string): void {
43
+ console.log(` ${LOG_COLORS.text.magenta}${LOG_ICONS.debug}${LOG_COLORS.reset} ${message}`);
44
+ }
45
+
46
+ /**
47
+ * Print a list item with an arrow
48
+ */
49
+ static item(label: string, value: string): void {
50
+ console.log(` ${LOG_COLORS.text.cyan}${LOG_ICONS.arrow}${LOG_COLORS.reset} ${LOG_COLORS.bright}${label}:${LOG_COLORS.reset} ${value}`);
51
+ }
52
+
53
+ /**
54
+ * Print a header with optional emoji
55
+ */
56
+ static header(text: string, emoji?: string): void {
57
+ const icon = emoji ? `${emoji} ` : "";
58
+ console.log(` ${icon}${LOG_COLORS.bright}${text}${LOG_COLORS.reset}`);
59
+ }
60
+
61
+ /**
62
+ * Print a subheader with dimmed text
63
+ */
64
+ static subheader(text: string): void {
65
+ console.log(` ${LOG_COLORS.dim}${text}${LOG_COLORS.reset}`);
66
+ }
67
+
68
+ /**
69
+ * Print a ready message with timing
70
+ */
71
+ static ready(timeMs: number): void {
72
+ console.log(
73
+ ` ${LOG_COLORS.text.green}✓${LOG_COLORS.reset} ${LOG_COLORS.dim}Ready in${LOG_COLORS.reset} ${LOG_COLORS.text.cyan}${timeMs}ms${LOG_COLORS.reset}`,
74
+ );
75
+ }
76
+
77
+ /**
78
+ * Get a colored string
79
+ */
80
+ static colorize(text: string, color: keyof typeof LOG_COLORS.text): string {
81
+ return `${LOG_COLORS.text[color]}${text}${LOG_COLORS.reset}`;
82
+ }
83
+ }
84
+
85
+ export default Console;
@@ -0,0 +1,61 @@
1
+ import type { NonNullableType } from "../types";
2
+ import Lib from "./Lib";
3
+
4
+ class Guards {
5
+ public static IsString(value: any): value is string;
6
+ public static IsString(value: any, as_typeof: boolean): value is NonNullableType<string>;
7
+ public static IsString(value: any, as_typeof: boolean, excludeNull: true): value is NonNullableType<string>;
8
+ public static IsString(value: any, as_typeof: boolean = false, excludeNull = false): value is string | NonNullableType<string> {
9
+ const output = Lib.IsString(value, as_typeof);
10
+ return excludeNull ? !Guards.IsNil(value) && output : output;
11
+ }
12
+
13
+ public static IsNumber(value: any): value is number;
14
+ public static IsNumber(value: any, as_typeof: boolean): value is NonNullableType<number>;
15
+ public static IsNumber(value: any, as_typeof: boolean, excludeNull: true): value is NonNullableType<number>;
16
+ public static IsNumber(value: any, as_typeof: boolean = false, excludeNull = false): value is number | NonNullableType<number> {
17
+ const output = Lib.IsNumber(value, as_typeof);
18
+ return excludeNull ? !Guards.IsNil(value) && output : output;
19
+ }
20
+
21
+ public static IsBoolean(value: any): value is boolean;
22
+ public static IsBoolean(value: any, excludeNull: true): value is NonNullableType<boolean>;
23
+ public static IsBoolean(value: any, excludeNull = false): value is boolean | NonNullableType<boolean> {
24
+ const output = Lib.GetType(value, true) === "boolean";
25
+ return excludeNull ? !Guards.IsNil(value) && output : output;
26
+ }
27
+
28
+ public static IsArray<T>(value: any): value is T[];
29
+ public static IsArray<T>(value: any, excludeNull: true): value is NonNullableType<T[]>;
30
+ public static IsArray<T>(value: any, excludeNull = false): value is T[] | NonNullableType<T[]> {
31
+ const output = Lib.IsArray(value);
32
+ return excludeNull ? !Guards.IsNil(value) && output : output;
33
+ }
34
+
35
+ public static IsObject(value: any): value is object;
36
+ public static IsObject(value: any, excludeNull: true): value is NonNullableType<object>;
37
+ public static IsObject(value: any, excludeNull = false): value is object | NonNullableType<object> {
38
+ const output = Lib.IsObject(value);
39
+ return excludeNull ? !Guards.IsNil(value) && output : output;
40
+ }
41
+
42
+ public static IsFunction(value: any): value is Function;
43
+ public static IsFunction(value: any, excludeNull: true): value is NonNullableType<Function>;
44
+ public static IsFunction(value: any, excludeNull = false): value is Function | NonNullableType<Function> {
45
+ const output = Lib.IsFunction(value);
46
+ return excludeNull ? !Guards.IsNil(value) && output : output;
47
+ }
48
+
49
+ public static IsNil(value: any): value is null | undefined {
50
+ return Lib.IsNil(value);
51
+ }
52
+
53
+ public static IsType<T>(obj: any): obj is T;
54
+ public static IsType<T>(obj: any, keys: (keyof T)[]): obj is T;
55
+ public static IsType<T>(obj: any, keys?: (keyof T)[]): obj is T {
56
+ if (!keys) return !this.IsNil(obj);
57
+ return keys.every((key) => key in obj);
58
+ }
59
+ }
60
+
61
+ export default Guards;