@twin.org/core 0.9.3-next.1 → 0.9.3-next.11

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.
Files changed (40) hide show
  1. package/dist/es/factories/facadeFactory.js +9 -0
  2. package/dist/es/factories/facadeFactory.js.map +1 -0
  3. package/dist/es/factories/factory.js +137 -6
  4. package/dist/es/factories/factory.js.map +1 -1
  5. package/dist/es/helpers/timeoutHelper.js +46 -0
  6. package/dist/es/helpers/timeoutHelper.js.map +1 -0
  7. package/dist/es/index.js +4 -0
  8. package/dist/es/index.js.map +1 -1
  9. package/dist/es/models/IFacade.js +4 -0
  10. package/dist/es/models/IFacade.js.map +1 -0
  11. package/dist/es/models/IMutexWaiter.js +4 -0
  12. package/dist/es/models/IMutexWaiter.js.map +1 -0
  13. package/dist/es/utils/lfuCache.js +91 -13
  14. package/dist/es/utils/lfuCache.js.map +1 -1
  15. package/dist/es/utils/lruCache.js +83 -10
  16. package/dist/es/utils/lruCache.js.map +1 -1
  17. package/dist/es/utils/mutex.js +170 -23
  18. package/dist/es/utils/mutex.js.map +1 -1
  19. package/dist/types/factories/facadeFactory.d.ts +6 -0
  20. package/dist/types/factories/factory.d.ts +22 -2
  21. package/dist/types/helpers/timeoutHelper.d.ts +17 -0
  22. package/dist/types/index.d.ts +4 -0
  23. package/dist/types/models/IFacade.d.ts +12 -0
  24. package/dist/types/models/IMutexWaiter.d.ts +17 -0
  25. package/dist/types/utils/lfuCache.d.ts +8 -2
  26. package/dist/types/utils/lruCache.d.ts +8 -2
  27. package/dist/types/utils/mutex.d.ts +9 -0
  28. package/docs/changelog.md +170 -0
  29. package/docs/examples.md +38 -1
  30. package/docs/reference/classes/Factory.md +80 -2
  31. package/docs/reference/classes/LfuCache.md +18 -2
  32. package/docs/reference/classes/LruCache.md +18 -2
  33. package/docs/reference/classes/Mutex.md +9 -0
  34. package/docs/reference/classes/TimeoutHelper.md +57 -0
  35. package/docs/reference/index.md +4 -0
  36. package/docs/reference/interfaces/IFacade.md +32 -0
  37. package/docs/reference/interfaces/IMutexWaiter.md +37 -0
  38. package/docs/reference/variables/FacadeFactory.md +5 -0
  39. package/locales/en.json +5 -2
  40. package/package.json +2 -2
@@ -11,6 +11,13 @@
11
11
  * The main thread must call Mutex.handleWorkerMessage(msg) from its worker message handler
12
12
  * before that worker first calls Mutex.lock().
13
13
  *
14
+ * Callers on the same thread are served in the order they arrived. Each key has a FIFO
15
+ * queue of waiters, unlock() hands the lock directly to the waiter at the front, and a new
16
+ * caller only takes the lock outright when that queue is empty. Without this a caller that
17
+ * arrives while a waiter is being woken can take the lock first, which lets a busy key
18
+ * starve a waiter until its timeout elapses. Threads still contend with each other for the
19
+ * shared lock, so the ordering guarantee is per thread rather than global.
20
+ *
14
21
  * The lock is not re-entrant: a thread that already holds a key and calls lock() again on
15
22
  * the same key will block until the timeout elapses.
16
23
  */
