@oscarpalmer/atoms 0.117.0 → 0.118.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/dist/array/get.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { isPlainObject } from "../internal/is.js";
2
2
  function getArray(value, indiced) {
3
3
  if (Array.isArray(value)) return value;
4
+ if (value instanceof Map || value instanceof Set) return [...value.values()];
4
5
  if (!isPlainObject(value)) return [value];
5
6
  if (indiced !== true) return Object.values(value);
6
7
  const keys = Object.keys(value);
@@ -1,6 +1,5 @@
1
1
  import { isPlainObject } from "../internal/is.js";
2
2
  import { compare } from "../internal/value/compare.js";
3
- import "../is.js";
4
3
  function getCallback(value, key, forObject) {
5
4
  if (key != null) return;
6
5
  if (forObject && typeof value.value === "function") return value.value;
@@ -178,6 +178,7 @@ function flatten(array) {
178
178
  }
179
179
  function getArray(value, indiced) {
180
180
  if (Array.isArray(value)) return value;
181
+ if (value instanceof Map || value instanceof Set) return [...value.values()];
181
182
  if (!isPlainObject(value)) return [value];
182
183
  if (indiced !== true) return Object.values(value);
183
184
  const keys = Object.keys(value);
@@ -354,32 +355,6 @@ const comparators = {
354
355
  number: compareNumbers,
355
356
  symbol: compareSymbols
356
357
  };
357
- function isEmpty(value) {
358
- const values = Object.values(value);
359
- const { length } = values;
360
- for (let index = 0; index < length; index += 1) if (values[index] != null) return false;
361
- return true;
362
- }
363
- function isNullable(value) {
364
- return value == null;
365
- }
366
- function isNullableOrEmpty(value) {
367
- return value == null || getString(value) === "";
368
- }
369
- function isNullableOrWhitespace(value) {
370
- return value == null || EXPRESSION_WHITESPACE.test(getString(value));
371
- }
372
- function isNumerical(value) {
373
- return isNumber(value) || typeof value === "string" && value.trim().length > 0 && !Number.isNaN(+value);
374
- }
375
- function isObject(value) {
376
- return typeof value === "object" && value !== null || typeof value === "function";
377
- }
378
- function isPrimitive(value) {
379
- return value == null || EXPRESSION_PRIMITIVE.test(typeof value);
380
- }
381
- const EXPRESSION_PRIMITIVE = /^(bigint|boolean|number|string|symbol)$/;
382
- const EXPRESSION_WHITESPACE = /^\s*$/;
383
358
  function getCallback(value, key, forObject) {
384
359
  if (key != null) return;
385
360
  if (forObject && typeof value.value === "function") return value.value;
@@ -476,18 +451,21 @@ function unique(array, key) {
476
451
  }
477
452
  function noop() {}
478
453
  var Beacon = class {
454
+ #options;
479
455
  #state;
480
456
  get active() {
481
457
  return this.#state.active;
482
458
  }
483
459
  get observable() {
460
+ if (!this.#state.active) throw new Error(DESTROYED_BEACON);
484
461
  return this.#state.observable;
485
462
  }
486
463
  get value() {
487
464
  return this.#state.value;
488
465
  }
489
- constructor(value) {
466
+ constructor(value, options) {
490
467
  const observers = /* @__PURE__ */ new Map();
468
+ this.#options = getBeaconOptions(options);
491
469
  this.#state = {
492
470
  observers,
493
471
  value,
@@ -499,28 +477,30 @@ var Beacon = class {
499
477
  finishBeacon(this.#state, false);
500
478
  }
501
479
  emit(value, finish) {
502
- this.#on("next", finish ?? false, value);
480
+ this.#on("next", value, finish);
503
481
  }
504
482
  error(error, finish) {
505
- this.#on("error", finish ?? false, error);
483
+ this.#on("error", error, finish);
506
484
  }
507
485
  finish() {
508
486
  finishBeacon(this.#state, true);
509
487
  }
510
- #on(type, finish, value) {
511
- if (this.#state.active) {
512
- if (type === "next") this.#state.value = value;
513
- for (const [, observer] of this.#state.observers) observer[type]?.(value);
514
- if (finish === true) finishBeacon(this.#state, true);
488
+ #on(type, value, finish) {
489
+ if (!this.#state.active) return;
490
+ if (type === "next") {
491
+ if (this.#options.equal(this.#state.value, value)) return;
492
+ this.#state.value = value;
515
493
  }
494
+ for (const [, observer] of this.#state.observers) observer[type]?.(value);
495
+ if (finish === true) finishBeacon(this.#state, true);
516
496
  }
517
497
  };
518
498
  var Observable = class {
519
499
  #state;
520
- constructor(emitter, observers) {
500
+ constructor(instance, observers) {
521
501
  this.#state = {
522
502
  observers,
523
- beacon: emitter,
503
+ beacon: instance,
524
504
  closed: false
525
505
  };
526
506
  }
@@ -528,7 +508,7 @@ var Observable = class {
528
508
  this.#state.closed = true;
529
509
  }
530
510
  subscribe(first, second, third) {
531
- if (this.#state.closed) throw new Error("Cannot subscribe to a destroyed observable");
511
+ if (this.#state.closed) throw new Error(DESTROYED_OBSERVABLE);
532
512
  const observer = getObserver(first, second, third);
533
513
  const instance = new Subscription(this.#state);
534
514
  this.#state.observers.set(instance, observer);
@@ -557,40 +537,46 @@ var Subscription = class {
557
537
  }
558
538
  }
559
539
  };
560
- function beacon(value) {
561
- return new Beacon(value);
540
+ function beacon(value, options) {
541
+ return new Beacon(value, options);
562
542
  }
563
543
  function finishBeacon(state, emit) {
564
- if (state.active) {
565
- state.active = false;
566
- const entries = [...state.observers.entries()];
567
- const { length } = entries;
568
- for (let index = 0; index < length; index += 1) {
569
- const [subscription, observer] = entries[index];
570
- if (emit) observer.complete?.();
571
- subscription.destroy();
572
- }
573
- state.observable?.destroy();
574
- state.observers.clear();
544
+ if (!state.active) return;
545
+ state.active = false;
546
+ const entries = [...state.observers.entries()];
547
+ const { length } = entries;
548
+ for (let index = 0; index < length; index += 1) {
549
+ const [subscription, observer] = entries[index];
550
+ if (emit) observer.complete?.();
551
+ subscription.destroy();
575
552
  }
553
+ state.observable?.destroy();
554
+ state.observers.clear();
555
+ }
556
+ function getBeaconOptions(input) {
557
+ const options = isPlainObject(input) ? input : {};
558
+ options.equal = typeof options.equal === "function" ? options.equal : Object.is;
559
+ return options;
576
560
  }
577
- function getFunction(value, defaultValue) {
578
- return typeof value === "function" ? value : defaultValue;
561
+ function getObservableCallback(value) {
562
+ return typeof value === "function" ? value : noop;
579
563
  }
580
564
  function getObserver(first, second, third) {
581
565
  let observer = { next: noop };
582
566
  if (typeof first === "function") observer = {
583
- error: getFunction(second, noop),
584
- next: getFunction(first, noop),
585
- complete: getFunction(third, noop)
567
+ error: getObservableCallback(second),
568
+ next: getObservableCallback(first),
569
+ complete: getObservableCallback(third)
586
570
  };
587
571
  else if (typeof first === "object") {
588
- observer.complete = getFunction(first?.complete, noop);
589
- observer.error = getFunction(first?.error, noop);
590
- observer.next = getFunction(first?.next, noop);
572
+ observer.complete = getObservableCallback(first?.complete);
573
+ observer.error = getObservableCallback(first?.error);
574
+ observer.next = getObservableCallback(first?.next);
591
575
  }
592
576
  return observer;
593
577
  }
578
+ const DESTROYED_BEACON = "Cannot retrieve observable from a destroyed beacon";
579
+ const DESTROYED_OBSERVABLE = "Cannot subscribe to a destroyed observable";
594
580
  const ALPHA_FULL_HEX_SHORT = "f";
595
581
  const ALPHA_FULL_HEX_LONG = `${ALPHA_FULL_HEX_SHORT}${ALPHA_FULL_HEX_SHORT}`;
596
582
  const ALPHA_FULL_VALUE = 1;
@@ -1287,6 +1273,34 @@ function throttle(callback, time) {
1287
1273
  return throttler;
1288
1274
  }
1289
1275
  const DEFAULT_CACHE_SIZE = 65536;
1276
+ function isEmpty(value) {
1277
+ if (value == null) return true;
1278
+ if (typeof value === "string") return value.length === 0;
1279
+ const values = getArray(value);
1280
+ const { length } = values;
1281
+ for (let index = 0; index < length; index += 1) if (values[index] != null) return false;
1282
+ return true;
1283
+ }
1284
+ function isNullable(value) {
1285
+ return value == null;
1286
+ }
1287
+ function isNullableOrEmpty(value) {
1288
+ return value == null || getString(value) === "";
1289
+ }
1290
+ function isNullableOrWhitespace(value) {
1291
+ return value == null || EXPRESSION_WHITESPACE.test(getString(value));
1292
+ }
1293
+ function isNumerical(value) {
1294
+ return isNumber(value) || typeof value === "string" && value.trim().length > 0 && !Number.isNaN(+value);
1295
+ }
1296
+ function isObject(value) {
1297
+ return typeof value === "object" && value !== null || typeof value === "function";
1298
+ }
1299
+ function isPrimitive(value) {
1300
+ return value == null || EXPRESSION_PRIMITIVE.test(typeof value);
1301
+ }
1302
+ const EXPRESSION_PRIMITIVE = /^(bigint|boolean|number|string|symbol)$/;
1303
+ const EXPRESSION_WHITESPACE = /^\s*$/;
1290
1304
  let enabled = true;
1291
1305
  var Logger = class {
1292
1306
  get debug() {
package/dist/beacon.js CHANGED
@@ -1,17 +1,21 @@
1
+ import { isPlainObject } from "./internal/is.js";
1
2
  import { noop } from "./internal/function.js";
2
3
  var Beacon = class {
4
+ #options;
3
5
  #state;
4
6
  get active() {
5
7
  return this.#state.active;
6
8
  }
7
9
  get observable() {
10
+ if (!this.#state.active) throw new Error(DESTROYED_BEACON);
8
11
  return this.#state.observable;
9
12
  }
10
13
  get value() {
11
14
  return this.#state.value;
12
15
  }
13
- constructor(value) {
16
+ constructor(value, options) {
14
17
  const observers = /* @__PURE__ */ new Map();
18
+ this.#options = getBeaconOptions(options);
15
19
  this.#state = {
16
20
  observers,
17
21
  value,
@@ -23,28 +27,30 @@ var Beacon = class {
23
27
  finishBeacon(this.#state, false);
24
28
  }
25
29
  emit(value, finish) {
26
- this.#on("next", finish ?? false, value);
30
+ this.#on("next", value, finish);
27
31
  }
28
32
  error(error, finish) {
29
- this.#on("error", finish ?? false, error);
33
+ this.#on("error", error, finish);
30
34
  }
31
35
  finish() {
32
36
  finishBeacon(this.#state, true);
33
37
  }
34
- #on(type, finish, value) {
35
- if (this.#state.active) {
36
- if (type === "next") this.#state.value = value;
37
- for (const [, observer] of this.#state.observers) observer[type]?.(value);
38
- if (finish === true) finishBeacon(this.#state, true);
38
+ #on(type, value, finish) {
39
+ if (!this.#state.active) return;
40
+ if (type === "next") {
41
+ if (this.#options.equal(this.#state.value, value)) return;
42
+ this.#state.value = value;
39
43
  }
44
+ for (const [, observer] of this.#state.observers) observer[type]?.(value);
45
+ if (finish === true) finishBeacon(this.#state, true);
40
46
  }
41
47
  };
42
48
  var Observable = class {
43
49
  #state;
44
- constructor(emitter, observers) {
50
+ constructor(instance, observers) {
45
51
  this.#state = {
46
52
  observers,
47
- beacon: emitter,
53
+ beacon: instance,
48
54
  closed: false
49
55
  };
50
56
  }
@@ -52,7 +58,7 @@ var Observable = class {
52
58
  this.#state.closed = true;
53
59
  }
54
60
  subscribe(first, second, third) {
55
- if (this.#state.closed) throw new Error("Cannot subscribe to a destroyed observable");
61
+ if (this.#state.closed) throw new Error(DESTROYED_OBSERVABLE);
56
62
  const observer = getObserver(first, second, third);
57
63
  const instance = new Subscription(this.#state);
58
64
  this.#state.observers.set(instance, observer);
@@ -81,38 +87,44 @@ var Subscription = class {
81
87
  }
82
88
  }
83
89
  };
84
- function beacon(value) {
85
- return new Beacon(value);
90
+ function beacon(value, options) {
91
+ return new Beacon(value, options);
86
92
  }
87
93
  function finishBeacon(state, emit) {
88
- if (state.active) {
89
- state.active = false;
90
- const entries = [...state.observers.entries()];
91
- const { length } = entries;
92
- for (let index = 0; index < length; index += 1) {
93
- const [subscription, observer] = entries[index];
94
- if (emit) observer.complete?.();
95
- subscription.destroy();
96
- }
97
- state.observable?.destroy();
98
- state.observers.clear();
99
- }
94
+ if (!state.active) return;
95
+ state.active = false;
96
+ const entries = [...state.observers.entries()];
97
+ const { length } = entries;
98
+ for (let index = 0; index < length; index += 1) {
99
+ const [subscription, observer] = entries[index];
100
+ if (emit) observer.complete?.();
101
+ subscription.destroy();
102
+ }
103
+ state.observable?.destroy();
104
+ state.observers.clear();
105
+ }
106
+ function getBeaconOptions(input) {
107
+ const options = isPlainObject(input) ? input : {};
108
+ options.equal = typeof options.equal === "function" ? options.equal : Object.is;
109
+ return options;
100
110
  }
101
- function getFunction(value, defaultValue) {
102
- return typeof value === "function" ? value : defaultValue;
111
+ function getObservableCallback(value) {
112
+ return typeof value === "function" ? value : noop;
103
113
  }
104
114
  function getObserver(first, second, third) {
105
115
  let observer = { next: noop };
106
116
  if (typeof first === "function") observer = {
107
- error: getFunction(second, noop),
108
- next: getFunction(first, noop),
109
- complete: getFunction(third, noop)
117
+ error: getObservableCallback(second),
118
+ next: getObservableCallback(first),
119
+ complete: getObservableCallback(third)
110
120
  };
111
121
  else if (typeof first === "object") {
112
- observer.complete = getFunction(first?.complete, noop);
113
- observer.error = getFunction(first?.error, noop);
114
- observer.next = getFunction(first?.next, noop);
122
+ observer.complete = getObservableCallback(first?.complete);
123
+ observer.error = getObservableCallback(first?.error);
124
+ observer.next = getObservableCallback(first?.next);
115
125
  }
116
126
  return observer;
117
127
  }
128
+ var DESTROYED_BEACON = "Cannot retrieve observable from a destroyed beacon";
129
+ var DESTROYED_OBSERVABLE = "Cannot subscribe to a destroyed observable";
118
130
  export { beacon };
package/dist/index.js CHANGED
@@ -15,7 +15,6 @@ import { push } from "./array/push.js";
15
15
  import { max } from "./internal/math/aggregate.js";
16
16
  import { getString, join, words } from "./internal/string.js";
17
17
  import { compare } from "./internal/value/compare.js";
18
- import { isEmpty, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumerical, isObject, isPrimitive } from "./is.js";
19
18
  import { sort } from "./array/sort.js";
20
19
  import { splice } from "./array/splice.js";
21
20
  import { toMap } from "./array/to-map.js";
@@ -36,6 +35,7 @@ import { getColor } from "./color/index.js";
36
35
  import frame_rate_default from "./internal/frame-rate.js";
37
36
  import { SizedMap, SizedSet } from "./sized.js";
38
37
  import { debounce, memoize, throttle } from "./function.js";
38
+ import { isEmpty, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumerical, isObject, isPrimitive } from "./is.js";
39
39
  import { logger } from "./logger.js";
40
40
  import { average, count, min, round, sum } from "./math.js";
41
41
  import { setValue } from "./internal/value/set.js";
package/dist/is.js CHANGED
@@ -1,7 +1,10 @@
1
1
  import { isArrayOrPlainObject, isKey, isNumber, isPlainObject, isTypedArray } from "./internal/is.js";
2
+ import { getArray } from "./array/get.js";
2
3
  import { getString } from "./internal/string.js";
3
4
  function isEmpty(value) {
4
- const values = Object.values(value);
5
+ if (value == null) return true;
6
+ if (typeof value === "string") return value.length === 0;
7
+ const values = getArray(value);
5
8
  const { length } = values;
6
9
  for (let index = 0; index < length; index += 1) if (values[index] != null) return false;
7
10
  return true;
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "jsdom": "^27.3",
11
11
  "oxfmt": "^0.19",
12
12
  "oxlint": "^1.34",
13
- "rolldown": "1.0.0-beta.55",
13
+ "rolldown": "1.0.0-beta.56",
14
14
  "tslib": "^2.8",
15
15
  "typescript": "^5.9",
16
16
  "vite": "8.0.0-beta.2",
@@ -101,5 +101,5 @@
101
101
  },
102
102
  "type": "module",
103
103
  "types": "./types/index.d.ts",
104
- "version": "0.117.0"
104
+ "version": "0.118.0"
105
105
  }
package/src/array/get.ts CHANGED
@@ -45,6 +45,10 @@ export function getArray(value: unknown, indiced?: unknown): unknown[] {
45
45
  return value;
46
46
  }
47
47
 
48
+ if (value instanceof Map || value instanceof Set) {
49
+ return [...value.values()];
50
+ }
51
+
48
52
  if (!isPlainObject(value)) {
49
53
  return [value];
50
54
  }
package/src/array/sort.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import {compare} from '../internal/value/compare';
2
- import {isPlainObject} from '../is';
2
+ import {isPlainObject} from '../internal/is';
3
3
  import type {GenericCallback, PlainObject, Primitive} from '../models';
4
4
  import type {CallbackSorter, KeySorter} from './models';
5
5
 
package/src/beacon.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import {noop} from './internal/function';
2
+ import {isPlainObject} from './internal/is';
3
+ import type {PlainObject} from './models';
2
4
 
3
5
  class Beacon<Value> {
6
+ readonly #options: Options;
4
7
  readonly #state: BeaconState<Value>;
5
8
 
6
9
  /**
@@ -14,6 +17,10 @@ class Beacon<Value> {
14
17
  * The observable that can be subscribed to
15
18
  */
16
19
  get observable(): Observable<Value> {
20
+ if (!this.#state.active) {
21
+ throw new Error(DESTROYED_BEACON);
22
+ }
23
+
17
24
  return this.#state.observable;
18
25
  }
19
26
 
@@ -24,9 +31,11 @@ class Beacon<Value> {
24
31
  return this.#state.value;
25
32
  }
26
33
 
27
- constructor(value: Value) {
34
+ constructor(value: Value, options?: BeaconOptions<Value>) {
28
35
  const observers = new Map<Subscription<Value>, Observer<Value>>();
29
36
 
37
+ this.#options = getBeaconOptions(options);
38
+
30
39
  this.#state = {
31
40
  observers,
32
41
  value,
@@ -48,7 +57,7 @@ class Beacon<Value> {
48
57
  * @param finish Finish the beacon after emitting? _(defaults to `false`)_
49
58
  */
50
59
  emit(value: Value, finish?: boolean): void {
51
- this.#on('next', finish ?? false, value);
60
+ this.#on('next', value, finish);
52
61
  }
53
62
 
54
63
  /**
@@ -57,7 +66,7 @@ class Beacon<Value> {
57
66
  * @param finish Finish the beacon after emitting? _(defaults to `false`)_
58
67
  */
59
68
  error(error: Error, finish?: boolean): void {
60
- this.#on('error', finish ?? false, error);
69
+ this.#on('error', error, finish);
61
70
  }
62
71
 
63
72
  /**
@@ -67,23 +76,40 @@ class Beacon<Value> {
67
76
  finishBeacon(this.#state, true);
68
77
  }
69
78
 
70
- #on(type: keyof Observer<never>, finish: boolean, value: Error | Value): void {
71
- if (this.#state.active) {
72
- if (type === 'next') {
73
- this.#state.value = value as Value;
74
- }
79
+ #on(type: keyof Observer<never>, value: Error | Value, finish?: boolean): void {
80
+ if (!this.#state.active) {
81
+ return;
82
+ }
75
83
 
76
- for (const [, observer] of this.#state.observers) {
77
- observer[type]?.(value as never);
84
+ if (type === 'next') {
85
+ if (this.#options.equal(this.#state.value, value as Value)) {
86
+ return;
78
87
  }
79
88
 
80
- if (finish === true) {
81
- finishBeacon(this.#state, true);
82
- }
89
+ this.#state.value = value as Value;
90
+ }
91
+
92
+ for (const [, observer] of this.#state.observers) {
93
+ observer[type]?.(value as never);
94
+ }
95
+
96
+ if (finish === true) {
97
+ finishBeacon(this.#state, true);
83
98
  }
84
99
  }
85
100
  }
86
101
 
102
+ type BeaconOptions<Value> = {
103
+ /**
104
+ * Method for comparing values for equality
105
+ * @param first First value
106
+ * @param second Second value
107
+ * @returns `true` if the values are equal, otherwise `false`
108
+ * @default Object.is
109
+ */
110
+ equal?: (first: Value, second: Value) => boolean;
111
+ };
112
+
87
113
  type BeaconState<Value> = {
88
114
  active: boolean;
89
115
  observable: Observable<Value>;
@@ -94,10 +120,10 @@ type BeaconState<Value> = {
94
120
  class Observable<Value> {
95
121
  readonly #state: ObservableState<Value>;
96
122
 
97
- constructor(emitter: Beacon<Value>, observers: Map<Subscription<Value>, Observer<Value>>) {
123
+ constructor(instance: Beacon<Value>, observers: Map<Subscription<Value>, Observer<Value>>) {
98
124
  this.#state = {
99
125
  observers,
100
- beacon: emitter,
126
+ beacon: instance,
101
127
  closed: false,
102
128
  };
103
129
  }
@@ -135,7 +161,7 @@ class Observable<Value> {
135
161
  third?: () => void,
136
162
  ): Subscription<Value> {
137
163
  if (this.#state.closed) {
138
- throw new Error('Cannot subscribe to a destroyed observable');
164
+ throw new Error(DESTROYED_OBSERVABLE);
139
165
  }
140
166
 
141
167
  const observer = getObserver(first, second, third);
@@ -170,6 +196,8 @@ type Observer<Value> = {
170
196
  next?: (value: Value) => void;
171
197
  };
172
198
 
199
+ type Options = Required<BeaconOptions<unknown>>;
200
+
173
201
  class Subscription<Value> {
174
202
  readonly #state: SubscriptionState<Value>;
175
203
 
@@ -214,40 +242,53 @@ type SubscriptionState<Value> = {
214
242
  observers: Map<Subscription<Value>, Observer<Value>>;
215
243
  };
216
244
 
245
+ //
246
+
217
247
  /**
218
248
  * Create a new beacon
219
249
  * @param value Initial value
250
+ * @param options Beacon options
220
251
  * @returns Beacon instance
221
252
  */
222
- export function beacon<Value>(value: Value): Beacon<Value> {
223
- return new Beacon(value);
253
+ export function beacon<Value>(value: Value, options?: BeaconOptions<Value>): Beacon<Value> {
254
+ return new Beacon(value, options);
224
255
  }
225
256
 
226
257
  function finishBeacon<Value>(state: BeaconState<Value>, emit: boolean): void {
227
- if (state.active) {
228
- state.active = false;
258
+ if (!state.active) {
259
+ return;
260
+ }
229
261
 
230
- const entries = [...state.observers.entries()];
231
- const {length} = entries;
262
+ state.active = false;
232
263
 
233
- for (let index = 0; index < length; index += 1) {
234
- const [subscription, observer] = entries[index];
264
+ const entries = [...state.observers.entries()];
265
+ const {length} = entries;
235
266
 
236
- if (emit) {
237
- observer.complete?.();
238
- }
267
+ for (let index = 0; index < length; index += 1) {
268
+ const [subscription, observer] = entries[index];
239
269
 
240
- subscription.destroy();
270
+ if (emit) {
271
+ observer.complete?.();
241
272
  }
242
273
 
243
- state.observable?.destroy();
244
-
245
- state.observers.clear();
274
+ subscription.destroy();
246
275
  }
276
+
277
+ state.observable?.destroy();
278
+
279
+ state.observers.clear();
247
280
  }
248
281
 
249
- function getFunction<Callback>(value: Callback, defaultValue: Callback): Callback {
250
- return typeof value === 'function' ? value : defaultValue;
282
+ function getBeaconOptions(input?: BeaconOptions<never>): Options {
283
+ const options: Partial<Options> = isPlainObject(input) ? (input as PlainObject) : {};
284
+
285
+ options.equal = typeof options.equal === 'function' ? options.equal : Object.is;
286
+
287
+ return options as Options;
288
+ }
289
+
290
+ function getObservableCallback<Callback>(value: Callback): Callback {
291
+ return typeof value === 'function' ? value : (noop as Callback);
251
292
  }
252
293
 
253
294
  function getObserver<Value>(
@@ -261,17 +302,23 @@ function getObserver<Value>(
261
302
 
262
303
  if (typeof first === 'function') {
263
304
  observer = {
264
- error: getFunction(second, noop),
265
- next: getFunction(first, noop),
266
- complete: getFunction(third, noop),
305
+ error: getObservableCallback(second),
306
+ next: getObservableCallback(first),
307
+ complete: getObservableCallback(third),
267
308
  };
268
309
  } else if (typeof first === 'object') {
269
- observer.complete = getFunction(first?.complete, noop);
270
- observer.error = getFunction(first?.error, noop);
271
- observer.next = getFunction(first?.next, noop);
310
+ observer.complete = getObservableCallback(first?.complete);
311
+ observer.error = getObservableCallback(first?.error);
312
+ observer.next = getObservableCallback(first?.next);
272
313
  }
273
314
 
274
315
  return observer;
275
316
  }
276
317
 
318
+ //
319
+
320
+ const DESTROYED_BEACON = 'Cannot retrieve observable from a destroyed beacon';
321
+
322
+ const DESTROYED_OBSERVABLE = 'Cannot subscribe to a destroyed observable';
323
+
277
324
  export type {Beacon, Observable, Observer, Subscription};
@@ -1,4 +1,4 @@
1
- import type {GenericCallback, PlainObject} from '../../models';
1
+ import type {GenericCallback, NumericalValues, PlainObject} from '../../models';
2
2
 
3
3
  type Aggregation = {
4
4
  count: number;
@@ -9,13 +9,6 @@ type AggregationCallback = (current: number, value: number, notNumber: boolean)
9
9
 
10
10
  type AggregationType = 'average' | 'max' | 'min' | 'sum';
11
11
 
12
- /**
13
- * The numerical values of an object
14
- */
15
- export type OnlyNumericalKeys<Item> = {
16
- [Key in keyof Item as Item[Key] extends number ? Key : never]: Item[Key];
17
- };
18
-
19
12
  export function aggregate(type: AggregationType, array: unknown[], key: unknown): Aggregation {
20
13
  const length = Array.isArray(array) ? array.length : 0;
21
14
 
@@ -73,7 +66,7 @@ export function max<Item extends PlainObject>(
73
66
  */
74
67
  export function max<Item extends PlainObject>(
75
68
  items: Item[],
76
- key: keyof OnlyNumericalKeys<Item>,
69
+ key: keyof NumericalValues<Item>,
77
70
  ): number;
78
71
 
79
72
  /**
package/src/is.ts CHANGED
@@ -1,14 +1,23 @@
1
+ import {getArray} from './array/get';
1
2
  import {isNumber} from './internal/is';
2
3
  import {getString} from './internal/string';
3
- import type {ArrayOrPlainObject, Primitive} from './models';
4
+ import type {Primitive} from './models';
4
5
 
5
6
  /**
6
- * Is the array or object completely empty, or only containing `null` or `undefined` values?
7
- * @param value Array or object to check
8
- * @returns `true` if the value is considered empty, otherwise `false`
7
+ * Is the value empty, or only containing `null` or `undefined` values?
8
+ * @param value Object to check
9
+ * @returns `true` if the object is considered empty, otherwise `false`
9
10
  */
10
- export function isEmpty(value: ArrayOrPlainObject): boolean {
11
- const values = Object.values(value);
11
+ export function isEmpty(value: unknown): boolean {
12
+ if (value == null) {
13
+ return true;
14
+ }
15
+
16
+ if (typeof value === 'string') {
17
+ return value.length === 0;
18
+ }
19
+
20
+ const values = getArray(value);
12
21
  const {length} = values;
13
22
 
14
23
  for (let index = 0; index < length; index += 1) {
package/src/math.ts CHANGED
@@ -1,5 +1,5 @@
1
- import {aggregate, type OnlyNumericalKeys} from './internal/math/aggregate';
2
- import type {PlainObject} from './models';
1
+ import {aggregate} from './internal/math/aggregate';
2
+ import type {NumericalValues, PlainObject} from './models';
3
3
 
4
4
  /**
5
5
  * Get the average value from a list of items
@@ -20,7 +20,7 @@ export function average<Item extends PlainObject>(
20
20
  */
21
21
  export function average<Item extends PlainObject>(
22
22
  items: Item[],
23
- key: keyof OnlyNumericalKeys<Item>,
23
+ key: keyof NumericalValues<Item>,
24
24
  ): number;
25
25
 
26
26
  /**
@@ -119,7 +119,7 @@ export function min<Item extends PlainObject>(
119
119
  */
120
120
  export function min<Item extends PlainObject>(
121
121
  items: Item[],
122
- key: keyof OnlyNumericalKeys<Item>,
122
+ key: keyof NumericalValues<Item>,
123
123
  ): number;
124
124
 
125
125
  /**
@@ -174,7 +174,7 @@ export function sum<Item extends PlainObject>(
174
174
  */
175
175
  export function sum<Item extends PlainObject>(
176
176
  items: Item[],
177
- key: keyof OnlyNumericalKeys<Item>,
177
+ key: keyof NumericalValues<Item>,
178
178
  ): number;
179
179
 
180
180
  /**
@@ -190,4 +190,5 @@ export function sum(array: unknown[], key?: unknown): number {
190
190
  return aggregated.count > 0 ? aggregated.value : Number.NaN;
191
191
  }
192
192
 
193
- export {max, type OnlyNumericalKeys} from './internal/math/aggregate';
193
+ export {max} from './internal/math/aggregate';
194
+ export type {NumericalValues}
package/src/models.ts CHANGED
@@ -38,6 +38,9 @@ export type KeyedValue<Item, Key extends keyof Item> = Item[Key] extends Propert
38
38
  export type NestedArray<Value> =
39
39
  Value extends Array<infer NestedValue> ? NestedArray<NestedValue> : Value;
40
40
 
41
+ /**
42
+ * All nested keys of an object as dot notation strings _(up to 5 levels deep)_
43
+ */
41
44
  export type NestedKeys<Value extends PlainObject> = _NestedKeys<Value>;
42
45
 
43
46
  type _NestedKeys<Value, Depth extends number = 5> = Depth extends 0
@@ -47,11 +50,9 @@ type _NestedKeys<Value, Depth extends number = 5> = Depth extends 0
47
50
  ? // Tuple: extract actual indices
48
51
  {
49
52
  [Key in keyof Value]-?: Key extends `${number}`
50
- ? Value[Key] extends readonly any[]
51
- ? `${Key}` | `${Key}.${_NestedKeys<Value[Key], SubtractDepth<Depth>>}`
52
- : Value[Key] extends PlainObject
53
- ? `${Key}` | `${Key}.${_NestedKeys<Value[Key], SubtractDepth<Depth>>}`
54
- : `${Key}`
53
+ ? NonNullable<Value[Key]> extends readonly any[] | PlainObject
54
+ ? `${Key}` | `${Key}.${_NestedKeys<NonNullable<Value[Key]>, SubtractDepth<Depth>>}`
55
+ : `${Key}`
55
56
  : never;
56
57
  }[number]
57
58
  : // Array: use no indices
@@ -59,11 +60,9 @@ type _NestedKeys<Value, Depth extends number = 5> = Depth extends 0
59
60
  : Value extends PlainObject
60
61
  ? {
61
62
  [Key in keyof Value]-?: Key extends number | string
62
- ? Value[Key] extends readonly any[]
63
- ? `${Key}` | `${Key}.${_NestedKeys<Value[Key], SubtractDepth<Depth>>}`
64
- : Value[Key] extends PlainObject
65
- ? `${Key}` | `${Key}.${_NestedKeys<Value[Key], SubtractDepth<Depth>>}`
66
- : `${Key}`
63
+ ? NonNullable<Value[Key]> extends readonly any[] | PlainObject
64
+ ? `${Key}` | `${Key}.${_NestedKeys<NonNullable<Value[Key]>, SubtractDepth<Depth>>}`
65
+ : `${Key}`
67
66
  : never;
68
67
  }[keyof Value]
69
68
  : never;
@@ -75,30 +74,43 @@ export type NestedPartial<T> = {
75
74
  [K in keyof T]?: T[K] extends object ? NestedPartial<T[K]> : T[K];
76
75
  };
77
76
 
78
- export type NestedValue<Value extends PlainObject, Path extends string> = Simplify<
79
- _NestedValue<Value, Path>
80
- >;
81
-
82
- export type _NestedValue<Value, Path extends string> = Path extends keyof Value
83
- ? Value[Path]
84
- : Path extends `${infer Key}.${infer Rest}`
85
- ? Key extends keyof Value
86
- ? _NestedValue<Value[Key], Rest>
87
- : Key extends `${number}`
88
- ? Value extends readonly any[]
89
- ? _NestedValue<Value[number], Rest>
90
- : never
91
- : never
92
- : Path extends `${number}`
77
+ /**
78
+ * The value for a nested key of an object
79
+ */
80
+ export type NestedValue<Value extends PlainObject, Path extends string> = _NestedValue<Value, Path>;
81
+
82
+ type _NestedValue<Value, Path extends string> = Path extends `${infer Key}.${infer Rest}`
83
+ ? Key extends keyof Value
84
+ ? undefined extends Value[Key]
85
+ ? _NestedValue<Exclude<Value[Key], undefined>, Rest> | undefined
86
+ : _NestedValue<Value[Key], Rest>
87
+ : Key extends `${number}`
93
88
  ? Value extends readonly any[]
94
- ? Value[number]
89
+ ? _NestedValue<Value[number], Rest>
95
90
  : never
91
+ : never
92
+ : Path extends `${number}`
93
+ ? Value extends readonly any[]
94
+ ? Value[number]
95
+ : never
96
+ : Path extends keyof Value
97
+ ? Value[Path]
96
98
  : never;
97
99
 
100
+ /**
101
+ * The nested (keyed) values of an object _(up to 5 levels deep)_
102
+ */
98
103
  export type NestedValues<Value extends PlainObject> = {
99
104
  [Path in NestedKeys<Value>]: NestedValue<Value, Path>;
100
105
  };
101
106
 
107
+ /**
108
+ * The numerical values of an object
109
+ */
110
+ export type NumericalValues<Item extends PlainObject> = {
111
+ [Key in keyof Item as Item[Key] extends number ? Key : never]: Item[Key];
112
+ };
113
+
102
114
  /**
103
115
  * A generic object
104
116
  */
package/src/sized.ts CHANGED
@@ -206,6 +206,6 @@ function getMaximum(first?: unknown, second?: unknown): number {
206
206
 
207
207
  //
208
208
 
209
- const MAXIMUM_ABSOLUTE = 16_777_216;
209
+ const MAXIMUM_ABSOLUTE = 16_777_216; // 2^24
210
210
 
211
- const MAXIMUM_DEFAULT = 1_048_576;
211
+ const MAXIMUM_DEFAULT = 1_048_576; // 2^20
package/types/beacon.d.ts CHANGED
@@ -12,7 +12,7 @@ declare class Beacon<Value> {
12
12
  * The current value
13
13
  */
14
14
  get value(): Value;
15
- constructor(value: Value);
15
+ constructor(value: Value, options?: BeaconOptions<Value>);
16
16
  /**
17
17
  * Destroy the beacon
18
18
  */
@@ -34,9 +34,19 @@ declare class Beacon<Value> {
34
34
  */
35
35
  finish(): void;
36
36
  }
37
+ type BeaconOptions<Value> = {
38
+ /**
39
+ * Method for comparing values for equality
40
+ * @param first First value
41
+ * @param second Second value
42
+ * @returns `true` if the values are equal, otherwise `false`
43
+ * @default Object.is
44
+ */
45
+ equal?: (first: Value, second: Value) => boolean;
46
+ };
37
47
  declare class Observable<Value> {
38
48
  #private;
39
- constructor(emitter: Beacon<Value>, observers: Map<Subscription<Value>, Observer<Value>>);
49
+ constructor(instance: Beacon<Value>, observers: Map<Subscription<Value>, Observer<Value>>);
40
50
  /**
41
51
  * Destroy the observable
42
52
  */
@@ -94,7 +104,8 @@ declare class Subscription<Value> {
94
104
  /**
95
105
  * Create a new beacon
96
106
  * @param value Initial value
107
+ * @param options Beacon options
97
108
  * @returns Beacon instance
98
109
  */
99
- export declare function beacon<Value>(value: Value): Beacon<Value>;
110
+ export declare function beacon<Value>(value: Value, options?: BeaconOptions<Value>): Beacon<Value>;
100
111
  export type { Beacon, Observable, Observer, Subscription };
@@ -1,15 +1,9 @@
1
- import type { PlainObject } from '../../models';
1
+ import type { NumericalValues, PlainObject } from '../../models';
2
2
  type Aggregation = {
3
3
  count: number;
4
4
  value: number;
5
5
  };
6
6
  type AggregationType = 'average' | 'max' | 'min' | 'sum';
7
- /**
8
- * The numerical values of an object
9
- */
10
- export type OnlyNumericalKeys<Item> = {
11
- [Key in keyof Item as Item[Key] extends number ? Key : never]: Item[Key];
12
- };
13
7
  export declare function aggregate(type: AggregationType, array: unknown[], key: unknown): Aggregation;
14
8
  /**
15
9
  * Get the maximum value from a list of items
@@ -24,7 +18,7 @@ export declare function max<Item extends PlainObject>(items: Item[], callback: (
24
18
  * @param key Key to use for value
25
19
  * @returns Maximum value, or `NaN` if no maximum can be found
26
20
  */
27
- export declare function max<Item extends PlainObject>(items: Item[], key: keyof OnlyNumericalKeys<Item>): number;
21
+ export declare function max<Item extends PlainObject>(items: Item[], key: keyof NumericalValues<Item>): number;
28
22
  /**
29
23
  * Get the maximum value from a list of numbers
30
24
  * @param values List of numbers
package/types/is.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import type { ArrayOrPlainObject, Primitive } from './models';
1
+ import type { Primitive } from './models';
2
2
  /**
3
- * Is the array or object completely empty, or only containing `null` or `undefined` values?
4
- * @param value Array or object to check
5
- * @returns `true` if the value is considered empty, otherwise `false`
3
+ * Is the value empty, or only containing `null` or `undefined` values?
4
+ * @param value Object to check
5
+ * @returns `true` if the object is considered empty, otherwise `false`
6
6
  */
7
- export declare function isEmpty(value: ArrayOrPlainObject): boolean;
7
+ export declare function isEmpty(value: unknown): boolean;
8
8
  /**
9
9
  * Is the value `undefined` or `null`?
10
10
  * @param value Value to check
package/types/math.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { type OnlyNumericalKeys } from './internal/math/aggregate';
2
- import type { PlainObject } from './models';
1
+ import type { NumericalValues, PlainObject } from './models';
3
2
  /**
4
3
  * Get the average value from a list of items
5
4
  * @param items List of items
@@ -13,7 +12,7 @@ export declare function average<Item extends PlainObject>(items: Item[], callbac
13
12
  * @param key Key to use for value
14
13
  * @returns Average value, or `NaN` if no average can be calculated
15
14
  */
16
- export declare function average<Item extends PlainObject>(items: Item[], key: keyof OnlyNumericalKeys<Item>): number;
15
+ export declare function average<Item extends PlainObject>(items: Item[], key: keyof NumericalValues<Item>): number;
17
16
  /**
18
17
  * Get the average value from a list of numbers
19
18
  * @param numbers List of numbers
@@ -55,7 +54,7 @@ export declare function min<Item extends PlainObject>(items: Item[], callback: (
55
54
  * @param key Key to use for value
56
55
  * @returns Minimum value, or `NaN` if no minimum can be found
57
56
  */
58
- export declare function min<Item extends PlainObject>(items: Item[], key: keyof OnlyNumericalKeys<Item>): number;
57
+ export declare function min<Item extends PlainObject>(items: Item[], key: keyof NumericalValues<Item>): number;
59
58
  /**
60
59
  * Get the minimum value from a list of numbers
61
60
  * @param values List of numbers
@@ -82,11 +81,12 @@ export declare function sum<Item extends PlainObject>(items: Item[], callback: (
82
81
  * @param key Key to use for value
83
82
  * @returns Sum of the values, or `NaN` if no sum can be calculated
84
83
  */
85
- export declare function sum<Item extends PlainObject>(items: Item[], key: keyof OnlyNumericalKeys<Item>): number;
84
+ export declare function sum<Item extends PlainObject>(items: Item[], key: keyof NumericalValues<Item>): number;
86
85
  /**
87
86
  * Get the sum of a list of numbers
88
87
  * @param values List of numbers
89
88
  * @returns Sum of the numbers, or `NaN` if no sum can be calculated
90
89
  */
91
90
  export declare function sum(values: number[]): number;
92
- export { max, type OnlyNumericalKeys } from './internal/math/aggregate';
91
+ export { max } from './internal/math/aggregate';
92
+ export type { NumericalValues };
package/types/models.d.ts CHANGED
@@ -28,11 +28,14 @@ export type KeyedValue<Item, Key extends keyof Item> = Item[Key] extends Propert
28
28
  * A nested array
29
29
  */
30
30
  export type NestedArray<Value> = Value extends Array<infer NestedValue> ? NestedArray<NestedValue> : Value;
31
+ /**
32
+ * All nested keys of an object as dot notation strings _(up to 5 levels deep)_
33
+ */
31
34
  export type NestedKeys<Value extends PlainObject> = _NestedKeys<Value>;
32
35
  type _NestedKeys<Value, Depth extends number = 5> = Depth extends 0 ? never : Value extends readonly any[] ? Value extends readonly [any, ...any] ? {
33
- [Key in keyof Value]-?: Key extends `${number}` ? Value[Key] extends readonly any[] ? `${Key}` | `${Key}.${_NestedKeys<Value[Key], SubtractDepth<Depth>>}` : Value[Key] extends PlainObject ? `${Key}` | `${Key}.${_NestedKeys<Value[Key], SubtractDepth<Depth>>}` : `${Key}` : never;
36
+ [Key in keyof Value]-?: Key extends `${number}` ? NonNullable<Value[Key]> extends readonly any[] | PlainObject ? `${Key}` | `${Key}.${_NestedKeys<NonNullable<Value[Key]>, SubtractDepth<Depth>>}` : `${Key}` : never;
34
37
  }[number] : never : Value extends PlainObject ? {
35
- [Key in keyof Value]-?: Key extends number | string ? Value[Key] extends readonly any[] ? `${Key}` | `${Key}.${_NestedKeys<Value[Key], SubtractDepth<Depth>>}` : Value[Key] extends PlainObject ? `${Key}` | `${Key}.${_NestedKeys<Value[Key], SubtractDepth<Depth>>}` : `${Key}` : never;
38
+ [Key in keyof Value]-?: Key extends number | string ? NonNullable<Value[Key]> extends readonly any[] | PlainObject ? `${Key}` | `${Key}.${_NestedKeys<NonNullable<Value[Key]>, SubtractDepth<Depth>>}` : `${Key}` : never;
36
39
  }[keyof Value] : never;
37
40
  /**
38
41
  * An extended version of `Partial` that allows for nested properties to be optional
@@ -40,11 +43,23 @@ type _NestedKeys<Value, Depth extends number = 5> = Depth extends 0 ? never : Va
40
43
  export type NestedPartial<T> = {
41
44
  [K in keyof T]?: T[K] extends object ? NestedPartial<T[K]> : T[K];
42
45
  };
43
- export type NestedValue<Value extends PlainObject, Path extends string> = Simplify<_NestedValue<Value, Path>>;
44
- export type _NestedValue<Value, Path extends string> = Path extends keyof Value ? Value[Path] : Path extends `${infer Key}.${infer Rest}` ? Key extends keyof Value ? _NestedValue<Value[Key], Rest> : Key extends `${number}` ? Value extends readonly any[] ? _NestedValue<Value[number], Rest> : never : never : Path extends `${number}` ? Value extends readonly any[] ? Value[number] : never : never;
46
+ /**
47
+ * The value for a nested key of an object
48
+ */
49
+ export type NestedValue<Value extends PlainObject, Path extends string> = _NestedValue<Value, Path>;
50
+ type _NestedValue<Value, Path extends string> = Path extends `${infer Key}.${infer Rest}` ? Key extends keyof Value ? undefined extends Value[Key] ? _NestedValue<Exclude<Value[Key], undefined>, Rest> | undefined : _NestedValue<Value[Key], Rest> : Key extends `${number}` ? Value extends readonly any[] ? _NestedValue<Value[number], Rest> : never : never : Path extends `${number}` ? Value extends readonly any[] ? Value[number] : never : Path extends keyof Value ? Value[Path] : never;
51
+ /**
52
+ * The nested (keyed) values of an object _(up to 5 levels deep)_
53
+ */
45
54
  export type NestedValues<Value extends PlainObject> = {
46
55
  [Path in NestedKeys<Value>]: NestedValue<Value, Path>;
47
56
  };
57
+ /**
58
+ * The numerical values of an object
59
+ */
60
+ export type NumericalValues<Item extends PlainObject> = {
61
+ [Key in keyof Item as Item[Key] extends number ? Key : never]: Item[Key];
62
+ };
48
63
  /**
49
64
  * A generic object
50
65
  */