@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.
- package/dist/es/factories/facadeFactory.js +9 -0
- package/dist/es/factories/facadeFactory.js.map +1 -0
- package/dist/es/factories/factory.js +137 -6
- package/dist/es/factories/factory.js.map +1 -1
- package/dist/es/helpers/timeoutHelper.js +46 -0
- package/dist/es/helpers/timeoutHelper.js.map +1 -0
- package/dist/es/index.js +4 -0
- package/dist/es/index.js.map +1 -1
- package/dist/es/models/IFacade.js +4 -0
- package/dist/es/models/IFacade.js.map +1 -0
- package/dist/es/models/IMutexWaiter.js +4 -0
- package/dist/es/models/IMutexWaiter.js.map +1 -0
- package/dist/es/utils/lfuCache.js +91 -13
- package/dist/es/utils/lfuCache.js.map +1 -1
- package/dist/es/utils/lruCache.js +83 -10
- package/dist/es/utils/lruCache.js.map +1 -1
- package/dist/es/utils/mutex.js +170 -23
- package/dist/es/utils/mutex.js.map +1 -1
- package/dist/types/factories/facadeFactory.d.ts +6 -0
- package/dist/types/factories/factory.d.ts +22 -2
- package/dist/types/helpers/timeoutHelper.d.ts +17 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/models/IFacade.d.ts +12 -0
- package/dist/types/models/IMutexWaiter.d.ts +17 -0
- package/dist/types/utils/lfuCache.d.ts +8 -2
- package/dist/types/utils/lruCache.d.ts +8 -2
- package/dist/types/utils/mutex.d.ts +9 -0
- package/docs/changelog.md +170 -0
- package/docs/examples.md +38 -1
- package/docs/reference/classes/Factory.md +80 -2
- package/docs/reference/classes/LfuCache.md +18 -2
- package/docs/reference/classes/LruCache.md +18 -2
- package/docs/reference/classes/Mutex.md +9 -0
- package/docs/reference/classes/TimeoutHelper.md +57 -0
- package/docs/reference/index.md +4 -0
- package/docs/reference/interfaces/IFacade.md +32 -0
- package/docs/reference/interfaces/IMutexWaiter.md +37 -0
- package/docs/reference/variables/FacadeFactory.md +5 -0
- package/locales/en.json +5 -2
- 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 =
|
|
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.
|
package/docs/reference/index.md
CHANGED
|
@@ -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.
|
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 {
|
|
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.
|
|
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.
|
|
17
|
+
"@twin.org/nameof": "0.9.3-next.11",
|
|
18
18
|
"intl-messageformat": "11.2.13",
|
|
19
19
|
"rfc6902": "5.3.0"
|
|
20
20
|
},
|