@axi-engine/utils 0.2.7 → 0.2.9

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/dist/index.d.mts CHANGED
@@ -37,6 +37,12 @@ type PathType = string | string[];
37
37
  * console.log(userInstance.timestamp); // Logs the current date
38
38
  */
39
39
  type Constructor<T = {}> = new (...args: any[]) => T;
40
+ /**
41
+ * Describes an object that can be unsubscribed from.
42
+ */
43
+ interface Unsubscribable {
44
+ unsubscribe(): void;
45
+ }
40
46
  /**
41
47
  * Defines the public, read-only contract for an event emitter.
42
48
  * It allows subscribing to an event but not emitting it.
@@ -48,7 +54,7 @@ type Subscribable<T extends any[]> = {
48
54
  * Subscribes a listener to this event.
49
55
  * @returns A function to unsubscribe the listener.
50
56
  */
51
- subscribe(listener: (...args: T) => void): () => void;
57
+ subscribe(listener: (...args: T) => void): Unsubscribable;
52
58
  unsubscribe(listener: (...args: T) => void): boolean;
53
59
  clear(): void;
54
60
  };
@@ -233,6 +239,34 @@ interface DataSink {
233
239
  interface DataStorage extends DataSource, DataSink {
234
240
  }
235
241
 
242
+ /**
243
+ * Represents a disposable resource, such as the execution of an Observable or an Event Listener.
244
+ * Allows grouping multiple teardown logic into a single unit (Composite Subscription).
245
+ */
246
+ declare class Subscription implements Unsubscribable {
247
+ private _closed;
248
+ private _teardowns;
249
+ /**
250
+ * Indicates whether this subscription has already been unsubscribed.
251
+ */
252
+ get closed(): boolean;
253
+ /**
254
+ * @param teardown Optional initial teardown logic to execute when unsubscribed.
255
+ */
256
+ constructor(teardown?: () => void);
257
+ /**
258
+ * Adds a teardown logic to this subscription.
259
+ * If the subscription is already closed, the teardown is executed immediately.
260
+ * @param teardown A function or another Unsubscribable object to be managed.
261
+ */
262
+ add(teardown: Unsubscribable | (() => void)): void;
263
+ /**
264
+ * Disposes the resources held by the subscription.
265
+ * Executes all attached teardown logic and clears the list.
266
+ */
267
+ unsubscribe(): void;
268
+ }
269
+
236
270
  /**
237
271
  * A minimal, type-safe event emitter for a single event.
238
272
  * It does not manage state, it only manages subscribers and event dispatching.
@@ -246,9 +280,15 @@ declare class Emitter<T extends any[]> implements Subscribable<T> {
246
280
  get listenerCount(): number;
247
281
  /**
248
282
  * Subscribes a listener to this event.
249
- * @returns A function to unsubscribe the listener.
283
+ * @returns A Subscription object to manage the unsubscription.
250
284
  */
251
- subscribe(listener: (...args: T) => void): () => void;
285
+ subscribe(listener: (...args: T) => void): Subscription;
286
+ /**
287
+ * Subscribes a listener that triggers only once and then automatically unsubscribes.
288
+ * @param listener The callback function to execute once.
289
+ * @returns A Subscription object (can be used to cancel before the event fires).
290
+ */
291
+ once(listener: (...args: T) => void): Subscription;
252
292
  /**
253
293
  * Manually unsubscribe by listener
254
294
  * @returns returns true if an listener has been removed, or false if the listener does not exist.
@@ -263,6 +303,33 @@ declare class Emitter<T extends any[]> implements Subscribable<T> {
263
303
  */
264
304
  clear(): void;
265
305
  }
306
+ /**
307
+ * An Emitter that stores the last emitted value.
308
+ * New subscribers immediately receive the last value upon subscription.
309
+ */
310
+ declare class StateEmitter<T extends any[]> extends Emitter<T> {
311
+ private _lastValue;
312
+ /**
313
+ * @param initialValue Optional initial value to set.
314
+ */
315
+ constructor(initialValue?: T);
316
+ /**
317
+ * Gets the current value synchronously without subscribing.
318
+ */
319
+ get value(): T | undefined;
320
+ /**
321
+ * Updates the state and notifies all listeners.
322
+ * @param args The new value(s).
323
+ */
324
+ emit(...args: T): void;
325
+ /**
326
+ * Subscribes to the event. If a value exists, the listener is called immediately.
327
+ * @param listener The callback function.
328
+ * @returns A Subscription object.
329
+ */
330
+ subscribe(listener: (...args: T) => void): Subscription;
331
+ clear(): void;
332
+ }
266
333
 
