@zajno/common 2.8.1 → 2.8.2

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/cjs/lazy/lazy.js CHANGED
@@ -2,6 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Lazy = void 0;
4
4
  const disposer_js_1 = require("../functions/disposer.js");
5
+ /**
6
+ * Synchronous lazy-loading container that initializes a value on first access.
7
+ * The value is cached until reset or expired. Supports custom disposal and cache expiration.
8
+ */
5
9
  class Lazy {
6
10
  _factory;
7
11
  _instance = undefined;
@@ -31,18 +35,22 @@ class Lazy {
31
35
  }
32
36
  return true;
33
37
  }
38
+ /** Provides custom cleanup logic when the instance is reset or disposed. */
34
39
  withDisposer(disposer) {
35
40
  this._disposer = disposer;
36
41
  return this;
37
42
  }
43
+ /** Configures automatic cache expiration using an expire tracker. */
38
44
  withExpire(tracker) {
39
45
  this._expireTracker = tracker;
40
46
  return this;
41
47
  }
48
+ /** Eagerly loads the value without accessing it. Useful for preloading. */
42
49
  prewarm() {
43
50
  this.ensureInstance();
44
51
  return this;
45
52
  }
53
+ /** Manually sets the cached value. */
46
54
  setInstance(instance) {
47
55
  this._instance = instance;
48
56
  if (this._instance !== undefined && this._expireTracker) {
@@ -1 +1 @@
1
- {"version":3,"file":"lazy.js","sourceRoot":"","sources":["../../../src/lazy/lazy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lazy.js","sourceRoot":"","sources":["../../../src/lazy/lazy.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LazyPromise = void 0;
4
4
  const disposer_js_1 = require("../functions/disposer.js");
5
+ /**
6
+ * Asynchronous lazy-loading container that initializes via a promise-based factory.
7
+ * Handles concurrent operations with "latest wins" semantics: multiple refreshes are automatically
8
+ * coordinated so all awaiting promises receive the final value. Supports extensions for custom behavior.
9
+ */
5
10
  class LazyPromise {
6
11
  _factory;
7
12
  _initial;
@@ -9,8 +14,10 @@ class LazyPromise {
9
14
  _isLoading = null;
10
15
  _promise;
11
16
  _expireTracker;
12
- _lastRefreshingPromise = null;
17
+ // Track the active factory promise to determine "latest wins"
18
+ _activeFactoryPromise = null;
13
19
  _error = null;
20
+ _ownDisposer;
14
21
  constructor(factory, initial) {
15
22
  this._factory = factory;
16
23
  this._initial = initial;
@@ -27,99 +34,72 @@ class LazyPromise {
27
34
  this.ensureInstanceLoading();
28
35
  return this._instance;
29
36
  }
30
- /** does not calls factory */
37
+ /** Returns current value without triggering loading. */
31
38
  get currentValue() {
32
39
  return this._instance;
33
40
  }
41
+ /** Configures automatic cache expiration using an expire tracker. */
34
42
  withExpire(tracker) {
35
43
  this._expireTracker = tracker;
36
44
  return this;
37
45
  }
38
46
  /**
39
- * Extends this instance with additional functionality by applying extensions in place.
47
+ * Extends this instance with additional functionality via in-place mutation.
40
48
  *
41
49
  * **Capabilities:**
42
- * - `overrideFactory`: Wrap the factory function (logging, retry, caching, etc.)
43
- * - `extendShape`: Add custom properties/methods to the instance
50
+ * - `overrideFactory`: Wrap the factory (logging, retry, caching, etc.)
51
+ * - `extendShape`: Add custom properties/methods
52
+ * - `dispose`: Cleanup resources when disposed
44
53
  *
45
54
  * **Type Safety:**
46
55
  * - Use `ILazyPromiseExtension<any>` for universal extensions
47
- * - Use `ILazyPromiseExtension<ConcreteType>` for type-specific extensions (e.g., number-only)
56
+ * - Use `ILazyPromiseExtension<ConcreteType>` for type-specific extensions
48
57
  *
49
- * **Note:** Extensions mutate the instance and can be chained. Subclasses preserve their type.
58
+ * **Note:** Extensions mutate the instance and can be chained.
50
59
  *
51
- * @param extension - Configuration with factory override and/or shape extensions
52
- * @returns The same instance (this) with applied extensions
60
+ * @param extension - Extension configuration
61
+ * @returns The same instance with applied extensions
53
62
  *
54
63
  * @example
55
64
  * ```typescript
56
- * // Universal logging extension
57
65
  * const logged = lazy.extend({
58
66
  * overrideFactory: (factory) => async (refreshing) => {
59
67
  * console.log('Loading...');
60
68
  * return await factory(refreshing);
61
69
  * }
62
70
  * });
63
- *
64
- * // Type-specific extension with custom methods
65
- * const enhanced = lazyNumber.extend<{ double: () => number | undefined }>({
66
- * extendShape: (instance) => Object.assign(instance, {
67
- * double: () => instance.currentValue !== undefined
68
- * ? instance.currentValue * 2
69
- * : undefined
70
- * })
71
- * });
72
- *
73
- * // Chaining multiple extensions
74
- * const composed = lazy
75
- * .extend(cacheExtension)
76
- * .extend(loggingExtension);
77
71
  * ```
78
72
  */
79
73
  extend(
80
74
  // Partial allows extensions with extra properties beyond the interface
81
75
  // 'any' type parameter doesn't affect return type since we return 'this'
82
76
  extension) {
83
- // Override the factory if provided
84
- if (extension.overrideFactory) {
85
- this._factory = extension.overrideFactory(this._factory, this);
86
- }
77
+ let extended = this;
87
78
  // Apply shape extension if provided
88
79
  if (extension.extendShape) {
89
- return extension.extendShape(this);
80
+ extended = extension.extendShape(this);
90
81
  }
91
- return this;
92
- }
93
- ensureInstanceLoading() {
94
- if (this.isLoading === false && this._instance !== undefined && this._expireTracker?.isExpired) {
95
- // do not reset the instance, just make sure it will be reloaded
96
- this._isLoading = null;
82
+ // Override the factory if provided
83
+ if (extension.overrideFactory) {
84
+ this._factory = extension.overrideFactory(this._factory, extended);
97
85
  }
98
- if (this._isLoading === null) {
99
- this._isLoading = true;
100
- this.doLoad();
86
+ if (extension.dispose) {
87
+ const previousDisposer = this._ownDisposer;
88
+ const nextDisposer = extension.dispose;
89
+ this._ownDisposer = () => {
90
+ nextDisposer(extended);
91
+ previousDisposer?.();
92
+ };
101
93
  }
94
+ return extended;
102
95
  }
103
- doLoad() {
104
- this._promise = this._factory(false)
105
- .then(this.onResolved.bind(this))
106
- .catch(this.onRejected.bind(this));
107
- }
108
- onResolved(res) {
109
- // case: during the promise `setInstance` was called
110
- if (!this._isLoading && this._instance !== undefined) {
111
- return this._instance;
112
- }
113
- this.setInstance(res);
114
- return res;
115
- }
116
- onRejected(e) {
117
- this._isLoading = false;
118
- this._instance = this._initial;
119
- this._promise = Promise.resolve(this._initial);
120
- this.setError(e);
121
- return this._initial;
122
- }
96
+ /**
97
+ * Manually sets the value and marks loading as complete.
98
+ * Clears any errors and restarts the expiration tracker if configured.
99
+ *
100
+ * @param res - The value to set
101
+ * @returns The value that was set
102
+ */
123
103
  setInstance(res) {
124
104
  this._isLoading = false;
125
105
  this.clearError(); // clear error on successful set
@@ -127,41 +107,24 @@ class LazyPromise {
127
107
  // + make sure it's resolved with the freshest value
128
108
  // also do this before setting the instance... just in case :)
129
109
  this._promise = Promise.resolve(res);
110
+ this._activeFactoryPromise = null;
130
111
  this._instance = res;
131
- if (this._expireTracker) {
132
- this._expireTracker.restart();
133
- }
112
+ this._expireTracker?.restart();
134
113
  return res;
135
114
  }
136
- setError(err) {
137
- this._error = this.parseError(err);
138
- }
139
- clearError() {
140
- this._error = null;
141
- }
115
+ /**
116
+ * Re-executes the factory to get fresh data.
117
+ *
118
+ * **Concurrency handling:**
119
+ * - Supersedes any in-progress load or refresh
120
+ * - Multiple concurrent refreshes: latest wins
121
+ * - All awaiting promises receive the final refreshed value
122
+ *
123
+ * @returns Promise resolving to the refreshed value
124
+ */
142
125
  async refresh() {
143
- let myPromise = null;
144
- try {
145
- myPromise = this._factory(true);
146
- // every new refresh overrides the previous one
147
- // so this one becomes "last"
148
- // and previous becomes stale and won't update the value when it resolves
149
- this._lastRefreshingPromise = myPromise;
150
- const fresh = await myPromise;
151
- if (this._lastRefreshingPromise === myPromise) {
152
- this.setInstance(fresh);
153
- }
154
- return fresh;
155
- }
156
- catch (e) {
157
- this.setError(e);
158
- return this._instance;
159
- }
160
- finally {
161
- if (myPromise != null && this._lastRefreshingPromise === myPromise) {
162
- this._lastRefreshingPromise = null;
163
- }
164
- }
126
+ this.startLoading(true);
127
+ return this._promise;
165
128
  }
166
129
  reset() {
167
130
  this._isLoading = null;
@@ -170,6 +133,7 @@ class LazyPromise {
170
133
  this._instance = this._initial;
171
134
  const p = this._promise;
172
135
  this._promise = undefined;
136
+ this._activeFactoryPromise = null; // Clear active promise reference
173
137
  // check if loading is still in progress
174
138
  // need to dispose abandoned value
175
139
  if (p && !wasDisposed) {
@@ -179,8 +143,73 @@ class LazyPromise {
179
143
  }
180
144
  }
181
145
  dispose() {
146
+ this._ownDisposer?.();
182
147
  this.reset();
183
148
  }
149
+ ensureInstanceLoading() {
150
+ if (this.isLoading === false && this._instance !== undefined && this._expireTracker?.isExpired) {
151
+ // do not reset the instance, just make sure it will be reloaded
152
+ this._isLoading = null;
153
+ }
154
+ if (this._isLoading === null) {
155
+ this._isLoading = true;
156
+ this.startLoading(false);
157
+ }
158
+ }
159
+ startLoading(refreshing) {
160
+ if (!refreshing && this._activeFactoryPromise) {
161
+ // Case when refreshing already is happening - we have an active promise
162
+ return;
163
+ }
164
+ const factoryPromise = this._factory(refreshing)
165
+ .then(res => {
166
+ if (!this._activeFactoryPromise) {
167
+ // this promise was abandoned: was superseded or reset called
168
+ return this._instance ?? this._initial;
169
+ }
170
+ if (this._activeFactoryPromise === factoryPromise) {
171
+ // case: during the promise `setInstance` was called manually
172
+ if (!refreshing && !this._isLoading && this._instance !== undefined) {
173
+ return this._instance;
174
+ }
175
+ this.setInstance(res);
176
+ return res;
177
+ }
178
+ // Stale promise - return the latest active promise instead
179
+ // This ensures anyone awaiting this old promise gets the fresh value
180
+ return this._activeFactoryPromise;
181
+ })
182
+ .catch(err => {
183
+ if (!this._activeFactoryPromise || this._activeFactoryPromise === factoryPromise) {
184
+ return this.onRejected(err);
185
+ }
186
+ throw err;
187
+ });
188
+ const hadActive = !!this._activeFactoryPromise;
189
+ // This is now the active promise - any previous one is superseded
190
+ this._activeFactoryPromise = factoryPromise;
191
+ // don't overwrite an existing promise (e.g., from refresh)
192
+ // it should pick up the new active promise automatically
193
+ if (!this._promise || !hadActive) {
194
+ this._promise = factoryPromise;
195
+ }
196
+ }
197
+ onRejected(e) {
198
+ this._isLoading = false;
199
+ // Keep the current instance on error (don't reset to initial)
200
+ // This allows retaining the last successful value
201
+ const currentInstance = this._instance !== undefined ? this._instance : this._initial;
202
+ this._promise = Promise.resolve(currentInstance);
203
+ this._activeFactoryPromise = null;
204
+ this.setError(e);
205
+ return currentInstance;
206
+ }
207
+ setError(err) {
208
+ this._error = this.parseError(err);
209
+ }
210
+ clearError() {
211
+ this._error = null;
212
+ }
184
213
  parseError(err) {
185
214
  if (typeof err === 'string') {
186
215
  return err;
@@ -1 +1 @@
1
- {"version":3,"file":"promise.js","sourceRoot":"","sources":["../../../src/lazy/promise.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"promise.js","sourceRoot":"","sources":["../../../src/lazy/promise.ts"],"names":[],"mappings":"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"}
package/esm/lazy/lazy.js CHANGED
@@ -1,4 +1,8 @@
1
1
  import { tryDispose } from '../functions/disposer.js';
2
+ /**
3
+ * Synchronous lazy-loading container that initializes a value on first access.
4
+ * The value is cached until reset or expired. Supports custom disposal and cache expiration.
5
+ */
2
6
  export class Lazy {
3
7
  _factory;
4
8
  _instance = undefined;
@@ -28,18 +32,22 @@ export class Lazy {
28
32
  }
29
33
  return true;
30
34
  }
35
+ /** Provides custom cleanup logic when the instance is reset or disposed. */
31
36
  withDisposer(disposer) {
32
37
  this._disposer = disposer;
33
38
  return this;
34
39
  }
40
+ /** Configures automatic cache expiration using an expire tracker. */
35
41
  withExpire(tracker) {
36
42
  this._expireTracker = tracker;
37
43
  return this;
38
44
  }
45
+ /** Eagerly loads the value without accessing it. Useful for preloading. */
39
46
  prewarm() {
40
47
  this.ensureInstance();
41
48
  return this;
42
49
  }
50
+ /** Manually sets the cached value. */
43
51
  setInstance(instance) {
44
52
  this._instance = instance;
45
53
  if (this._instance !== undefined && this._expireTracker) {
@@ -1 +1 @@
1
- {"version":3,"file":"lazy.js","sourceRoot":"","sources":["../../../src/lazy/lazy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAoB,MAAM,0BAA0B,CAAC;AAKxE,MAAM,OAAO,IAAI;IAOkB;IALrB,SAAS,GAAkB,SAAS,CAAC;IACvC,cAAc,CAA6B;IAC3C,SAAS,CAAqB;IAC9B,MAAM,GAAkB,IAAI,CAAC;IAErC,YAA+B,QAAmB;QAAnB,aAAQ,GAAR,QAAQ,CAAW;IAAI,CAAC;IAEvD,IAAW,QAAQ,KAAK,OAAO,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC;IAE9D,IAAW,KAAK;QACZ,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC,SAAc,CAAC;IAC/B,CAAC;IAED,IAAW,YAAY,KAAK,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACpD,IAAW,KAAK,KAAK,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAE1C,iEAAiE;IACjE,IAAc,OAAO;QACjB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACjB,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,IAAI,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC;gBAChC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACb,OAAO,KAAK,CAAC;YACjB,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,YAAY,CAAC,QAA2B;QAC3C,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;QAC1B,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,UAAU,CAAC,OAAmC;QACjD,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;QAC9B,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,OAAO;QACV,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,WAAW,CAAC,QAAuB;QACtC,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;QAE1B,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACtD,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;QAClC,CAAC;IACL,CAAC;IAEM,KAAK;QACR,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAClC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACnC,CAAC;iBAAM,CAAC;gBACJ,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC/B,CAAC;QACL,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACvB,CAAC;IAEM,OAAO,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAE1B,cAAc;QAClB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,OAAO;QACX,CAAC;QAED,oEAAoE;QACpE,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,IAAI,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC5B,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,CAAU,EAAE,CAAC;YAClB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACrC,CAAC;IACL,CAAC;IAES,UAAU,CAAC,GAAY;QAC7B,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,GAAG,CAAC;QACf,CAAC;QACD,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;YACvB,OAAO,GAAG,CAAC,OAAO,CAAC;QACvB,CAAC;QACD,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC;IAC1C,CAAC;CACJ"}
1
+ {"version":3,"file":"lazy.js","sourceRoot":"","sources":["../../../src/lazy/lazy.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,9 @@
1
1
  import { tryDispose } from '../functions/disposer.js';
2
+ /**
3
+ * Asynchronous lazy-loading container that initializes via a promise-based factory.
4
+ * Handles concurrent operations with "latest wins" semantics: multiple refreshes are automatically
5
+ * coordinated so all awaiting promises receive the final value. Supports extensions for custom behavior.
6
+ */
2
7
  export class LazyPromise {
3
8
  _factory;
4
9
  _initial;
@@ -6,8 +11,10 @@ export class LazyPromise {
6
11
  _isLoading = null;
7
12
  _promise;
8
13
  _expireTracker;
9
- _lastRefreshingPromise = null;
14
+ // Track the active factory promise to determine "latest wins"
15
+ _activeFactoryPromise = null;
10
16
  _error = null;
17
+ _ownDisposer;
11
18
  constructor(factory, initial) {
12
19
  this._factory = factory;
13
20
  this._initial = initial;
@@ -24,99 +31,72 @@ export class LazyPromise {
24
31
  this.ensureInstanceLoading();
25
32
  return this._instance;
26
33
  }
27
- /** does not calls factory */
34
+ /** Returns current value without triggering loading. */
28
35
  get currentValue() {
29
36
  return this._instance;
30
37
  }
38
+ /** Configures automatic cache expiration using an expire tracker. */
31
39
  withExpire(tracker) {
32
40
  this._expireTracker = tracker;
33
41
  return this;
34
42
  }
35
43
  /**
36
- * Extends this instance with additional functionality by applying extensions in place.
44
+ * Extends this instance with additional functionality via in-place mutation.
37
45
  *
38
46
  * **Capabilities:**
39
- * - `overrideFactory`: Wrap the factory function (logging, retry, caching, etc.)
40
- * - `extendShape`: Add custom properties/methods to the instance
47
+ * - `overrideFactory`: Wrap the factory (logging, retry, caching, etc.)
48
+ * - `extendShape`: Add custom properties/methods
49
+ * - `dispose`: Cleanup resources when disposed
41
50
  *
42
51
  * **Type Safety:**
43
52
  * - Use `ILazyPromiseExtension<any>` for universal extensions
44
- * - Use `ILazyPromiseExtension<ConcreteType>` for type-specific extensions (e.g., number-only)
53
+ * - Use `ILazyPromiseExtension<ConcreteType>` for type-specific extensions
45
54
  *
46
- * **Note:** Extensions mutate the instance and can be chained. Subclasses preserve their type.
55
+ * **Note:** Extensions mutate the instance and can be chained.
47
56
  *
48
- * @param extension - Configuration with factory override and/or shape extensions
49
- * @returns The same instance (this) with applied extensions
57
+ * @param extension - Extension configuration
58
+ * @returns The same instance with applied extensions
50
59
  *
51
60
  * @example
52
61
  * ```typescript
53
- * // Universal logging extension
54
62
  * const logged = lazy.extend({
55
63
  * overrideFactory: (factory) => async (refreshing) => {
56
64
  * console.log('Loading...');
57
65
  * return await factory(refreshing);
58
66
  * }
59
67
  * });
60
- *
61
- * // Type-specific extension with custom methods
62
- * const enhanced = lazyNumber.extend<{ double: () => number | undefined }>({
63
- * extendShape: (instance) => Object.assign(instance, {
64
- * double: () => instance.currentValue !== undefined
65
- * ? instance.currentValue * 2
66
- * : undefined
67
- * })
68
- * });
69
- *
70
- * // Chaining multiple extensions
71
- * const composed = lazy
72
- * .extend(cacheExtension)
73
- * .extend(loggingExtension);
74
68
  * ```
75
69
  */
76
70
  extend(
77
71
  // Partial allows extensions with extra properties beyond the interface
78
72
  // 'any' type parameter doesn't affect return type since we return 'this'
79
73
  extension) {
80
- // Override the factory if provided
81
- if (extension.overrideFactory) {
82
- this._factory = extension.overrideFactory(this._factory, this);
83
- }
74
+ let extended = this;
84
75
  // Apply shape extension if provided
85
76
  if (extension.extendShape) {
86
- return extension.extendShape(this);
77
+ extended = extension.extendShape(this);
87
78
  }
88
- return this;
89
- }
90
- ensureInstanceLoading() {
91
- if (this.isLoading === false && this._instance !== undefined && this._expireTracker?.isExpired) {
92
- // do not reset the instance, just make sure it will be reloaded
93
- this._isLoading = null;
79
+ // Override the factory if provided
80
+ if (extension.overrideFactory) {
81
+ this._factory = extension.overrideFactory(this._factory, extended);
94
82
  }
95
- if (this._isLoading === null) {
96
- this._isLoading = true;
97
- this.doLoad();
83
+ if (extension.dispose) {
84
+ const previousDisposer = this._ownDisposer;
85
+ const nextDisposer = extension.dispose;
86
+ this._ownDisposer = () => {
87
+ nextDisposer(extended);
88
+ previousDisposer?.();
89
+ };
98
90
  }
91
+ return extended;
99
92
  }
100
- doLoad() {
101
- this._promise = this._factory(false)
102
- .then(this.onResolved.bind(this))
103
- .catch(this.onRejected.bind(this));
104
- }
105
- onResolved(res) {
106
- // case: during the promise `setInstance` was called
107
- if (!this._isLoading && this._instance !== undefined) {
108
- return this._instance;
109
- }
110
- this.setInstance(res);
111
- return res;
112
- }
113
- onRejected(e) {
114
- this._isLoading = false;
115
- this._instance = this._initial;
116
- this._promise = Promise.resolve(this._initial);
117
- this.setError(e);
118
- return this._initial;
119
- }
93
+ /**
94
+ * Manually sets the value and marks loading as complete.
95
+ * Clears any errors and restarts the expiration tracker if configured.
96
+ *
97
+ * @param res - The value to set
98
+ * @returns The value that was set
99
+ */
120
100
  setInstance(res) {
121
101
  this._isLoading = false;
122
102
  this.clearError(); // clear error on successful set
@@ -124,41 +104,24 @@ export class LazyPromise {
124
104
  // + make sure it's resolved with the freshest value
125
105
  // also do this before setting the instance... just in case :)
126
106
  this._promise = Promise.resolve(res);
107
+ this._activeFactoryPromise = null;
127
108
  this._instance = res;
128
- if (this._expireTracker) {
129
- this._expireTracker.restart();
130
- }
109
+ this._expireTracker?.restart();
131
110
  return res;
132
111
  }
133
- setError(err) {
134
- this._error = this.parseError(err);
135
- }
136
- clearError() {
137
- this._error = null;
138
- }
112
+ /**
113
+ * Re-executes the factory to get fresh data.
114
+ *
115
+ * **Concurrency handling:**
116
+ * - Supersedes any in-progress load or refresh
117
+ * - Multiple concurrent refreshes: latest wins
118
+ * - All awaiting promises receive the final refreshed value
119
+ *
120
+ * @returns Promise resolving to the refreshed value
121
+ */
139
122
  async refresh() {
140
- let myPromise = null;
141
- try {
142
- myPromise = this._factory(true);
143
- // every new refresh overrides the previous one
144
- // so this one becomes "last"
145
- // and previous becomes stale and won't update the value when it resolves
146
- this._lastRefreshingPromise = myPromise;
147
- const fresh = await myPromise;
148
- if (this._lastRefreshingPromise === myPromise) {
149
- this.setInstance(fresh);
150
- }
151
- return fresh;
152
- }
153
- catch (e) {
154
- this.setError(e);
155
- return this._instance;
156
- }
157
- finally {
158
- if (myPromise != null && this._lastRefreshingPromise === myPromise) {
159
- this._lastRefreshingPromise = null;
160
- }
161
- }
123
+ this.startLoading(true);
124
+ return this._promise;
162
125
  }
163
126
  reset() {
164
127
  this._isLoading = null;
@@ -167,6 +130,7 @@ export class LazyPromise {
167
130
  this._instance = this._initial;
168
131
  const p = this._promise;
169
132
  this._promise = undefined;
133
+ this._activeFactoryPromise = null; // Clear active promise reference
170
134
  // check if loading is still in progress
171
135
  // need to dispose abandoned value
172
136
  if (p && !wasDisposed) {
@@ -176,8 +140,73 @@ export class LazyPromise {
176
140
  }
177
141
  }
178
142
  dispose() {
143
+ this._ownDisposer?.();
179
144
  this.reset();
180
145
  }
146
+ ensureInstanceLoading() {
147
+ if (this.isLoading === false && this._instance !== undefined && this._expireTracker?.isExpired) {
148
+ // do not reset the instance, just make sure it will be reloaded
149
+ this._isLoading = null;
150
+ }
151
+ if (this._isLoading === null) {
152
+ this._isLoading = true;
153
+ this.startLoading(false);
154
+ }
155
+ }
156
+ startLoading(refreshing) {
157
+ if (!refreshing && this._activeFactoryPromise) {
158
+ // Case when refreshing already is happening - we have an active promise
159
+ return;
160
+ }
161
+ const factoryPromise = this._factory(refreshing)
162
+ .then(res => {
163
+ if (!this._activeFactoryPromise) {
164
+ // this promise was abandoned: was superseded or reset called
165
+ return this._instance ?? this._initial;
166
+ }
167
+ if (this._activeFactoryPromise === factoryPromise) {
168
+ // case: during the promise `setInstance` was called manually
169
+ if (!refreshing && !this._isLoading && this._instance !== undefined) {
170
+ return this._instance;
171
+ }
172
+ this.setInstance(res);
173
+ return res;
174
+ }
175
+ // Stale promise - return the latest active promise instead
176
+ // This ensures anyone awaiting this old promise gets the fresh value
177
+ return this._activeFactoryPromise;
178
+ })
179
+ .catch(err => {
180
+ if (!this._activeFactoryPromise || this._activeFactoryPromise === factoryPromise) {
181
+ return this.onRejected(err);
182
+ }
183
+ throw err;
184
+ });
185
+ const hadActive = !!this._activeFactoryPromise;
186
+ // This is now the active promise - any previous one is superseded
187
+ this._activeFactoryPromise = factoryPromise;
188
+ // don't overwrite an existing promise (e.g., from refresh)
189
+ // it should pick up the new active promise automatically
190
+ if (!this._promise || !hadActive) {
191
+ this._promise = factoryPromise;
192
+ }
193
+ }
194
+ onRejected(e) {
195
+ this._isLoading = false;
196
+ // Keep the current instance on error (don't reset to initial)
197
+ // This allows retaining the last successful value
198
+ const currentInstance = this._instance !== undefined ? this._instance : this._initial;
199
+ this._promise = Promise.resolve(currentInstance);
200
+ this._activeFactoryPromise = null;
201
+ this.setError(e);
202
+ return currentInstance;
203
+ }
204
+ setError(err) {
205
+ this._error = this.parseError(err);
206
+ }
207
+ clearError() {
208
+ this._error = null;
209
+ }
181
210
  parseError(err) {
182
211
  if (typeof err === 'string') {
183
212
  return err;
@@ -1 +1 @@
1
- {"version":3,"file":"promise.js","sourceRoot":"","sources":["../../../src/lazy/promise.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"promise.js","sourceRoot":"","sources":["../../../src/lazy/promise.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zajno/common",
3
- "version": "2.8.1",
3
+ "version": "2.8.2",
4
4
  "description": "Zajno's re-usable utilities for JS/TS projects",
5
5
  "private": false,
6
6
  "type": "module",
@@ -2,6 +2,10 @@ import { type IDisposable } from '../functions/disposer.js';
2
2
  import type { IResettableModel } from '../models/types.js';
3
3
  import type { IExpireTracker } from '../structures/expire.js';
4
4
  import type { ILazy } from './types.js';
5
+ /**
6
+ * Synchronous lazy-loading container that initializes a value on first access.
7
+ * The value is cached until reset or expired. Supports custom disposal and cache expiration.
8
+ */
5
9
  export declare class Lazy<T> implements ILazy<T>, IDisposable, IResettableModel {
6
10
  protected readonly _factory: (() => T);
7
11
  protected _instance: T | undefined;
@@ -15,9 +19,13 @@ export declare class Lazy<T> implements ILazy<T>, IDisposable, IResettableModel
15
19
  get error(): string | null;
16
20
  /** Override me: additional way to make sure instance is valid */
17
21
  protected get isValid(): boolean;
22
+ /** Provides custom cleanup logic when the instance is reset or disposed. */
18
23
  withDisposer(disposer: (prev: T) => void): this;
24
+ /** Configures automatic cache expiration using an expire tracker. */
19
25
  withExpire(tracker: IExpireTracker | undefined): this;
26
+ /** Eagerly loads the value without accessing it. Useful for preloading. */
20
27
  prewarm(): this;
28
+ /** Manually sets the cached value. */
21
29
  setInstance(instance: T | undefined): void;
22
30
  reset(): void;
23
31
  dispose(): void;
@@ -2,6 +2,11 @@ import { type IDisposable } from '../functions/disposer.js';
2
2
  import type { IResettableModel } from '../models/types.js';
3
3
  import type { IExpireTracker } from '../structures/expire.js';
4
4
  import type { IControllableLazyPromise, ILazyPromiseExtension, LazyFactory } from './types.js';
5
+ /**
6
+ * Asynchronous lazy-loading container that initializes via a promise-based factory.
7
+ * Handles concurrent operations with "latest wins" semantics: multiple refreshes are automatically
8
+ * coordinated so all awaiting promises receive the final value. Supports extensions for custom behavior.
9
+ */
5
10
  export declare class LazyPromise<T, TInitial extends T | undefined = undefined> implements IControllableLazyPromise<T, TInitial>, IDisposable, IResettableModel {
6
11
  private _factory;
7
12
  private readonly _initial;
@@ -9,68 +14,72 @@ export declare class LazyPromise<T, TInitial extends T | undefined = undefined>
9
14
  private _isLoading;
10
15
  private _promise;
11
16
  private _expireTracker;
12
- private _lastRefreshingPromise;
17
+ private _activeFactoryPromise;
13
18
  private _error;
19
+ private _ownDisposer?;
14
20
  constructor(factory: LazyFactory<T>, initial?: TInitial);
15
21
  get isLoading(): boolean | null;
16
22
  get hasValue(): boolean;
17
23
  get error(): string | null;
18
24
  get promise(): Promise<T>;
19
25
  get value(): T | TInitial;
20
- /** does not calls factory */
26
+ /** Returns current value without triggering loading. */
21
27
  get currentValue(): T | TInitial;
28
+ /** Configures automatic cache expiration using an expire tracker. */
22
29
  withExpire(tracker: IExpireTracker | undefined): this;
23
30
  /**
24
- * Extends this instance with additional functionality by applying extensions in place.
31
+ * Extends this instance with additional functionality via in-place mutation.
25
32
  *
26
33
  * **Capabilities:**
27
- * - `overrideFactory`: Wrap the factory function (logging, retry, caching, etc.)
28
- * - `extendShape`: Add custom properties/methods to the instance
34
+ * - `overrideFactory`: Wrap the factory (logging, retry, caching, etc.)
35
+ * - `extendShape`: Add custom properties/methods
36
+ * - `dispose`: Cleanup resources when disposed
29
37
  *
30
38
  * **Type Safety:**
31
39
  * - Use `ILazyPromiseExtension<any>` for universal extensions
32
- * - Use `ILazyPromiseExtension<ConcreteType>` for type-specific extensions (e.g., number-only)
40
+ * - Use `ILazyPromiseExtension<ConcreteType>` for type-specific extensions
33
41
  *
34
- * **Note:** Extensions mutate the instance and can be chained. Subclasses preserve their type.
42
+ * **Note:** Extensions mutate the instance and can be chained.
35
43
  *
36
- * @param extension - Configuration with factory override and/or shape extensions
37
- * @returns The same instance (this) with applied extensions
44
+ * @param extension - Extension configuration
45
+ * @returns The same instance with applied extensions
38
46
  *
39
47
  * @example
40
48
  * ```typescript
41
- * // Universal logging extension
42
49
  * const logged = lazy.extend({
43
50
  * overrideFactory: (factory) => async (refreshing) => {
44
51
  * console.log('Loading...');
45
52
  * return await factory(refreshing);
46
53
  * }
47
54
  * });
48
- *
49
- * // Type-specific extension with custom methods
50
- * const enhanced = lazyNumber.extend<{ double: () => number | undefined }>({
51
- * extendShape: (instance) => Object.assign(instance, {
52
- * double: () => instance.currentValue !== undefined
53
- * ? instance.currentValue * 2
54
- * : undefined
55
- * })
56
- * });
57
- *
58
- * // Chaining multiple extensions
59
- * const composed = lazy
60
- * .extend(cacheExtension)
61
- * .extend(loggingExtension);
62
55
  * ```
63
56
  */
64
57
  extend<TExtShape extends object = object>(extension: Partial<ILazyPromiseExtension<any, TExtShape>>): object extends TExtShape ? this : this & TExtShape;
65
- protected ensureInstanceLoading(): void;
66
- protected doLoad(): void;
67
- protected onResolved(res: T): T;
68
- protected onRejected(e: unknown): T;
58
+ /**
59
+ * Manually sets the value and marks loading as complete.
60
+ * Clears any errors and restarts the expiration tracker if configured.
61
+ *
62
+ * @param res - The value to set
63
+ * @returns The value that was set
64
+ */
69
65
  setInstance(res: T): T;
70
- protected setError(err: unknown): void;
71
- protected clearError(): void;
66
+ /**
67
+ * Re-executes the factory to get fresh data.
68
+ *
69
+ * **Concurrency handling:**
70
+ * - Supersedes any in-progress load or refresh
71
+ * - Multiple concurrent refreshes: latest wins
72
+ * - All awaiting promises receive the final refreshed value
73
+ *
74
+ * @returns Promise resolving to the refreshed value
75
+ */
72
76
  refresh(): Promise<T>;
73
77
  reset(): void;
74
78
  dispose(): void;
79
+ protected ensureInstanceLoading(): void;
80
+ private startLoading;
81
+ protected onRejected(e: unknown): T | TInitial;
82
+ protected setError(err: unknown): void;
83
+ protected clearError(): void;
75
84
  protected parseError(err: unknown): string;
76
85
  }
@@ -1,58 +1,54 @@
1
1
  import type { IResettableModel } from '../models/types.js';
2
- /** Represents a lazily loaded value. */
2
+ /** Represents a lazily loaded value that initializes on first access. */
3
3
  export interface ILazy<T> {
4
- /** Returns current value. If not loaded, loading is triggered. */
4
+ /** Returns current value, triggering loading if not yet loaded. */
5
5
  readonly value: T;
6
- /** Returns whether has current value. Accessing this property does not trigger loading. */
6
+ /** Returns true if value has been loaded. Does not trigger loading. */
7
7
  readonly hasValue: boolean;
8
- /** Returns current value or undefined if not present. Accessing this property does not trigger loading. */
8
+ /** Returns current value or undefined if not loaded. Does not trigger loading. */
9
9
  readonly currentValue: T | undefined;
10
- /** Returns error message if loading failed, null otherwise. Accessing this property does not trigger loading. */
10
+ /** Returns error message if loading failed, null otherwise. Does not trigger loading. */
11
11
  readonly error: string | null;
12
12
  }
13
- /** Represents a lazily asynchronously loaded value. */
13
+ /** Represents a lazily asynchronously loaded value with promise-based access. */
14
14
  export interface ILazyPromise<T, TInitial extends T | undefined = undefined> extends ILazy<T | TInitial> {
15
15
  /**
16
- * Returns true if loading is in progress, false if loading is completed, null if loading was not initiated.
17
- *
18
- * Accessing this property does not trigger loading.
16
+ * Returns loading state: true (loading), false (loaded), null (not started).
17
+ * Does not trigger loading.
19
18
  */
20
19
  readonly isLoading: boolean | null;
21
- /** Returns the promise for the current value. If not loaded, accessing this property triggers loading. */
20
+ /** Returns the promise for the value, triggering loading if not started. */
22
21
  readonly promise: Promise<T>;
23
22
  /**
24
- * Refreshes the value by re-executing the factory function.
25
- * The factory will be called with `refreshing: true` parameter.
26
- * If multiple refreshes are called concurrently, only the last one will update the instance.
23
+ * Re-executes the factory to get fresh data. If concurrent refreshes occur, the latest wins.
24
+ * All awaiting promises will resolve to the final refreshed value.
27
25
  *
28
- * **⚠️ Use sparingly:** This method should only be called when you explicitly need fresh data.
29
- * Over-using refresh defeats the purpose of lazy loading and caching.
26
+ * **⚠️ Use sparingly:** Only refresh when explicitly needed for fresh data.
27
+ * Over-use defeats lazy loading and caching benefits.
30
28
  *
31
- * **Common valid use cases:**
32
- * - User-initiated refresh action (pull-to-refresh, refresh button)
33
- * - Cache invalidation after a mutation (e.g., after updating data on server)
34
- * - Time-based refresh with proper debouncing/throttling
35
- * - Recovery from an error state
29
+ * **Valid use cases:**
30
+ * - User-initiated refresh (pull-to-refresh, refresh button)
31
+ * - Cache invalidation after mutation
32
+ * - Time-based refresh with throttling
33
+ * - Error recovery
36
34
  *
37
- * **Anti-patterns to avoid:**
38
- * - Calling refresh on every render or component mount
39
- * - Using refresh instead of proper cache expiration (use `withExpire` instead)
40
- * - Calling refresh in loops or high-frequency events without debouncing
41
- * - Using refresh as a substitute for real-time updates (consider WebSockets/polling instead)
35
+ * **Avoid:**
36
+ * - Refreshing on every render/mount
37
+ * - Using instead of cache expiration (use `withExpire`)
38
+ * - Calling in loops or high-frequency events without debouncing
42
39
  *
43
- * @returns Promise that resolves to the refreshed value, or current value if refresh fails.
40
+ * @returns Promise resolving to the refreshed value
44
41
  */
45
42
  refresh(): Promise<T>;
46
43
  }
47
44
  /**
48
- * Represents a controllable lazy promise with manual state management capabilities.
49
- * Extends ILazyPromise and IResettableModel with methods to manually set values and reset state.
50
- * Use this interface in extensions that need direct control over the lazy value lifecycle.
45
+ * Controllable lazy promise with manual state management.
46
+ * Extends ILazyPromise with methods to manually set values and reset state.
51
47
  */
52
48
  export interface IControllableLazyPromise<T, TInitial extends T | undefined = undefined> extends ILazyPromise<T, TInitial>, IResettableModel {
53
49
  /**
54
- * Manually sets the instance value and marks loading as complete.
55
- * Useful for cache synchronization and manual state management.
50
+ * Manually sets the value and marks loading as complete.
51
+ * Useful for cache synchronization and manual state updates.
56
52
  *
57
53
  * @param value - The value to set
58
54
  * @returns The value that was set
@@ -60,28 +56,20 @@ export interface IControllableLazyPromise<T, TInitial extends T | undefined = un
60
56
  setInstance(value: T): T;
61
57
  }
62
58
  /**
63
- * Factory function to retrieve the fresh value.
59
+ * Factory function that retrieves the value for LazyPromise.
64
60
  *
65
- * @param refreshing - indicates whether manual refresh is requested
66
- * */
61
+ * @param refreshing - True when called via refresh(), false on initial load
62
+ */
67
63
  export type LazyFactory<T> = (refreshing?: boolean) => Promise<T>;
68
64
  /**
69
- * Extension for LazyPromise instances.
65
+ * Extension for LazyPromise instances, enabling factory wrapping and instance augmentation.
70
66
  *
71
- * @template T - The type of value the extension is compatible with. Use `any` for universal extensions.
72
- * @template TExtShape - Additional shape added to the extended instance (properties/methods).
67
+ * @template T - Value type the extension is compatible with (use `any` for universal extensions)
68
+ * @template TExtShape - Additional properties/methods added to the instance
73
69
  *
74
70
  * @example
75
71
  * ```typescript
76
- * // Type-specific extension (only for numbers)
77
- * const doublingExtension: ILazyPromiseExtension<number> = {
78
- * overrideFactory: (original) => async (refreshing) => {
79
- * const result = await original(refreshing);
80
- * return result * 2;
81
- * }
82
- * };
83
- *
84
- * // Universal extension (works with any type)
72
+ * // Universal logging extension
85
73
  * const loggingExtension: ILazyPromiseExtension<any> = {
86
74
  * overrideFactory: (original) => async (refreshing) => {
87
75
  * console.log('Loading...');
@@ -92,19 +80,40 @@ export type LazyFactory<T> = (refreshing?: boolean) => Promise<T>;
92
80
  */
93
81
  export interface ILazyPromiseExtension<T = any, TExtShape extends object = object> {
94
82
  /**
95
- * Override or wrap the factory function.
83
+ * Augment the instance with additional properties/methods.
84
+ * Receives IControllableLazyPromise with setInstance() and reset() for manual control.
85
+ *
86
+ * @param previous - The controllable LazyPromise instance
87
+ * @returns The instance with additional shape
88
+ */
89
+ extendShape?: <TInitial extends T | undefined = undefined>(previous: IControllableLazyPromise<T, TInitial>) => IControllableLazyPromise<T, TInitial> & TExtShape;
90
+ /**
91
+ * Wrap or replace the factory function.
92
+ *
96
93
  * @param original - The original factory function
97
94
  * @param target - The LazyPromise instance being extended
98
95
  * @returns A new factory function
99
96
  */
100
- overrideFactory?: <TInitial extends T | undefined = undefined>(original: LazyFactory<T>, target: ILazyPromise<T, TInitial>) => LazyFactory<T>;
97
+ overrideFactory?: <TInitial extends T | undefined = undefined>(original: LazyFactory<T>, target: ILazyPromise<T, TInitial> & TExtShape) => LazyFactory<T>;
101
98
  /**
102
- * Extend the instance with additional properties/methods.
103
- * Receives IControllableLazyPromise which includes setInstance() and reset() methods
104
- * for manual state management and cache synchronization.
99
+ * Cleanup function called when the LazyPromise is disposed.
100
+ * Use for cleaning up resources (timers, subscriptions, listeners).
101
+ * Executes in reverse order: newest extension first, oldest last.
105
102
  *
106
- * @param previous - The controllable LazyPromise instance to extend
107
- * @returns The instance with additional shape
103
+ * @param instance - The extended LazyPromise instance being disposed
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * const intervalExtension: ILazyPromiseExtension<any, { stopTimer: () => void }> = {
108
+ * extendShape: (instance) => {
109
+ * let intervalId: NodeJS.Timeout | null = null;
110
+ * return Object.assign(instance, {
111
+ * stopTimer: () => { if (intervalId) clearInterval(intervalId); }
112
+ * });
113
+ * },
114
+ * dispose: (instance) => instance.stopTimer()
115
+ * };
116
+ * ```
108
117
  */
109
- extendShape?: <TInitial extends T | undefined = undefined>(previous: IControllableLazyPromise<T, TInitial>) => IControllableLazyPromise<T, TInitial> & TExtShape;
118
+ dispose?: (instance: ILazyPromise<T, any> & TExtShape) => void;
110
119
  }