@valkyriestudios/utils 12.31.1 → 12.33.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/README.md CHANGED
@@ -1010,6 +1010,97 @@ These functions are the following:
1010
1010
  - **Is.Equal**
1011
1011
  - **Is.Eq**
1012
1012
 
1013
+ ### modules/PubSub
1014
+ A lightweight fire-and-forget publish–subscribe utility that supports both synchronous and asynchronous subscribers, with built‑in support for storing the last published data for each event.
1015
+
1016
+ This feature lets new subscribers immediately receive the most recent value for specific events.
1017
+
1018
+ The default storage behavior is configurable via the constructor or can be overridden on a per‑publish basis.
1019
+
1020
+ ##### Usage
1021
+ Simple fire-and-foreget without storage:
1022
+ ```typescript
1023
+ import { PubSub } from '@valkyriestudios/utils/modules/PubSub';
1024
+
1025
+ const relay = new PubSub({ name: 'MyPubSub', store: true });
1026
+
1027
+ /* Subscribe to events. */
1028
+ relay.subscribe({
1029
+ onDataUpdate: (data) => ...,
1030
+ onModalOpen: (...) => ...,
1031
+ }, 'client1');
1032
+
1033
+ /* Subscribe to events. */
1034
+ const client2 = relay.subscribe({
1035
+ onUserUpdate: (...) => ...,
1036
+ }); /* If not passing a client id a client id will be generated and returned */
1037
+
1038
+ /* Publish events. Since storage is enabled, the data is saved */
1039
+ relay.publish('onDataUpdate', {foo: 'bar'});
1040
+ relay.publish('onUserUpdate', {language: 'nl'});
1041
+
1042
+ /* When not necessary anymore */
1043
+ relay.unsubscribe(client2);
1044
+ ```
1045
+
1046
+ In-Memory storage included:
1047
+ ```typescript
1048
+ import { PubSub } from '@valkyriestudios/utils/modules/PubSub';
1049
+
1050
+ /* Create a PubSub instance that stores published data by default */
1051
+ const relay = new PubSub({ name: 'MyPubSub', store: true });
1052
+
1053
+ /* Subscribe to events. */
1054
+ relay.subscribe({
1055
+ dataUpdate: (data) => console.log('Subscriber 1 received dataUpdate:', data),
1056
+ /* This event will automatically unsubscribe after the first time its called */
1057
+ onceEvent: {
1058
+ run: (data) => console.log('This runs only once:', data),
1059
+ once: true,
1060
+ },
1061
+ }, 'client1');
1062
+
1063
+ /* Publish events. Since storage is enabled, the data is saved */
1064
+ relay.publish('dataUpdate', { foo: 'bar' });
1065
+ relay.publish('onceEvent', 'only once');
1066
+
1067
+ /* Later, a new subscriber immediately gets the last published value */
1068
+ relay.subscribe({
1069
+ dataUpdate: (data) => console.log('Late subscriber received dataUpdate:', data)
1070
+ }, 'client2');
1071
+ ```
1072
+
1073
+ ##### API Overview
1074
+ - **new PubSub(options?: { logger?: LogFn; name?: string; store?: boolean })**
1075
+ Creates a new PubSub instance.
1076
+ -- **logger (optional)**: Custom logging function to capture errors.
1077
+ -- **name (optional)**: A non‑empty string to name the instance.
1078
+ -- **store (optional)**: A boolean to set the default behavior for storing published data (default is false).
1079
+ - **publish(event: string, data?: unknown, store?: boolean): void**
1080
+ Publishes data for a specific event.
1081
+ -- If storage is enabled (via the default or the store override), the published data is stored for that event.
1082
+ -- All subscribers for the event are invoked.
1083
+ -- Supports both synchronous and asynchronous subscriber functions. Asynchronous errors are caught and logged.
1084
+ - **subscribe(events: RawEvents, client_id?: string, override?: boolean): string | null**
1085
+ Subscribes a client to one or more events.
1086
+ -- Returns a client ID (either provided or generated) which can later be used to unsubscribe.
1087
+ -- If stored data exists for an event, the new subscriber is immediately invoked with that data.
1088
+ -- The override flag (defaults to true) determines whether existing subscriptions for the same client are replaced.
1089
+ - **unsubscribe(client_id: string, event?: string | string[]): void**
1090
+ Unsubscribes a client from the specified event(s) or all events if none are provided.
1091
+ - **clientIds(event?: string | string[]): Set<string>**
1092
+ Retrieves a set of client IDs subscribed to one or more events.
1093
+ - **clear(event?: string | string[]): void**
1094
+ Clears subscriptions for specific event(s) or all events if no event is specified.
1095
+
1096
+ ##### Notes:
1097
+ - **Data Storage:**
1098
+ When storage is enabled, the last published value is tied to an event’s subscription entry. If there are no subscribers left, the stored value is automatically cleaned up.
1099
+ - **Error Handling:**
1100
+ Both synchronous and asynchronous errors are logged via the custom logger (if provided). The utility checks for thenables by verifying that the returned object has both then and catch methods.
1101
+ - **Flexibility:**
1102
+ The storage behavior can be controlled globally through the constructor or overridden for individual publish calls.
1103
+
1013
1104
  ### number/is(val:unknown)