267
334
  /**
268
335
  * Type guard that checks if a value is a valid ScalarType.
@@ -424,4 +491,4 @@ declare class Registry<K extends PropertyKey, V> {
424
491
  clear(): void;
425
492
  }
426
493
 
427
- export { type AxiEngineConfig, type Constructor, type DataSink, type DataSource, type DataStorage, Emitter, type PathType, Registry, type ScalarType, type Subscribable, areArraysEqual, axiSettings, clampNumber, configure, ensurePathArray, ensurePathString, firstKeyOf, genArray, getPercentOf, getRandomElement, haveSameElements, isBoolean, isFunction, isNull, isNullOrUndefined, isNumber, isObject, isPercentageString, isPromise, isScalar, isSequentialStart, isString, isUndefined, last, randId, randInt, shuffleArray, throwError, throwIf, throwIfEmpty, unique };
494
+ export { type AxiEngineConfig, type Constructor, type DataSink, type DataSource, type DataStorage, Emitter, type PathType, Registry, type ScalarType, StateEmitter, type Subscribable, Subscription, type Unsubscribable, areArraysEqual, axiSettings, clampNumber, configure, ensurePathArray, ensurePathString, firstKeyOf, genArray, getPercentOf, getRandomElement, haveSameElements, isBoolean, isFunction, isNull, isNullOrUndefined, isNumber, isObject, isPercentageString, isPromise, isScalar, isSequentialStart, isString, isUndefined, last, randId, randInt, shuffleArray, throwError, throwIf, throwIfEmpty, unique };
package/dist/index.d.ts CHANGED
@@ -37,6 +37,12 @@ type PathType = string | string[];
37
37
  * console.log(userInstance.timestamp); // Logs the current date
38
38
  */
39
39
  type Constructor<T = {}> = new (...args: any[]) => T;
40
+ /**
41
+ * Describes an object that can be unsubscribed from.
42
+ */
43
+ interface Unsubscribable {
44
+ unsubscribe(): void;
45
+ }
40
46
  /**
41
47
  * Defines the public, read-only contract for an event emitter.
42
48
  * It allows subscribing to an event but not emitting it.
@@ -48,7 +54,7 @@ type Subscribable<T extends any[]> = {
48
54
  * Subscribes a listener to this event.
49
55
  * @returns A function to unsubscribe the listener.
50
56
  */
51
- subscribe(listener: (...args: T) => void): () => void;
57
+ subscribe(listener: (...args: T) => void): Unsubscribable;
52
58
  unsubscribe(listener: (...args: T) => void): boolean;
53
59
  clear(): void;
54
60
  };
@@ -233,6 +239,34 @@ interface DataSink {
233
239
  interface DataStorage extends DataSource, DataSink {
234
240
  }
235
241
 
242
+ /**
243
+ * Represents a disposable resource, such as the execution of an Observable or an Event Listener.
244
+ * Allows grouping multiple teardown logic into a single unit (Composite Subscription).
245
+ */
246
+ declare class Subscription implements Unsubscribable {
247
+ private _closed;
248
+ private _teardowns;
249
+ /**
250
+ * Indicates whether this subscription has already been unsubscribed.
251
+ */
252
+ get closed(): boolean;
253
+ /**
254
+ * @param teardown Optional initial teardown logic to execute when unsubscribed.
255
+ */
256
+ constructor(teardown?: () => void);
257
+ /**
258
+ * Adds a teardown logic to this subscription.
259
+ * If the subscription is already closed, the teardown is executed immediately.
260
+ * @param teardown A function or another Unsubscribable object to be managed.
261
+ */
262
+ add(teardown: Unsubscribable | (() => void)): void;
263
+ /**
264
+ * Disposes the resources held by the subscription.
265
+ * Executes all attached teardown logic and clears the list.
266
+ */
267
+ unsubscribe(): void;
268
+ }
269
+
236
270
  /**
237
271
  * A minimal, type-safe event emitter for a single event.
238
272
  * It does not manage state, it only manages subscribers and event dispatching.
@@ -246,9 +280,15 @@ declare class Emitter<T extends any[]> implements Subscribable<T> {
246
280
  get listenerCount(): number;
247
281
  /**
248
282
  * Subscribes a listener to this event.
249
- * @returns A function to unsubscribe the listener.
283
+ * @returns A Subscription object to manage the unsubscription.
250
284
  */
251
- subscribe(listener: (...args: T) => void): () => void;
285
+ subscribe(listener: (...args: T) => void): Subscription;
286
+ /**
287
+ * Subscribes a listener that triggers only once and then automatically unsubscribes.
288
+ * @param listener The callback function to execute once.
289
+ * @returns A Subscription object (can be used to cancel before the event fires).
290
+ */
291
+ once(listener: (...args: T) => void): Subscription;
252
292
  /**
253
293
  * Manually unsubscribe by listener
254
294
  * @returns returns true if an listener has been removed, or false if the listener does not exist.
@@ -263,6 +303,33 @@ declare class Emitter<T extends any[]> implements Subscribable<T> {
263
303
  */
264
304
  clear(): void;
265
305
  }
