@axi-engine/utils 0.2.8 → 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.
284
+ */
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).
250
290
  */
251
- subscribe(listener: (...args: T) => void): () => void;
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.
@@ -269,13 +309,25 @@ declare class Emitter<T extends any[]> implements Subscribable<T> {
269
309
  */
270
310
  declare class StateEmitter<T extends any[]> extends Emitter<T> {
271
311
  private _lastValue;
312
+ /**
313
+ * @param initialValue Optional initial value to set.
314
+ */
272
315
  constructor(initialValue?: T);
273
316
  /**
274
317
  * Gets the current value synchronously without subscribing.
275
318
  */
276
319
  get value(): T | undefined;
320
+ /**
321
+ * Updates the state and notifies all listeners.
322
+ * @param args The new value(s).
323
+ */
277
324
  emit(...args: T): void;
278
- subscribe(listener: (...args: T) => void): () => 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;
279
331
  clear(): void;
280
332
  }
281
333
 
@@ -439,4 +491,4 @@ declare class Registry<K extends PropertyKey, V> {
439
491
  clear(): void;
440
492
  }
441
493
 
442
- export { type AxiEngineConfig, type Constructor, type DataSink, type DataSource, type DataStorage, Emitter, type PathType, Registry, type ScalarType, StateEmitter, 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.
284
+ */
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).
250
290
  */
251
- subscribe(listener: (...args: T) => void): () => void;
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.
@@ -269,13 +309,25 @@ declare class Emitter<T extends any[]> implements Subscribable<T> {
269
309
  */
270
310
  declare class StateEmitter<T extends any[]> extends Emitter<T> {
271
311
  private _lastValue;
312
+ /**
313
+ * @param initialValue Optional initial value to set.
314
+ */
272
315
  constructor(initialValue?: T);
273
316
  /**
274
317
  * Gets the current value synchronously without subscribing.
275
318
  */
276
319
  get value(): T | undefined;
320
+ /**
321
+ * Updates the state and notifies all listeners.
322
+ * @param args The new value(s).
323
+ */
277
324
  emit(...args: T): void;
278
- subscribe(listener: (...args: T) => void): () => 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;
279
331
  clear(): void;
280
332
  }
281
333
 
@@ -439,4 +491,4 @@ declare class Registry<K extends PropertyKey, V> {
439
491
  clear(): void;
440
492
  }
441
493
 
442
- export { type AxiEngineConfig, type Constructor, type DataSink, type DataSource, type DataStorage, Emitter, type PathType, Registry, type ScalarType, StateEmitter, 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
@@ -23,6 +23,7 @@ __export(index_exports, {
23
23
  Emitter: () => Emitter,
24
24
  Registry: () => Registry,
25
25
  StateEmitter: () => StateEmitter,
26
+ Subscription: () => Subscription,
26
27
  areArraysEqual: () => areArraysEqual,
27
28
  axiSettings: () => axiSettings,
28
29
  clampNumber: () => clampNumber,
@@ -164,6 +165,48 @@ function configure(newConfig) {
164
165
  Object.assign(axiSettings, newConfig);
165
166
  }
166
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
+
167
210
  // src/emitter.ts
168
211
  var Emitter = class {
169
212
  listeners = /* @__PURE__ */ new Set();
@@ -175,11 +218,23 @@ var Emitter = class {
175
218
  }
176
219
  /**
177
220
  * Subscribes a listener to this event.
178
- * @returns A function to unsubscribe the listener.
221
+ * @returns A Subscription object to manage the unsubscription.
179
222
  */
180
223
  subscribe(listener) {
181
224
  this.listeners.add(listener);
182
- 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);
183
238
  }
184
239
  /**
185
240
  * Manually unsubscribe by listener
@@ -203,6 +258,9 @@ var Emitter = class {
203
258
  };
204
259
  var StateEmitter = class extends Emitter {
205
260
  _lastValue;
261
+ /**
262
+ * @param initialValue Optional initial value to set.
263
+ */
206
264
  constructor(initialValue) {
207
265
  super();
208
266
  this._lastValue = initialValue ?? void 0;
@@ -213,10 +271,19 @@ var StateEmitter = class extends Emitter {
213
271
  get value() {
214
272
  return this._lastValue;
215
273
  }
274
+ /**
275
+ * Updates the state and notifies all listeners.
276
+ * @param args The new value(s).
277
+ */
216
278
  emit(...args) {
217
279
  this._lastValue = args;
218
280
  super.emit(...args);
219
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
+ */
220
287
  subscribe(listener) {
221
288
  const unsubscribe = super.subscribe(listener);
222
289
  if (!isUndefined(this._lastValue)) {
@@ -318,6 +385,7 @@ var Registry = class {
318
385
  Emitter,
319
386
  Registry,
320
387
  StateEmitter,
388
+ Subscription,
321
389
  areArraysEqual,
322
390
  axiSettings,
323
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
@@ -144,6 +198,9 @@ var Emitter = class {
144
198
  };
145
199
  var StateEmitter = class extends Emitter {
146
200
  _lastValue;
201
+ /**
202
+ * @param initialValue Optional initial value to set.
203
+ */
147
204
  constructor(initialValue) {
148
205
  super();
149
206
  this._lastValue = initialValue ?? void 0;
@@ -154,10 +211,19 @@ var StateEmitter = class extends Emitter {
154
211
  get value() {
155
212
  return this._lastValue;
156
213
  }
214
+ /**
215
+ * Updates the state and notifies all listeners.
216
+ * @param args The new value(s).
217
+ */
157
218
  emit(...args) {
158
219
  this._lastValue = args;
159
220
  super.emit(...args);
160
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
+ */
161
227
  subscribe(listener) {
162
228
  const unsubscribe = super.subscribe(listener);
163
229
  if (!isUndefined(this._lastValue)) {
@@ -258,6 +324,7 @@ export {
258
324
  Emitter,
259
325
  Registry,
260
326
  StateEmitter,
327
+ Subscription,
261
328
  areArraysEqual,
262
329
  axiSettings,
263
330
  clampNumber,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axi-engine/utils",
3
- "version": "0.2.8",
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": {