1014
1105
  Check if a variable is a number
1015
1106
  ```typescript
package/date/isFormat.js CHANGED
@@ -92,12 +92,10 @@ function compileSpec(spec, is_chunk = false) {
92
92
  return cached;
93
93
  }
94
94
  function isDateFormat(input, spec) {
95
- if (typeof input !== 'string' || input.trim().length === 0) {
96
- throw new TypeError('isDateFormat: input must be a non-empty string');
97
- }
98
- if (typeof spec !== 'string') {
95
+ if (typeof input !== 'string')
96
+ throw new TypeError('isDateFormat: input must be a string');
97
+ if (typeof spec !== 'string')
99
98
  throw new TypeError('isDateFormat: spec must be a string');
100
- }
101
99
  const { tokens, rgx } = compileSpec(SPEC_ALIASES[spec] || spec);
102
100
  if (!tokens.length)
103
101
  return false;
package/deep/get.js CHANGED
@@ -32,21 +32,17 @@ function deepGet(obj, path, get_parent = false) {
32
32
  const extracted = [];
33
33
  for (let y = 0; y < node.length; y++) {
34
34
  const el = deepGet(node[y], key);
35
- if (el !== undefined)
36
- extracted.push(el);
35
+ if (el !== undefined) {
36
+ extracted.push(...Array.isArray(el) ? el : [el]);
37
+ }
37
38
  }
38
- node = extracted.length ? extracted.flat() : undefined;
39
+ node = extracted;
39
40
  nodes.push(node);
40
41
  }
41
42
  }
42
43
  else if (typeof node === 'object' && node !== null) {
43
44
  node = node[key];
44
45
  nodes.push(node);
45
- if (node === undefined)
46
- return undefined;
47
- }
48
- else {
49
- return undefined;
50
46
  }
51
47
  key = '';
52
48
  break;
@@ -67,9 +63,9 @@ function deepGet(obj, path, get_parent = false) {
67
63
  else {
68
64
  const extracted = [];
69
65
  for (let i = 0; i < node.length; i++) {
70
- const el = node[i];
71
- if (el?.[key] !== undefined)
72
- extracted.push(el?.[key]);
66
+ const val = node[i]?.[key];
67
+ if (val !== undefined)
68
+ extracted.push(...Array.isArray(val) ? val : [val]);
73
69
  }
74
70
  node = extracted.length ? extracted : undefined;
75
71
  nodes.push(node);
@@ -3,19 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toObject = toObject;
4
4
  exports.default = toObject;
5
5
  const isFormat_1 = require("../date/isFormat");
6
- const RGX_CLOSE = /\]/g;
7
- const RGX_DIGIT = /^\d+$/;
8
- function assignValue(acc, rawkey, value, single) {
6
+ const RGX_TOKENS = /[^.[\]]+/g;
7
+ function assign(acc, rawkey, value, single) {
9
8
  let cursor = acc;
10
- const keys = rawkey.replace(RGX_CLOSE, '').split(/\[|\./);
9
+ const keys = rawkey.match(RGX_TOKENS);
11
10
  const keys_len = keys.length;
12
11
  for (let i = 0; i < keys_len; i++) {
13
12
  const key = keys[i];
14
13
  if (i < (keys_len - 1)) {
15
14
  const n_key = Array.isArray(cursor) ? Number(key) : key;
16
- if (!cursor[n_key]) {
17
- cursor[n_key] = RGX_DIGIT.test(keys[i + 1]) ? [] : {};
18
- }
15
+ if (!cursor[n_key])
16
+ cursor[n_key] = Number.isInteger(+keys[i + 1]) ? [] : {};
19
17
  cursor = cursor[n_key];
20
18
  }
21
19
  else if (!(key in cursor) || single.has(key)) {
@@ -42,31 +40,31 @@ function toObject(form, config) {
42
40
  const nNumber = config?.normalize_number !== false;
43
41
  const acc = {};
44
42
  form.forEach((value, key) => {
45
- if (set !== true && typeof value === 'string' && value !== '' && !set.has(key)) {
43
+ if (set !== true && value !== '' && typeof value === 'string' && !set.has(key)) {
46
44
  if (nBool) {
47
- const lower = value.toLowerCase();
48
- if (lower === 'true') {
49
- assignValue(acc, key, true, single);
50
- return;
51
- }
52
- else if (lower === 'false') {
53
- assignValue(acc, key, false, single);
54
- return;
45
+ switch (value) {
46
+ case 'true':
47
+ case 'TRUE':
48
+ case 'True':
49
+ return assign(acc, key, true, single);
50
+ case 'false':
51
+ case 'FALSE':
52
+ case 'False':
53
+ return assign(acc, key, false, single);
54
+ default:
55
+ break;
55
56
  }
56
57
  }
57
58
  if (nNumber) {
58
59
  const nVal = Number(value);
59
- if (!isNaN(nVal)) {
60
- assignValue(acc, key, nVal, single);
61
- return;
62
- }
63
- }
64
- if (nDate && (0, isFormat_1.isDateFormat)(value, 'ISO')) {
65
- assignValue(acc, key, new Date(value), single);
66
- return;
60
+ if (!isNaN(nVal))
61
+ return assign(acc, key, nVal, single);
67
62
  }
63
+ if (nDate &&
64
+ (0, isFormat_1.isDateFormat)(value, 'ISO'))
65
+ return assign(acc, key, new Date(value), single);
68
66
  }
69
- assignValue(acc, key, value, single);
67
+ assign(acc, key, value, single);
70
68
  });
71
69
  return acc;
72
70
  }
package/index.d.ts CHANGED
@@ -38,10 +38,25 @@ declare module "array/join" {
38
38
  export { join, join as default };
39
39
  }
40
40
  declare module "object/merge" {
41
- type MergeOptions = {
42
- union?: boolean;
41
+ type Merge<T, U> = {
42
+ [K in keyof T | keyof U]: K extends keyof U ? U[K] : K extends keyof T ? T[K] : never;
43
43
  };
44
- function merge(target: Record<string, any>, source?: Record<string, any> | Record<string, any>[], opts?: MergeOptions): Record<string, any>;
44
+ type MergeNonUnion<T, U> = {
45
+ [K in keyof T]: K extends keyof U ? U[K] : T[K];
46
+ };
47
+ type MergeArray<T, U extends Record<string, unknown>[], Union extends boolean> = U extends [infer Head, ...infer Tail] ? Head extends Record<string, unknown> ? Tail extends Record<string, unknown>[] ? MergeArray<Union extends true ? Merge<T, Head> : MergeNonUnion<T, Head>, Tail, Union> : Union extends true ? Merge<T, Head> : MergeNonUnion<T, Head> : T : T;
48
+ function merge<T extends Record<string, unknown>, U extends Record<string, unknown>>(target: T, source: U, opts: {
49
+ union: true;
50
+ }): Merge<T, U>;
51
+ function merge<T extends Record<string, unknown>, U extends Record<string, unknown>>(target: T, source: U, opts?: {
52
+ union?: false;
53
+ }): MergeNonUnion<T, U>;
54
+ function merge<T extends Record<string, unknown>, U extends Record<string, unknown>[]>(target: T, sources: [...U], opts: {
55
+ union: true;
56
+ }): MergeArray<T, U, true>;
57
+ function merge<T extends Record<string, unknown>, U extends Record<string, unknown>[]>(target: T, sources: [...U], opts?: {
58
+ union?: false;
59
+ }): MergeArray<T, U, false>;
45
60
  export { merge, merge as default };
46
61
  }
47
62
  declare module "array/mapFn" {
@@ -629,3 +644,40 @@ declare module "hash/index" {
629
644
  import { guid } from "hash/guid";
630
645
  export { fnv1A, guid };
631
646
  }
647
+ declare module "modules/PubSub" {
648
+ type SyncFn = (data: unknown) => void;
649
+ type AsyncFn = (data: unknown) => Promise<void>;
650
+ type Fn = SyncFn | AsyncFn;
651
+ export type RawEvents = {
652
+ [key: string]: Fn | {
653
+ run: Fn;
654
+ once?: boolean;
655
+ };
656
+ };
657
+ export type LogObject = {
658
+ name: string;
659
+ event: string;
660
+ client_id: string;
661
+ msg: string;
662
+ on: Date;
663
+ data: unknown;
664
+ err: Error;
665
+ };
666
+ export type LogFn = (log: LogObject) => void;
667
+ export type PubSubOptions = {
668
+ logger?: LogFn;
669
+ name?: string;
670
+ store?: boolean;
671
+ };
672
+ class PubSub {
673
+ #private;
674
+ constructor(options?: PubSubOptions);
675
+ get name(): string;
676
+ publish(event: string, data?: unknown, store?: boolean): void;
677
+ subscribe(events: RawEvents, client_id?: string, override?: boolean): string | null;
678
+ unsubscribe(client_id: string, event?: string | string[]): void;
679
+ clientIds(event?: string | string[]): Set<string>;
680
+ clear(event?: string | string[]): void;
681
+ }
682
+ export { PubSub, PubSub as default };
683
+ }
@@ -0,0 +1,90 @@
1
+ type SyncFn = (data: unknown) => void;
2
+ type AsyncFn = (data: unknown) => Promise<void>;
3
+ type Fn = SyncFn | AsyncFn;
4
+ export type RawEvents = {
5
+ [key: string]: Fn | {
6
+ run: Fn;
7
+ once?: boolean;
8
+ };
9
+ };
10
+ /**
11
+ * Types of Log Object shapes
12
+ */
13
+ export type LogObject = {
14
+ name: string;
15
+ event: string;
16
+ client_id: string;
17
+ msg: string;
18
+ on: Date;
19
+ data: unknown;
20
+ err: Error;
21
+ };
22
+ /**
23
+ * Logger Function type
24
+ */
25
+ export type LogFn = (log: LogObject) => void;
26
+ export type PubSubOptions = {
27
+ /**
28
+ * Custom logger function, will receive an instance of LogObject when an error is thrown
29
+ */
30
+ logger?: LogFn;
31
+ /**
32
+ * Name of the pubsub (used in logs as well)
33
+ */
34
+ name?: string;
35
+ /**
36
+ * Whether or not the default is for the last provided value to be stored. (if not passed = false)
37
+ */
38
+ store?: boolean;
39
+ };
40
+ declare class PubSub {
41
+ #private;
42
+ constructor(options?: PubSubOptions);
43
+ /**
44
+ * Getter returning the name of the pubsub.
45
+ */
46
+ get name(): string;
47
+ /**
48
+ * Publish data for a specific event.
49
+ *
50
+ * An optional third parameter allows the producer to override the default storage behavior.
51
+ * If storage is enabled, the published data is saved so that new subscribers receive it immediately.
52
+ *
53
+ * @param {string} event - Name of the event.
54
+ * @param {unknown} data - (Optional) Data to publish.
55
+ * @param {boolean?} store - (Optional) Override for storing this event’s data.
56
+ */
57
+ publish(event: string, data?: unknown, store?: boolean): void;
58
+ /**
59
+ * Subscribes to one or more events as a client.
60
+ *
61
+ * After adding the subscription, if there is stored data for an event,
62
+ * the new subscriber's callback is immediately invoked with that data.
63
+ *
64
+ * @param {RawEvents} events - Raw Events object to subscribe to.
65
+ * @param {string?} client_id - Optional client id. If not provided one will be generated.
66
+ * @param {boolean?} override - Defaults to true. If false, existing subscriptions for the same event are preserved.
67
+ * @returns Client id if successful, or null for invalid input.
68
+ */
69
+ subscribe(events: RawEvents, client_id?: string, override?: boolean): string | null;
70
+ /**
71
+ * Unsubscribes a client from specified event(s) or from all events if none are specified.
72
+ *
73
+ * @param {string} client_id - Client ID to unsubscribe.
74
+ * @param {string|string[]?} event - (Optional) Unsubscribe only from these event(s).
75
+ */
76
+ unsubscribe(client_id: string, event?: string | string[]): void;
77
+ /**
78
+ * Retrieve the client IDs subscribed to a single, multiple, or all events.
79
+ *
80
+ * @param {string|string[]?} event - Optional event or events array.
81
+ */
82
+ clientIds(event?: string | string[]): Set<string>;
83
+ /**
84
+ * Clear subscriptions for specific event(s) or all events.
85
+ *
86
+ * @param {string|string[]?} event - Events to clear.
87
+ */
88
+ clear(event?: string | string[]): void;
89
+ }
90
+ export { PubSub, PubSub as default };
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _PubSub_instances, _PubSub_subscriptions, _PubSub_name, _PubSub_log, _PubSub_store_by_default, _PubSub_publishToSubscriber;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.default = exports.PubSub = void 0;
16
+ const is_1 = require("../array/is");
17
+ const is_2 = require("../function/is");
18
+ const noop_1 = require("../function/noop");
19
+ const guid_1 = require("../hash/guid");
20
+ const is_3 = require("../object/is");
21
+ const isNotEmpty_1 = require("../object/isNotEmpty");
22
+ const isNotEmpty_2 = require("../string/isNotEmpty");
23
+ class PubSub {
24
+ constructor(options = {}) {
25
+ _PubSub_instances.add(this);
26
+ _PubSub_subscriptions.set(this, new Map());
27
+ _PubSub_name.set(this, 'PubSub');
28
+ _PubSub_log.set(this, noop_1.noop);
29
+ _PubSub_store_by_default.set(this, false);
30
+ if (!(0, is_3.isObject)(options))
31
+ throw new Error('PubSub@ctor: options should be an object');
32
+ if ('logger' in options) {
33
+ if (!(0, is_2.isFunction)(options.logger))
34
+ throw new Error('PubSub@ctor: logger should be a function');
35
+ __classPrivateFieldSet(this, _PubSub_log, options.logger, "f");
36
+ }
37
+ if ('name' in options) {
38
+ if (!(0, isNotEmpty_2.isNotEmptyString)(options.name))
39
+ throw new Error('PubSub@ctor: name should be a non-empty string');
40
+ __classPrivateFieldSet(this, _PubSub_name, options.name.trim(), "f");
41
+ }
42
+ if ('store' in options) {
43
+ if (options.store !== true && options.store !== false)
44
+ throw new Error('PubSub@ctor: store should be a boolean');
45
+ __classPrivateFieldSet(this, _PubSub_store_by_default, !!options.store, "f");
46
+ }
47
+ }
48
+ get name() {
49
+ return __classPrivateFieldGet(this, _PubSub_name, "f");
50
+ }
51
+ publish(event, data, store) {
52
+ if (!(0, isNotEmpty_2.isNotEmptyString)(event))
53
+ return;
54
+ let entry = __classPrivateFieldGet(this, _PubSub_subscriptions, "f").get(event);
55
+ const shouldStore = store !== undefined ? store : __classPrivateFieldGet(this, _PubSub_store_by_default, "f");
56
+ if (!entry) {
57
+ if (!shouldStore)
58
+ return;
59
+ entry = { value: data, subscribers: new Map() };
60
+ __classPrivateFieldGet(this, _PubSub_subscriptions, "f").set(event, entry);
61
+ }
62
+ else if (shouldStore) {
63
+ entry.value = data;
64
+ }
65
+ const entriesArray = [...entry.subscribers.entries()];
66
+ for (const [client_id, client_handler] of entriesArray) {
67
+ __classPrivateFieldGet(this, _PubSub_instances, "m", _PubSub_publishToSubscriber).call(this, event, entry, client_id, client_handler, data);
68
+ }
69
+ if (!shouldStore && !entry.subscribers.size)
70
+ __classPrivateFieldGet(this, _PubSub_subscriptions, "f").delete(event);
71
+ }
72
+ subscribe(events, client_id, override = true) {
73
+ if (!(0, isNotEmpty_1.isNotEmptyObject)(events))
74
+ return null;
75
+ const uid = (0, isNotEmpty_2.isNotEmptyString)(client_id) ? client_id : (0, guid_1.guid)();
76
+ for (const event of Object.keys(events)) {
77
+ const raw_payload = events[event];
78
+ let normalized_payload = null;
79
+ if ((0, is_2.isFunction)(raw_payload)) {
80
+ normalized_payload = { run: raw_payload, once: false };
81
+ }
82
+ else if ((0, is_2.isFunction)(raw_payload?.run)) {
83
+ normalized_payload = {
84
+ run: raw_payload.run,
85
+ once: raw_payload.once === true,
86
+ };
87
+ }
88
+ if (!normalized_payload)
89
+ continue;
90
+ let entry = __classPrivateFieldGet(this, _PubSub_subscriptions, "f").get(event);
91
+ if (!entry) {
92
+ entry = { subscribers: new Map(), value: undefined };
93
+ __classPrivateFieldGet(this, _PubSub_subscriptions, "f").set(event, entry);
94
+ }
95
+ if (override === false && entry.subscribers.has(uid))
96
+ continue;
97
+ entry.subscribers.set(uid, normalized_payload);
98
+ if (entry.value !== undefined) {
99
+ __classPrivateFieldGet(this, _PubSub_instances, "m", _PubSub_publishToSubscriber).call(this, event, entry, uid, normalized_payload, entry.value);
100
+ }
101
+ }
102
+ return uid;
103
+ }
104
+ unsubscribe(client_id, event) {
105
+ if (!(0, isNotEmpty_2.isNotEmptyString)(client_id))
106
+ return;
107
+ const events = (0, isNotEmpty_2.isNotEmptyString)(event)
108
+ ? [event]
109
+ : (0, is_1.isArray)(event)
110
+ ? event
111
+ : [...__classPrivateFieldGet(this, _PubSub_subscriptions, "f").keys()];
112
+ for (let i = 0; i < events.length; i++) {
113
+ const ev = events[i];
114
+ if (!(0, isNotEmpty_2.isNotEmptyString)(ev))
115
+ continue;
116
+ const entry = __classPrivateFieldGet(this, _PubSub_subscriptions, "f").get(ev);
117
+ if (entry) {
118
+ entry.subscribers.delete(client_id);
119
+ if (!entry.subscribers.size) {
120
+ __classPrivateFieldGet(this, _PubSub_subscriptions, "f").delete(ev);
121
+ }
122
+ }
123
+ }
124
+ }
125
+ clientIds(event) {
126
+ const events = (0, isNotEmpty_2.isNotEmptyString)(event)
127
+ ? [event]
128
+ : (0, is_1.isArray)(event)
129
+ ? event
130
+ : [...__classPrivateFieldGet(this, _PubSub_subscriptions, "f").keys()];
131
+ const result = new Set();
132
+ for (let i = 0; i < events.length; i++) {
133
+ const entry = __classPrivateFieldGet(this, _PubSub_subscriptions, "f").get(events[i]);
134
+ if (entry) {
135
+ for (const key of entry.subscribers.keys()) {
136
+ result.add(key);
137
+ }
138
+ }
139
+ }
140
+ return result;
141
+ }
142
+ clear(event) {
143
+ if ((0, isNotEmpty_2.isNotEmptyString)(event)) {
144
+ __classPrivateFieldGet(this, _PubSub_subscriptions, "f").delete(event);
145
+ }
146
+ else if ((0, is_1.isArray)(event)) {
147
+ for (let i = 0; i < event.length; i++) {
148
+ const ev = event[i];
149
+ if ((0, isNotEmpty_2.isNotEmptyString)(ev)) {
150
+ __classPrivateFieldGet(this, _PubSub_subscriptions, "f").delete(ev);
151
+ }
152
+ }
153
+ }
154
+ else {
155
+ __classPrivateFieldSet(this, _PubSub_subscriptions, new Map(), "f");
156
+ }
157
+ }
158
+ }
159
+ exports.PubSub = PubSub;
160
+ exports.default = PubSub;
161
+ _PubSub_subscriptions = new WeakMap(), _PubSub_name = new WeakMap(), _PubSub_log = new WeakMap(), _PubSub_store_by_default = new WeakMap(), _PubSub_instances = new WeakSet(), _PubSub_publishToSubscriber = function _PubSub_publishToSubscriber(event, map, client_id, sub, data) {
162
+ try {
163
+ const out = sub.run(data);
164
+ if (sub.once)
165
+ map.subscribers.delete(client_id);
166
+ if (out && (0, is_2.isFunction)(out?.catch) && (0, is_2.isFunction)(out?.then)) {
167
+ Promise.resolve(out).catch(err => {
168
+ __classPrivateFieldGet(this, _PubSub_log, "f").call(this, {
169
+ name: __classPrivateFieldGet(this, _PubSub_name, "f"),
170
+ event,
171
+ client_id,
172
+ msg: '[async] ' + __classPrivateFieldGet(this, _PubSub_name, "f") + '@publish: ' + (err?.message || 'Unknown Error'),
173
+ on: new Date(),
174
+ data,
175
+ err: err,
176
+ });
177
+ });
178
+ }
179
+ }
180
+ catch (err) {
181
+ __classPrivateFieldGet(this, _PubSub_log, "f").call(this, {
182
+ name: __classPrivateFieldGet(this, _PubSub_name, "f"),
183
+ event,
184
+ client_id,
185
+ msg: '[sync] ' + __classPrivateFieldGet(this, _PubSub_name, "f") + '@publish: ' + (err?.message || 'Unknown Error'),
186
+ on: new Date(),
187
+ data,
188
+ err: err,
189
+ });
190
+ }
191
+ };
package/object/merge.d.ts CHANGED
@@ -1,17 +1,31 @@
1
- type MergeOptions = {
2
- /**
3
- * Defaults to false, when passed as true it ensures all keys from both objects
4
- * are available in the merged object
5
- */
6
- union?: boolean;
1
+ /**
2
+ * For union true: for every key in T or U, use U’s type if available; otherwise T’s.
3
+ */
4
+ type Merge<T, U> = {
5
+ [K in keyof T | keyof U]: K extends keyof U ? U[K] : K extends keyof T ? T[K] : never;
6
+ };
7
+ /**
8
+ * For union false: only keys in T are retained; if U has a key, override its type.
9
+ */
10
+ type MergeNonUnion<T, U> = {
11
+ [K in keyof T]: K extends keyof U ? U[K] : T[K];
7
12
  };
8
13
  /**
9
- * Deep merge two objects together while ensuring nested objects also get merged,
10
- * take note: this does not merge onto passed objects by reference but instead
11
- * returns a new object
12
- *
13
- * @param {Record<string,any>} target - Base Object
14
- * @param {Record<string,any>|Record<string,any>[]} source - (default={}) Object to merge onto base object
14
+ * Recursively merge an array of source objects.
15
+ * For each element in the array, apply Merge (if union is true)
16
+ * or MergeNonUnion (if union is false).
15
17
  */
16
- declare function merge(target: Record<string, any>, source?: Record<string, any> | Record<string, any>[], opts?: MergeOptions): Record<string, any>;
18
+ type MergeArray<T, U extends Record<string, unknown>[], Union extends boolean> = U extends [infer Head, ...infer Tail] ? Head extends Record<string, unknown> ? Tail extends Record<string, unknown>[] ? MergeArray<Union extends true ? Merge<T, Head> : MergeNonUnion<T, Head>, Tail, Union> : Union extends true ? Merge<T, Head> : MergeNonUnion<T, Head> : T : T;
19
+ declare function merge<T extends Record<string, unknown>, U extends Record<string, unknown>>(target: T, source: U, opts: {
20
+ union: true;
21
+ }): Merge<T, U>;
22
+ declare function merge<T extends Record<string, unknown>, U extends Record<string, unknown>>(target: T, source: U, opts?: {
23
+ union?: false;
24
+ }): MergeNonUnion<T, U>;
25
+ declare function merge<T extends Record<string, unknown>, U extends Record<string, unknown>[]>(target: T, sources: [...U], opts: {
26
+ union: true;
27
+ }): MergeArray<T, U, true>;
28
+ declare function merge<T extends Record<string, unknown>, U extends Record<string, unknown>[]>(target: T, sources: [...U], opts?: {
29
+ union?: false;
30
+ }): MergeArray<T, U, false>;
17
31
  export { merge, merge as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valkyriestudios/utils",
3
- "version": "12.31.1",
3
+ "version": "12.33.0",
4
4
  "description": "A collection of single-function utilities for common tasks",
5
5
  "author": {
6
6
  "name": "Peter Vermeulen",
@@ -1108,11 +1108,11 @@
1108
1108
  }
1109
1109
  },
1110
1110
  "devDependencies": {
1111
- "@types/node": "^22.13.1",
1111
+ "@types/node": "^22.13.5",
1112
1112
  "esbuild-register": "^3.6.0",
1113
- "eslint": "^9.19.0",
1113
+ "eslint": "^9.21.0",
1114
1114
  "nyc": "^17.1.0",
1115
1115
  "typescript": "^5.7.3",
1116
- "typescript-eslint": "^8.23.0"
1116
+ "typescript-eslint": "^8.25.0"
1117
1117
  }
1118
1118
  }