306
+ /**
307
+ * An Emitter that stores the last emitted value.
308
+ * New subscribers immediately receive the last value upon subscription.
309
+ */
310
+ declare class StateEmitter<T extends any[]> extends Emitter<T> {
311
+ private _lastValue;
312
+ /**
313
+ * @param initialValue Optional initial value to set.
314
+ */
315
+ constructor(initialValue?: T);
316
+ /**
317
+ * Gets the current value synchronously without subscribing.
318
+ */
319
+ get value(): T | undefined;
320
+ /**
321
+ * Updates the state and notifies all listeners.
322
+ * @param args The new value(s).
323
+ */
324
+ emit(...args: T): void;
325
+ /**
326
+ * Subscribes to the event. If a value exists, the listener is called immediately.
327
+ * @param listener The callback function.
328
+ * @returns A Subscription object.
329
+ */
330
+ subscribe(listener: (...args: T) => void): Subscription;
331
+ clear(): void;
332
+ }
266
333
 
267
334
  /**
268
335
  * Type guard that checks if a value is a valid ScalarType.
@@ -424,4 +491,4 @@ declare class Registry<K extends PropertyKey, V> {
424
491
  clear(): void;
425
492
  }
426
493
 
427
- export { type AxiEngineConfig, type Constructor, type DataSink, type DataSource, type DataStorage, Emitter, type PathType, Registry, type ScalarType, type Subscribable, areArraysEqual, axiSettings, clampNumber, configure, ensurePathArray, ensurePathString, firstKeyOf, genArray, getPercentOf, getRandomElement, haveSameElements, isBoolean, isFunction, isNull, isNullOrUndefined, isNumber, isObject, isPercentageString, isPromise, isScalar, isSequentialStart, isString, isUndefined, last, randId, randInt, shuffleArray, throwError, throwIf, throwIfEmpty, unique };
494
+ export { type AxiEngineConfig, type Constructor, type DataSink, type DataSource, type DataStorage, Emitter, type PathType, Registry, type ScalarType, StateEmitter, type Subscribable, Subscription, type Unsubscribable, areArraysEqual, axiSettings, clampNumber, configure, ensurePathArray, ensurePathString, firstKeyOf, genArray, getPercentOf, getRandomElement, haveSameElements, isBoolean, isFunction, isNull, isNullOrUndefined, isNumber, isObject, isPercentageString, isPromise, isScalar, isSequentialStart, isString, isUndefined, last, randId, randInt, shuffleArray, throwError, throwIf, throwIfEmpty, unique };
package/dist/index.js CHANGED
@@ -22,6 +22,8 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  Emitter: () => Emitter,
24
24
  Registry: () => Registry,
25
+ StateEmitter: () => StateEmitter,
26
+ Subscription: () => Subscription,
25
27
  areArraysEqual: () => areArraysEqual,
26
28
  axiSettings: () => axiSettings,
27
29
  clampNumber: () => clampNumber,
@@ -163,6 +165,48 @@ function configure(newConfig) {
163
165
  Object.assign(axiSettings, newConfig);
164
166
  }
165
167
 
168
+ // src/subscription.ts
169
+ var Subscription = class {
170
+ _closed = false;
171
+ _teardowns = [];
172
+ /**
173
+ * Indicates whether this subscription has already been unsubscribed.
174
+ */
175
+ get closed() {
176
+ return this._closed;
177
+ }
178
+ /**
179
+ * @param teardown Optional initial teardown logic to execute when unsubscribed.
180
+ */
181
+ constructor(teardown) {
182
+ if (teardown) {
183
+ this._teardowns.push(teardown);
184
+ }
185
+ }
186
+ /**
187
+ * Adds a teardown logic to this subscription.
188
+ * If the subscription is already closed, the teardown is executed immediately.
189
+ * @param teardown A function or another Unsubscribable object to be managed.
190
+ */
191
+ add(teardown) {
192
+ if (this._closed) {
193
+ isFunction(teardown) ? teardown() : teardown.unsubscribe();
194
+ return;
195
+ }
196
+ this._teardowns.push(isFunction(teardown) ? teardown : () => teardown.unsubscribe());
197
+ }
198
+ /**
199
+ * Disposes the resources held by the subscription.
200
+ * Executes all attached teardown logic and clears the list.
201
+ */
202
+ unsubscribe() {
203
+ if (this._closed) return;
204
+ this._closed = true;
205
+ this._teardowns.forEach((fn) => fn());
206
+ this._teardowns = [];
207
+ }
208
+ };
209
+
166
210
  // src/emitter.ts