@@ -35,6 +42,8 @@ export declare class Mutex {
35
42
  * held, it suspends the current async task until the lock is released or the timeout is reached.
36
43
  * Use this in async single-threaded contexts (e.g. the main thread or a Fastify route handler)
37
44
  * where calling the synchronous lock() would freeze the event loop and deadlock.
45
+ * Callers on the same thread are served in the order they arrived, so a contended key
46
+ * cannot starve an earlier caller.
38
47
  * The lock is not re-entrant: if the same context holds the key and calls lockAsync() again on
39
48
  * the same key, it will suspend until the timeout elapses.
40
49
  * @param key The key to lock on.
package/docs/changelog.md CHANGED
@@ -1,5 +1,175 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.3-next.11](https://github.com/iotaledger/twin-framework/compare/core-v0.9.3-next.10...core-v0.9.3-next.11) (2026-09-10)
4
+
5
+
6
+ ### Features
7
+
8
+ * factory facade exclude types as reg ex ([ae34b54](https://github.com/iotaledger/twin-framework/commit/ae34b544cd6fd2618784ecece5a7fec6fa833699))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/nameof bumped from 0.9.3-next.10 to 0.9.3-next.11
16
+ * devDependencies
17
+ * @twin.org/nameof-transformer bumped from 0.9.3-next.10 to 0.9.3-next.11
18
+ * @twin.org/nameof-vitest-plugin bumped from 0.9.3-next.10 to 0.9.3-next.11
19
+
20
+ ## [0.9.3-next.10](https://github.com/iotaledger/twin-framework/compare/core-v0.9.3-next.9...core-v0.9.3-next.10) (2026-09-10)
21
+
22
+
23
+ ### Features
24
+
25
+ * exclude instance types from a facade ([#492](https://github.com/iotaledger/twin-framework/issues/492)) ([5962e7a](https://github.com/iotaledger/twin-framework/commit/5962e7a98603a5b6674953cce89c2221dc0281a9))
26
+
27
+
28
+ ### Dependencies
29
+
30
+ * The following workspace dependencies were updated
31
+ * dependencies
32
+ * @twin.org/nameof bumped from 0.9.3-next.9 to 0.9.3-next.10
33
+ * devDependencies
34
+ * @twin.org/nameof-transformer bumped from 0.9.3-next.9 to 0.9.3-next.10
35
+ * @twin.org/nameof-vitest-plugin bumped from 0.9.3-next.9 to 0.9.3-next.10
36
+
37
+ ## [0.9.3-next.9](https://github.com/iotaledger/twin-framework/compare/core-v0.9.3-next.8...core-v0.9.3-next.9) (2026-09-10)
38
+
39
+
40
+ ### Miscellaneous Chores
41
+
42
+ * **core:** Synchronize repo versions
43
+
44
+
45
+ ### Dependencies
46
+
47
+ * The following workspace dependencies were updated
48
+ * dependencies
49
+ * @twin.org/nameof bumped from 0.9.3-next.8 to 0.9.3-next.9
50
+ * devDependencies
51
+ * @twin.org/nameof-transformer bumped from 0.9.3-next.8 to 0.9.3-next.9
52
+ * @twin.org/nameof-vitest-plugin bumped from 0.9.3-next.8 to 0.9.3-next.9
53
+
54
+ ## [0.9.3-next.8](https://github.com/iotaledger/twin-framework/compare/core-v0.9.3-next.7...core-v0.9.3-next.8) (2026-09-07)
55
+
56
+
57
+ ### Miscellaneous Chores
58
+
59
+ * **core:** Synchronize repo versions
60
+
61
+
62
+ ### Dependencies
63
+
64
+ * The following workspace dependencies were updated
65
+ * dependencies
66
+ * @twin.org/nameof bumped from 0.9.3-next.7 to 0.9.3-next.8
67
+ * devDependencies
68
+ * @twin.org/nameof-transformer bumped from 0.9.3-next.7 to 0.9.3-next.8
69
+ * @twin.org/nameof-vitest-plugin bumped from 0.9.3-next.7 to 0.9.3-next.8
70
+
71
+ ## [0.9.3-next.7](https://github.com/iotaledger/twin-framework/compare/core-v0.9.3-next.6...core-v0.9.3-next.7) (2026-09-07)
72
+
73
+
74
+ ### Features
75
+
76
+ * timeout helper callback ([cb7a8d1](https://github.com/iotaledger/twin-framework/commit/cb7a8d1d3fb94a7e2148e704b23ece3c315ebb23))
77
+
78
+
79
+ ### Dependencies
80
+
81
+ * The following workspace dependencies were updated
82
+ * dependencies
83
+ * @twin.org/nameof bumped from 0.9.3-next.6 to 0.9.3-next.7
84
+ * devDependencies
85
+ * @twin.org/nameof-transformer bumped from 0.9.3-next.6 to 0.9.3-next.7
86
+ * @twin.org/nameof-vitest-plugin bumped from 0.9.3-next.6 to 0.9.3-next.7
87
+
88
+ ## [0.9.3-next.6](https://github.com/iotaledger/twin-framework/compare/core-v0.9.3-next.5...core-v0.9.3-next.6) (2026-09-07)
89
+
90
+
91
+ ### Features
92
+
93
+ * add TimeoutHelper ([2e0c50d](https://github.com/iotaledger/twin-framework/commit/2e0c50d70708f3a99e9e94ebad4b543250a1b987))
94
+
95
+
96
+ ### Dependencies
97
+
98
+ * The following workspace dependencies were updated
99
+ * dependencies
100
+ * @twin.org/nameof bumped from 0.9.3-next.5 to 0.9.3-next.6
101
+ * devDependencies
102
+ * @twin.org/nameof-transformer bumped from 0.9.3-next.5 to 0.9.3-next.6
103
+ * @twin.org/nameof-vitest-plugin bumped from 0.9.3-next.5 to 0.9.3-next.6
104
+
105
+ ## [0.9.3-next.5](https://github.com/iotaledger/twin-framework/compare/core-v0.9.3-next.4...core-v0.9.3-next.5) (2026-09-04)
106
+
107
+
108
+ ### Bug Fixes
109
+
110
+ * mutex ordering ([#476](https://github.com/iotaledger/twin-framework/issues/476)) ([45ed50a](https://github.com/iotaledger/twin-framework/commit/45ed50aab9f40b4d94921926e13deb37bcac8a70))
111
+
112
+
113
+ ### Dependencies
114
+
115
+ * The following workspace dependencies were updated
116
+ * dependencies
117
+ * @twin.org/nameof bumped from 0.9.3-next.4 to 0.9.3-next.5
118
+ * devDependencies
119
+ * @twin.org/nameof-transformer bumped from 0.9.3-next.4 to 0.9.3-next.5
120
+ * @twin.org/nameof-vitest-plugin bumped from 0.9.3-next.4 to 0.9.3-next.5
121
+
122
+ ## [0.9.3-next.4](https://github.com/iotaledger/twin-framework/compare/core-v0.9.3-next.3...core-v0.9.3-next.4) (2026-09-04)
123
+
124
+
125
+ ### Features
126
+
127
+ * cache specific timeout ([#473](https://github.com/iotaledger/twin-framework/issues/473)) ([e4c6fac](https://github.com/iotaledger/twin-framework/commit/e4c6facdd874cf17f02db13932d256bedd55302f))
128
+
129
+
130
+ ### Dependencies
131
+
132
+ * The following workspace dependencies were updated
133
+ * dependencies
134
+ * @twin.org/nameof bumped from 0.9.3-next.3 to 0.9.3-next.4
135
+ * devDependencies
136
+ * @twin.org/nameof-transformer bumped from 0.9.3-next.3 to 0.9.3-next.4
137
+ * @twin.org/nameof-vitest-plugin bumped from 0.9.3-next.3 to 0.9.3-next.4
138
+
139
+ ## [0.9.3-next.3](https://github.com/iotaledger/twin-framework/compare/core-v0.9.3-next.2...core-v0.9.3-next.3) (2026-09-02)
140
+
141
+
142
+ ### Features
143
+
144
+ * factory facades ([#470](https://github.com/iotaledger/twin-framework/issues/470)) ([004aade](https://github.com/iotaledger/twin-framework/commit/004aade8340ff640f24dbe2f05330e8f5920d3a9))
145
+
146
+
147
+ ### Dependencies
148
+
149
+ * The following workspace dependencies were updated
150
+ * dependencies
151
+ * @twin.org/nameof bumped from 0.9.3-next.2 to 0.9.3-next.3
152
+ * devDependencies
153
+ * @twin.org/nameof-transformer bumped from 0.9.3-next.2 to 0.9.3-next.3
154
+ * @twin.org/nameof-vitest-plugin bumped from 0.9.3-next.2 to 0.9.3-next.3
155
+
156
+ ## [0.9.3-next.2](https://github.com/iotaledger/twin-framework/compare/core-v0.9.3-next.1...core-v0.9.3-next.2) (2026-08-28)
157
+
158
+
159
+ ### Features
160
+
161
+ * improve mime type detection ([#467](https://github.com/iotaledger/twin-framework/issues/467)) ([e465a5c](https://github.com/iotaledger/twin-framework/commit/e465a5cf0926f0ba9c779bed14bdbcc409f478d5))
162
+
163
+
164
+ ### Dependencies
165
+
166
+ * The following workspace dependencies were updated
167
+ * dependencies
168
+ * @twin.org/nameof bumped from 0.9.3-next.1 to 0.9.3-next.2
169
+ * devDependencies
170
+ * @twin.org/nameof-transformer bumped from 0.9.3-next.1 to 0.9.3-next.2
171
+ * @twin.org/nameof-vitest-plugin bumped from 0.9.3-next.1 to 0.9.3-next.2
172
+
3
173
  ## [0.9.3-next.1](https://github.com/iotaledger/twin-framework/compare/core-v0.9.3-next.0...core-v0.9.3-next.1) (2026-08-26)
4
174
 
5
175
 
package/docs/examples.md CHANGED
@@ -171,12 +171,49 @@ class SimpleHasher implements IHasher {
171
171
  }
172
172
  }
173
173
 
174
- const factory = new Factory<IHasher>('hashers');
174
+ const factory = Factory.createFactory<IHasher>('hashers');
175
175
  factory.register('simple', () => new SimpleHasher());
176
176
 
177
177
  factory.create('simple').hash('abc'); // 'abc-hash'
178
178
  ```
179
179
 
180
+ ## Factory Facades
181
+
182
+ A facade wraps the components a factory produces, so a cross cutting concern can be applied without modifying them.
183
+
184
+ ```typescript
185
+ import { ComponentFactory, FacadeFactory, Is, type IFacade } from '@twin.org/core';
186
+
187
+ class LoggingFacade implements IFacade {
188
+ public wrap(target: unknown): unknown {
189
+ return new Proxy(target as object, {
190
+ get(t, prop, receiver): unknown {
191
+ const value = Reflect.get(t, prop, receiver);
192
+
193
+ if (!Is.function(value)) {
194
+ return value;
195
+ }
196
+
197
+ return (...args: unknown[]): unknown => {
198
+ globalThis.console.log('calling', String(prop));
199
+ return value.apply(t, args);
200
+ };
201
+ }
202
+ });
203
+ }
204
+ }
205
+
206
+ FacadeFactory.register('logging', () => new LoggingFacade());
207
+
208
+ // Every component the factory produces from here on is wrapped.
209
+ ComponentFactory.useFacade('logging');
210
+
211
+ // No longer wrapped, for instances produced after this call.
212
+ ComponentFactory.unuseFacade('logging');
213
+ ```
214
+
215
+ An instance is passed through the facades in the order they were activated, so the first activated is the outermost.
216
+
180
217
  ## Encoding and Compression
181
218
 
182
219
  ```typescript
@@ -366,12 +366,82 @@ Remove all the instances and the generators.
366
366
 
367
367
  ***
368
368
 
369
+ ### useFacade() {#usefacade}
370
+
371
+ > **useFacade**(`name`, `excludeTypes?`): `void`
372
+
373
+ Activate a facade for this factory, so every instance it produces is wrapped by it.
374
+ An instance is passed through the facades in the order they were activated, so the facade
375
+ activated first is the outermost. Activating a facade which is already active does nothing.
376
+
377
+ #### Parameters
378
+
379
+ ##### name
380
+
381
+ `string`
382
+
383
+ The name of the facade, as registered with the facade factory.
384
+
385
+ ##### excludeTypes?
386
+
387
+ `RegExp`[]
388
+
389
+ Patterns matching the instance types the facade is not applied to, tested
390
+ against the names they are registered with in this factory.
391
+
392
+ #### Returns
393
+
394
+ `void`
395
+
396
+ #### Throws
397
+
398
+ GuardError if the parameters are invalid.
399
+
400
+ #### Throws
401
+
402
+ GeneralError if no facade is registered with the name, or the factory is the facade
403
+ factory itself.
404
+
405
+ ***
406
+
407
+ ### unuseFacade() {#unusefacade}
408
+
409
+ > **unuseFacade**(`name`): `void`
410
+
411
+ Deactivate a facade for this factory. Deactivating a facade which is not active does nothing.
412
+
413
+ #### Parameters
414
+
415
+ ##### name
416
+
417
+ `string`
418
+
419
+ The name of the facade to deactivate.
420
+
421
+ #### Returns
422
+
423
+ `void`
424
+
425
+ #### Throws
426
+
427
+ GuardError if the parameters are invalid.
428
+
429
+ ***
430
+
369
431
  ### instancesMap() {#instancesmap}
370
432
 
371
- > **instancesMap**(): `object`
433
+ > **instancesMap**(`withFacade?`): `object`
372
434
 
373
435
  Get all the instances as a map.
374
436
 
437
+ #### Parameters
438
+
439
+ ##### withFacade?
440
+
441
+ `boolean`
442
+
443
+ Return the instances with the active facades applied, defaults to false.
444
+
375
445
  #### Returns
376
446
 
377
447
  `object`
@@ -382,10 +452,18 @@ The instances as a map.
382
452
 
383
453
  ### instancesList() {#instanceslist}
384
454
 
385
- > **instancesList**(): `T`[]
455
+ > **instancesList**(`withFacade?`): `T`[]
386
456
 
387
457
  Get all the instances as a list in the order they were registered.
388
458
 
459
+ #### Parameters
460
+
461
+ ##### withFacade?
462
+
463
+ `boolean`
464
+
465
+ Return the instances with the active facades applied, defaults to false.
466
+
389
467
  #### Returns
390
468
 
391
469
  `T`[]
@@ -10,6 +10,8 @@ minimum frequency is evicted.
10
10
  The timer only runs while there are entries; it stops automatically when the cache empties.
11
11
 
12
12
  `get` and `set` increment an entry's access frequency and reset its idle timer.
13
+ `set` and `getOrSet` accept an optional hard expiry timestamp; the entry is removed once that
14
+ time is reached however recently it was used, and the TTI still applies alongside it.
13
15
  `has` and `keys` are pure peeks they evict idle entries but do not affect frequency or TTI.
14
16
  Call `destroy` when the cache is no longer needed to stop the background timer.
15
17
 
@@ -125,7 +127,7 @@ The cached value, or undefined on a miss or idle eviction.
125
127
 
126
128
  ### set() {#set}
127
129
 
128
- > **set**(`key`, `value`): `void`
130
+ > **set**(`key`, `value`, `expires?`): `void`
129
131
 
130
132
  Store a value in the cache.
131
133
  If the key already exists its value and frequency are updated.
@@ -146,6 +148,13 @@ The key to store.
146
148
 
147
149
  The value to cache.
148
150
 
151
+ ##### expires?
152
+
153
+ `number`
154
+
155
+ Hard expiry timestamp in milliseconds since the epoch. The entry is removed
156
+ once this time is reached regardless of how recently it was used. Must be an integer.
157
+
149
158
  #### Returns
150
159
 
151
160
  `void`
@@ -154,7 +163,7 @@ The value to cache.
154
163
 
155
164
  ### getOrSet() {#getorset}
156
165
 
157
- > **getOrSet**(`key`, `valueFactory`): `Promise`\<`T`\>
166
+ > **getOrSet**(`key`, `valueFactory`, `expires?`): `Promise`\<`T`\>
158
167
 
159
168
  Atomically get an existing value or create and store it once using an async factory.
160
169
  Concurrent calls for the same key are serialized via a mutex.
@@ -173,6 +182,13 @@ The key to get or create.
173
182
 
174
183
  Async callback used to build a value when the key is absent.
175
184
 
185
+ ##### expires?
186
+
187
+ `number`
188
+
189
+ Hard expiry timestamp in milliseconds since the epoch, applied to the entry
190
+ when one is created. Must be an integer.
191
+
176
192
  #### Returns
177
193
 
178
194
  `Promise`\<`T`\>
@@ -8,6 +8,8 @@ Entries are removed in two ways:
8
8
  The timer only runs while there are entries; it stops automatically when the cache empties.
9
9
 
10
10
  `get` and `set` both update an entry's LRU position and reset its idle timer.
11
+ `set` and `getOrSet` accept an optional hard expiry timestamp; the entry is removed once that
12
+ time is reached however recently it was used, and the TTI still applies alongside it.
11
13
  `has` is a pure peek it evicts idle entries but does not refresh a live entry's TTI.
12
14
  Call `destroy` when the cache is no longer needed to stop the background timer.
13
15
 
@@ -123,7 +125,7 @@ The cached value, or undefined on a miss or idle eviction.
123
125
 
124
126
  ### set() {#set}
125
127
 
126
- > **set**(`key`, `value`): `void`
128
+ > **set**(`key`, `value`, `expires?`): `void`
127
129
 
128
130
  Store a value in the cache.
129
131
  If the key already exists its value and idle timer are refreshed.
@@ -144,6 +146,13 @@ The key to store.
144
146
 
145
147
  The value to cache.
146
148
 
149
+ ##### expires?
150
+
151
+ `number`
152
+
153
+ Hard expiry timestamp in milliseconds since the epoch. The entry is removed
154
+ once this time is reached regardless of how recently it was used. Must be an integer.
155
+
147
156
  #### Returns
148
157
 
149
158
  `void`
@@ -152,7 +161,7 @@ The value to cache.
152
161
 
153
162
  ### getOrSet() {#getorset}
154
163
 
155
- > **getOrSet**(`key`, `valueFactory`): `Promise`\<`T`\>
164
+ > **getOrSet**(`key`, `valueFactory`, `expires?`): `Promise`\<`T`\>
156
165
 
157
166
  Atomically get an existing value or create and store it once using an async factory.
158
167
  Concurrent calls for the same key are serialized via a mutex.
@@ -171,6 +180,13 @@ The key to get or create.
171
180
 
172
181
  Async callback used to build a value when the key is absent.
173
182
 
183
+ ##### expires?
184
+
185
+ `number`
186
+
187
+ Hard expiry timestamp in milliseconds since the epoch, applied to the entry
188
+ when one is created. Must be an integer.
189
+
174
190
  #### Returns
175
191
 
176
192
  `Promise`\<`T`\>
@@ -12,6 +12,13 @@ the shared buffer with the main thread on first use of each key, then caches it
12
12
  The main thread must call Mutex.handleWorkerMessage(msg) from its worker message handler
13
13
  before that worker first calls Mutex.lock().
14
14
 
15
+ Callers on the same thread are served in the order they arrived. Each key has a FIFO
16
+ queue of waiters, unlock() hands the lock directly to the waiter at the front, and a new
17
+ caller only takes the lock outright when that queue is empty. Without this a caller that
18
+ arrives while a waiter is being woken can take the lock first, which lets a busy key
19
+ starve a waiter until its timeout elapses. Threads still contend with each other for the
20
+ shared lock, so the ordering guarantee is per thread rather than global.
21
+
15
22
  The lock is not re-entrant: a thread that already holds a key and calls lock() again on
16
23
  the same key will block until the timeout elapses.
17
24
 
@@ -81,6 +88,8 @@ Acquires a lock for the given key without blocking the event loop. If the lock i
81
88
  held, it suspends the current async task until the lock is released or the timeout is reached.
82
89
  Use this in async single-threaded contexts (e.g. the main thread or a Fastify route handler)
83
90
  where calling the synchronous lock() would freeze the event loop and deadlock.
91
+ Callers on the same thread are served in the order they arrived, so a contended key
92
+ cannot starve an earlier caller.
84
93
  The lock is not re-entrant: if the same context holds the key and calls lockAsync() again on
85
94
  the same key, it will suspend until the timeout elapses.
86
95
 
@@ -0,0 +1,57 @@
1
+ # Class: TimeoutHelper
2
+
3
+ Helper for bounding operations which can fail to settle.
4
+
5
+ ## Constructors
6
+
7
+ ### Constructor
8
+
9
+ > **new TimeoutHelper**(): `TimeoutHelper`
10
+
11
+ #### Returns
12
+
13
+ `TimeoutHelper`
14
+
15
+ ## Methods
16
+
17
+ ### withTimeout() {#withtimeout}
18
+
19
+ > `static` **withTimeout**\<`T`\>(`operation`, `timeoutMs`, `onTimeout`): `Promise`\<`T`\>
20
+
21
+ Stop waiting for an operation which has not settled within the given time.
22
+ The operation itself cannot be cancelled, so a timed out operation is abandoned, which is
23
+ the only option available when the code being called can leave the promise it returned
24
+ pending forever.
25
+
26
+ #### Type Parameters
27
+
28
+ ##### T
29
+
30
+ `T`
31
+
32
+ #### Parameters
33
+
34
+ ##### operation
35
+
36
+ `Promise`\<`T`\>
37
+
38
+ The operation to bound.
39
+
40
+ ##### timeoutMs
41
+
42
+ `number`
43
+
44
+ The maximum time to wait in milliseconds, 0 or less waits indefinitely.
45
+
46
+ ##### onTimeout
47
+
48
+ () => `T`
49
+
50
+ Called when the wait expires, throw from it to fail the operation, or
51
+ return a value to complete it with that value instead.
52
+
53
+ #### Returns
54
+
55
+ `Promise`\<`T`\>
56
+
57
+ The result of the operation, or the value returned by onTimeout.
@@ -28,6 +28,7 @@
28
28
  - [ObjectHelper](classes/ObjectHelper.md)
29
29
  - [RandomHelper](classes/RandomHelper.md)
30
30
  - [StringHelper](classes/StringHelper.md)
31
+ - [TimeoutHelper](classes/TimeoutHelper.md)
31
32
  - [Uint8ArrayHelper](classes/Uint8ArrayHelper.md)
32
33
  - [BitString](classes/BitString.md)
33
34
  - [Duration](classes/Duration.md)
@@ -52,12 +53,14 @@
52
53
  - [IComponent](interfaces/IComponent.md)
53
54
  - [IDuration](interfaces/IDuration.md)
54
55
  - [IError](interfaces/IError.md)
56
+ - [IFacade](interfaces/IFacade.md)
55
57
  - [II18nShared](interfaces/II18nShared.md)
56
58
  - [IKeyValue](interfaces/IKeyValue.md)
57
59
  - [ILabelledValue](interfaces/ILabelledValue.md)
58
60
  - [ILocale](interfaces/ILocale.md)
59
61
  - [ILocaleDictionary](interfaces/ILocaleDictionary.md)
60
62
  - [ILocalesIndex](interfaces/ILocalesIndex.md)
63
+ - [IMutexWaiter](interfaces/IMutexWaiter.md)
61
64
  - [IMutexWorkerMessage](interfaces/IMutexWorkerMessage.md)
62
65
  - [IPatchOperation](interfaces/IPatchOperation.md)
63
66
  - [ISharedObjectBufferOptions](interfaces/ISharedObjectBufferOptions.md)
@@ -78,6 +81,7 @@
78
81
  ## Variables
79
82
 
80
83
  - [ComponentFactory](variables/ComponentFactory.md)
84
+ - [FacadeFactory](variables/FacadeFactory.md)
81
85
  - [CoerceType](variables/CoerceType.md)
82
86
  - [CompressionType](variables/CompressionType.md)
83
87
  - [MutexMessageTypes](variables/MutexMessageTypes.md)
@@ -0,0 +1,32 @@
1
+ # Interface: IFacade\<T\>
2
+
3
+ A facade wraps a component so a cross cutting concern can be applied to it without the
4
+ component being modified. The wrapped component is returned in place of the original.
5
+
6
+ ## Type Parameters
7
+
8
+ ### T
9
+
10
+ `T` = `unknown`
11
+
12
+ ## Methods
13
+
14
+ ### wrap() {#wrap}
15
+
16
+ > **wrap**(`target`): `T`
17
+
18
+ Wrap the target, returning a replacement.
19
+
20
+ #### Parameters
21
+
22
+ ##### target
23
+
24
+ `T`
25
+
26
+ The component to wrap.
27
+
28
+ #### Returns
29
+
30
+ `T`
31
+
32
+ The wrapped component.
@@ -0,0 +1,37 @@
1
+ # Interface: IMutexWaiter
2
+
3
+ A caller queued on a mutex key, waiting to be handed the lock.
4
+
5
+ ## Properties
6
+
7
+ ### settled {#settled}
8
+
9
+ > **settled**: `boolean`
10
+
11
+ Has the waiter already been granted the lock or given up waiting.
12
+
13
+ ***
14
+
15
+ ### resolve? {#resolve}
16
+
17
+ > `optional` **resolve?**: (`granted`) => `void`
18
+
19
+ Resolves the queued caller, true when it now owns the lock.
20
+
21
+ #### Parameters
22
+
23
+ ##### granted
24
+
25
+ `boolean`
26
+
27
+ #### Returns
28
+
29
+ `void`
30
+
31
+ ***
32
+
33
+ ### timer? {#timer}
34
+
35
+ > `optional` **timer?**: `Timeout`
36
+
37
+ Timer which enforces the waiter's deadline.
@@ -0,0 +1,5 @@
1
+ # Variable: FacadeFactory
2
+
3
+ > `const` **FacadeFactory**: [`Factory`](../classes/Factory.md)\<[`IFacade`](../interfaces/IFacade.md)\<`unknown`\>\>
4
+
5
+ Factory for creating implementation of facade types.
package/locales/en.json CHANGED
@@ -91,7 +91,10 @@
91
91
  "factory": {
92
92
  "noUnregister": "There is no {typeName} registered with the name \"{name}\"",
93
93
  "noGet": "The requested {typeName} \"{name}\" does not exist in the factory",
94
- "noCreate": "The requested {typeName} \"{name}\" cannot be created by the factory, with params \"{params}\""
94
+ "noCreate": "The requested {typeName} \"{name}\" cannot be created by the factory, with params \"{params}\"",
95
+ "noFacade": "The requested facade \"{name}\" is not registered, so it cannot be applied to the {typeName} factory",
96
+ "noFacadeOnFacades": "Facades cannot be applied to the facade factory itself",
97
+ "noFacadeWrap": "The facade \"{name}\" did not return a wrapped instance for the {typeName} factory"
95
98
  },
96
99
  "bitString": {
97
100
  "outOfRange": "The index should be >= 0 and less than the length of the bit string, the index is \"{index}\" and the number of bit is \"{numberBits}\""
@@ -111,7 +114,7 @@
111
114
  "mutex": {
112
115
  "lockNotFound": "The key \"{key}\" has no active lock",
113
116
  "lockAlreadyReleased": "The key \"{key}\" is not currently locked",
114
- "lockTimeout": "Failed to acquire lock for key \"{key}\" within the timeout of {timeout} milliseconds",
117
+ "lockTimeout": "Failed to acquire lock for key \"{key}\" within the timeout of {timeoutMs} milliseconds",
115
118
  "bufferFetchFailed": "Failed to retrieve shared buffer for key \"{key}\" from the main thread",
116
119
  "invalidTimeout": "The timeout value \"{timeoutMs}\" is invalid, it must be a non-negative integer"
117
120
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/core",
3
- "version": "0.9.3-next.1",
3
+ "version": "0.9.3-next.11",
4
4
  "description": "Helper methods/classes for data type checking/validation/guarding/error handling",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,7 +14,7 @@
14
14
  "node": ">=24.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@twin.org/nameof": "0.9.3-next.1",
17
+ "@twin.org/nameof": "0.9.3-next.11",
18
18
  "intl-messageformat": "11.2.13",
19
19
  "rfc6902": "5.3.0"
20
20
  },