167
211
  var Emitter = class {
168
212
  listeners = /* @__PURE__ */ new Set();
@@ -174,11 +218,23 @@ var Emitter = class {
174
218
  }
175
219
  /**
176
220
  * Subscribes a listener to this event.
177
- * @returns A function to unsubscribe the listener.
221
+ * @returns A Subscription object to manage the unsubscription.
178
222
  */
179
223
  subscribe(listener) {
180
224
  this.listeners.add(listener);
181
- return () => this.listeners.delete(listener);
225
+ return new Subscription(() => this.unsubscribe(listener));
226
+ }
227
+ /**
228
+ * Subscribes a listener that triggers only once and then automatically unsubscribes.
229
+ * @param listener The callback function to execute once.
230
+ * @returns A Subscription object (can be used to cancel before the event fires).
231
+ */
232
+ once(listener) {
233
+ const wrapper = (...args) => {
234
+ this.unsubscribe(wrapper);
235
+ listener(...args);
236
+ };
237
+ return this.subscribe(wrapper);
182
238
  }
183
239
  /**
184
240
  * Manually unsubscribe by listener
@@ -200,6 +256,46 @@ var Emitter = class {
200
256
  this.listeners.clear();
201
257
  }
202
258
  };
259
+ var StateEmitter = class extends Emitter {
260
+ _lastValue;
261
+ /**
262
+ * @param initialValue Optional initial value to set.
263
+ */
264
+ constructor(initialValue) {
265
+ super();
266
+ this._lastValue = initialValue ?? void 0;
267
+ }
268
+ /**
269
+ * Gets the current value synchronously without subscribing.
270
+ */
271
+ get value() {
272
+ return this._lastValue;
273
+ }
274
+ /**
275
+ * Updates the state and notifies all listeners.
276
+ * @param args The new value(s).
277
+ */
278
+ emit(...args) {
279
+ this._lastValue = args;
280
+ super.emit(...args);
281
+ }
282
+ /**
283
+ * Subscribes to the event. If a value exists, the listener is called immediately.
284
+ * @param listener The callback function.
285
+ * @returns A Subscription object.
286
+ */
287
+ subscribe(listener) {
288
+ const unsubscribe = super.subscribe(listener);
289
+ if (!isUndefined(this._lastValue)) {
290
+ listener(...this._lastValue);
291
+ }
292
+ return unsubscribe;
293
+ }
294
+ clear() {
295
+ super.clear();
296
+ this._lastValue = void 0;
297
+ }
298
+ };
203
299
 
204
300
  // src/math.ts
205
301
  function clampNumber(val, min, max) {
@@ -288,6 +384,8 @@ var Registry = class {
288
384
  0 && (module.exports = {
289
385
  Emitter,
290
386
  Registry,
387
+ StateEmitter,
388
+ Subscription,
291
389
  areArraysEqual,
292
390
  axiSettings,
293
391
  clampNumber,
package/dist/index.mjs CHANGED
@@ -105,6 +105,48 @@ function configure(newConfig) {
105
105
  Object.assign(axiSettings, newConfig);
106
106
  }
107
107
 
108
+ // src/subscription.ts
109
+ var Subscription = class {
110
+ _closed = false;
111
+ _teardowns = [];
112
+ /**
113
+ * Indicates whether this subscription has already been unsubscribed.
114
+ */
115
+ get closed() {
116
+ return this._closed;
117
+ }
118
+ /**
119
+ * @param teardown Optional initial teardown logic to execute when unsubscribed.
120
+ */
121
+ constructor(teardown) {
122
+ if (teardown) {
123
+ this._teardowns.push(teardown);
124
+ }
125
+ }
126
+ /**
127
+ * Adds a teardown logic to this subscription.
128
+ * If the subscription is already closed, the teardown is executed immediately.
129
+ * @param teardown A function or another Unsubscribable object to be managed.
130
+ */
131
+ add(teardown) {
132
+ if (this._closed) {
133
+ isFunction(teardown) ? teardown() : teardown.unsubscribe();
134
+ return;
135
+ }
136
+ this._teardowns.push(isFunction(teardown) ? teardown : () => teardown.unsubscribe());
137
+ }
138
+ /**
139
+ * Disposes the resources held by the subscription.
140
+ * Executes all attached teardown logic and clears the list.
141
+ */
142
+ unsubscribe() {
143
+ if (this._closed) return;
144
+ this._closed = true;
145
+ this._teardowns.forEach((fn) => fn());
146
+ this._teardowns = [];
147
+ }
148
+ };
149
+
108
150
  // src/emitter.ts
109
151
  var Emitter = class {
110
152
  listeners = /* @__PURE__ */ new Set();
@@ -116,11 +158,23 @@ var Emitter = class {
116
158
  }
117
159
  /**
118
160
  * Subscribes a listener to this event.
119
- * @returns A function to unsubscribe the listener.
161
+ * @returns A Subscription object to manage the unsubscription.
120
162
  */
121
163
  subscribe(listener) {
122
164
  this.listeners.add(listener);
123
- return () => this.listeners.delete(listener);
165
+ return new Subscription(() => this.unsubscribe(listener));
166
+ }
167
+ /**
168
+ * Subscribes a listener that triggers only once and then automatically unsubscribes.
169
+ * @param listener The callback function to execute once.
170
+ * @returns A Subscription object (can be used to cancel before the event fires).
171
+ */
172
+ once(listener) {
173
+ const wrapper = (...args) => {
174
+ this.unsubscribe(wrapper);
175
+ listener(...args);
176
+ };
177
+ return this.subscribe(wrapper);
124
178
  }
125
179
  /**
126
180
  * Manually unsubscribe by listener
@@ -142,6 +196,46 @@ var Emitter = class {
142
196
  this.listeners.clear();
143
197
  }
144
198
  };
199
+ var StateEmitter = class extends Emitter {
200
+ _lastValue;
201
+ /**
202
+ * @param initialValue Optional initial value to set.
203
+ */
204
+ constructor(initialValue) {
205
+ super();
206
+ this._lastValue = initialValue ?? void 0;
207
+ }
208
+ /**
209
+ * Gets the current value synchronously without subscribing.
210
+ */
211
+ get value() {
212
+ return this._lastValue;
213
+ }
214
+ /**
215
+ * Updates the state and notifies all listeners.
216
+ * @param args The new value(s).
217
+ */
218
+ emit(...args) {
219
+ this._lastValue = args;
220
+ super.emit(...args);
221
+ }
222
+ /**
223
+ * Subscribes to the event. If a value exists, the listener is called immediately.
224
+ * @param listener The callback function.
225
+ * @returns A Subscription object.
226
+ */
227
+ subscribe(listener) {
228
+ const unsubscribe = super.subscribe(listener);
229
+ if (!isUndefined(this._lastValue)) {
230
+ listener(...this._lastValue);
231
+ }
232
+ return unsubscribe;
233
+ }
234
+ clear() {
235
+ super.clear();
236
+ this._lastValue = void 0;
237
+ }
238
+ };
145
239
 
146
240
  // src/math.ts
147
241
  function clampNumber(val, min, max) {
@@ -229,6 +323,8 @@ var Registry = class {
229
323
  export {
230
324
  Emitter,
231
325
  Registry,
326
+ StateEmitter,
327
+ Subscription,
232
328
  areArraysEqual,
233
329
  axiSettings,
234
330
  clampNumber,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axi-engine/utils",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "Core utility library for Axi Engine, providing common functions for arrays, math, type guards, and more.",
5
5
  "license": "MIT",
6
6
  "repository": {