@twin.org/core 0.9.3-next.2 → 0.9.3-next.4
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 +127 -6
- package/dist/es/factories/factory.js.map +1 -1
- package/dist/es/index.js +2 -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/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/types/factories/facadeFactory.d.ts +6 -0
- package/dist/types/factories/factory.d.ts +20 -2
- package/dist/types/index.d.ts +2 -0
- package/dist/types/models/IFacade.d.ts +12 -0
- package/dist/types/utils/lfuCache.d.ts +8 -2
- package/dist/types/utils/lruCache.d.ts +8 -2
- package/docs/changelog.md +34 -0
- package/docs/examples.md +38 -1
- package/docs/reference/classes/Factory.md +73 -2
- package/docs/reference/classes/LfuCache.md +18 -2
- package/docs/reference/classes/LruCache.md +18 -2
- package/docs/reference/index.md +2 -0
- package/docs/reference/interfaces/IFacade.md +32 -0
- package/docs/reference/variables/FacadeFactory.md +5 -0
- package/locales/en.json +4 -1
- package/package.json +2 -2
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Copyright 2026 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { Factory } from "./factory.js";
|
|
4
|
+
/**
|
|
5
|
+
* Factory for creating implementation of facade types.
|
|
6
|
+
*/
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
8
|
+
export const FacadeFactory = Factory.createFactory("facade");
|
|
9
|
+
//# sourceMappingURL=facadeFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facadeFactory.js","sourceRoot":"","sources":["../../../src/factories/facadeFactory.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC;;GAEG;AACH,gEAAgE;AAChE,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAU,QAAQ,CAAC,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Factory } from \"./factory.js\";\nimport type { IFacade } from \"../models/IFacade.js\";\n\n/**\n * Factory for creating implementation of facade types.\n */\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport const FacadeFactory = Factory.createFactory<IFacade>(\"facade\");\n"]}
|
|
@@ -25,6 +25,27 @@ export class Factory {
|
|
|
25
25
|
* @internal
|
|
26
26
|
*/
|
|
27
27
|
_instances;
|
|
28
|
+
/**
|
|
29
|
+
* The factory the facades are resolved from, held once resolved. It cannot be resolved in the
|
|
30
|
+
* constructor, because createFactory only stores a factory after constructing it, so the facade
|
|
31
|
+
* factory would recurse while constructing itself.
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
_facadeFactory;
|
|
35
|
+
/**
|
|
36
|
+
* The facades applied to the instances, in the order they were activated. The facade is
|
|
37
|
+
* resolved when it is activated rather than when an instance is produced, so unregistering or
|
|
38
|
+
* replacing it afterwards cannot affect a factory which has already activated it.
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
_facades;
|
|
42
|
+
/**
|
|
43
|
+
* Store the instances with the facades applied, keyed as the instances are.
|
|
44
|
+
* The unwrapped instances stay in _instances, so activating or deactivating a facade only has
|
|
45
|
+
* to discard this cache rather than rebuild the components themselves.
|
|
46
|
+
* @internal
|
|
47
|
+
*/
|
|
48
|
+
_wrappedInstances;
|
|
28
49
|
/**
|
|
29
50
|
* Counter for the ordering.
|
|
30
51
|
* @internal
|
|
@@ -51,6 +72,8 @@ export class Factory {
|
|
|
51
72
|
this._typeName = typeName;
|
|
52
73
|
this._generators = {};
|
|
53
74
|
this._instances = {};
|
|
75
|
+
this._facades = [];
|
|
76
|
+
this._wrappedInstances = {};
|
|
54
77
|
this._orderCounter = 0;
|
|
55
78
|
this._autoInstance = autoInstance;
|
|
56
79
|
this._matcher = matcher ?? this.defaultMatcher.bind(this);
|
|
@@ -180,7 +203,7 @@ export class Factory {
|
|
|
180
203
|
this._instances[matchName] = this._generators[matchName].generator();
|
|
181
204
|
}
|
|
182
205
|
if (this._instances[matchName]) {
|
|
183
|
-
return this.
|
|
206
|
+
return this.wrappedInstance(matchName);
|
|
184
207
|
}
|
|
185
208
|
}
|
|
186
209
|
}
|
|
@@ -215,7 +238,10 @@ export class Factory {
|
|
|
215
238
|
Guards.stringValue(Factory.CLASS_NAME, "name", name);
|
|
216
239
|
const matchName = this._matcher(Object.keys(this._generators), name);
|
|
217
240
|
if (Is.stringValue(matchName) && this._generators[matchName]) {
|
|
218
|
-
|
|
241
|
+
const instance = this._generators[matchName].generator(args);
|
|
242
|
+
if (!Is.empty(instance)) {
|
|
243
|
+
return this.applyFacades(instance);
|
|
244
|
+
}
|
|
219
245
|
}
|
|
220
246
|
}
|
|
221
247
|
/**
|
|
@@ -226,31 +252,85 @@ export class Factory {
|
|
|
226
252
|
this.removeInstance(name);
|
|
227
253
|
}
|
|
228
254
|
this._instances = {};
|
|
255
|
+
this._wrappedInstances = {};
|
|
229
256
|
}
|
|
230
257
|
/**
|
|
231
258
|
* Remove all the instances and the generators.
|
|
232
259
|
*/
|
|
233
260
|
clear() {
|
|
234
261
|
this._instances = {};
|
|
262
|
+
this._wrappedInstances = {};
|
|
263
|
+
this._facades = [];
|
|
235
264
|
this._generators = {};
|
|
236
265
|
this._orderCounter = 0;
|
|
237
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Activate a facade for this factory, so every instance it produces is wrapped by it.
|
|
269
|
+
* An instance is passed through the facades in the order they were activated, so the facade
|
|
270
|
+
* activated first is the outermost. Activating a facade which is already active does nothing.
|
|
271
|
+
* @param name The name of the facade, as registered with the facade factory.
|
|
272
|
+
* @throws GuardError if the parameters are invalid.
|
|
273
|
+
* @throws GeneralError if no facade is registered with the name, or the factory is the facade
|
|
274
|
+
* factory itself.
|
|
275
|
+
*/
|
|
276
|
+
useFacade(name) {
|
|
277
|
+
Guards.stringValue(Factory.CLASS_NAME, "name", name);
|
|
278
|
+
const facadeFactory = this.facadeFactory();
|
|
279
|
+
// Wrapping the facades themselves would mean resolving a facade in order to apply it.
|
|
280
|
+
if (this._typeName === facadeFactory.typeName()) {
|
|
281
|
+
throw new GeneralError(Factory.CLASS_NAME, "noFacadeOnFacades");
|
|
282
|
+
}
|
|
283
|
+
if (!facadeFactory.hasName(name)) {
|
|
284
|
+
throw new GeneralError(Factory.CLASS_NAME, "noFacade", {
|
|
285
|
+
typeName: this._typeName,
|
|
286
|
+
name
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
if (!this._facades.some(f => f.name === name)) {
|
|
290
|
+
this._facades.push({ name, facade: facadeFactory.get(name) });
|
|
291
|
+
// Instances handed out before this point keep the facades they were built with, the
|
|
292
|
+
// change applies to instances produced from here on.
|
|
293
|
+
this._wrappedInstances = {};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Deactivate a facade for this factory. Deactivating a facade which is not active does nothing.
|
|
298
|
+
* @param name The name of the facade to deactivate.
|
|
299
|
+
* @throws GuardError if the parameters are invalid.
|
|
300
|
+
*/
|
|
301
|
+
unuseFacade(name) {
|
|
302
|
+
Guards.stringValue(Factory.CLASS_NAME, "name", name);
|
|
303
|
+
const index = this._facades.findIndex(f => f.name === name);
|
|
304
|
+
if (index >= 0) {
|
|
305
|
+
this._facades.splice(index, 1);
|
|
306
|
+
this._wrappedInstances = {};
|
|
307
|
+
}
|
|
308
|
+
}
|
|
238
309
|
/**
|
|
239
310
|
* Get all the instances as a map.
|
|
311
|
+
* @param withFacade Return the instances with the active facades applied, defaults to false.
|
|
240
312
|
* @returns The instances as a map.
|
|
241
313
|
*/
|
|
242
|
-
instancesMap() {
|
|
243
|
-
|
|
314
|
+
instancesMap(withFacade) {
|
|
315
|
+
if (!withFacade) {
|
|
316
|
+
return this._instances;
|
|
317
|
+
}
|
|
318
|
+
const instances = {};
|
|
319
|
+
for (const instanceName in this._instances) {
|
|
320
|
+
instances[instanceName] = this.wrappedInstance(instanceName);
|
|
321
|
+
}
|
|
322
|
+
return instances;
|
|
244
323
|
}
|
|
245
324
|
/**
|
|
246
325
|
* Get all the instances as a list in the order they were registered.
|
|
326
|
+
* @param withFacade Return the instances with the active facades applied, defaults to false.
|
|
247
327
|
* @returns The instances as a list in the order they were registered.
|
|
248
328
|
*/
|
|
249
|
-
instancesList() {
|
|
329
|
+
instancesList(withFacade) {
|
|
250
330
|
const orderedInstances = [];
|
|
251
331
|
for (const instanceName in this._instances) {
|
|
252
332
|
orderedInstances.push({
|
|
253
|
-
instance: this._instances[instanceName],
|
|
333
|
+
instance: withFacade ? this.wrappedInstance(instanceName) : this._instances[instanceName],
|
|
254
334
|
order: this._generators[instanceName].order
|
|
255
335
|
});
|
|
256
336
|
}
|
|
@@ -286,6 +366,47 @@ export class Factory {
|
|
|
286
366
|
*/
|
|
287
367
|
removeInstance(name) {
|
|
288
368
|
delete this._instances[name];
|
|
369
|
+
delete this._wrappedInstances[name];
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Get the factory the facades are resolved from, resolving it on first use.
|
|
373
|
+
* @returns The facade factory.
|
|
374
|
+
* @internal
|
|
375
|
+
*/
|
|
376
|
+
facadeFactory() {
|
|
377
|
+
this._facadeFactory ??= Factory.createFactory("facade");
|
|
378
|
+
return this._facadeFactory;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Get an instance with the active facades applied, wrapping it on first use.
|
|
382
|
+
* @param name The name of the instance, which must already have been created.
|
|
383
|
+
* @returns The wrapped instance.
|
|
384
|
+
* @throws GeneralError if a facade does not return a wrapped instance.
|
|
385
|
+
* @internal
|
|
386
|
+
*/
|
|
387
|
+
wrappedInstance(name) {
|
|
388
|
+
this._wrappedInstances[name] ??= this.applyFacades(this._instances[name]);
|
|
389
|
+
return this._wrappedInstances[name];
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Wrap an instance in the active facades. The first activated facade ends up outermost.
|
|
393
|
+
* @param instance The instance to wrap.
|
|
394
|
+
* @returns The wrapped instance, or the instance itself when no facades are active.
|
|
395
|
+
* @throws GeneralError if a facade does not return a wrapped instance.
|
|
396
|
+
* @internal
|
|
397
|
+
*/
|
|
398
|
+
applyFacades(instance) {
|
|
399
|
+
let wrapped = instance;
|
|
400
|
+
for (let i = this._facades.length - 1; i >= 0; i--) {
|
|
401
|
+
wrapped = this._facades[i].facade.wrap(wrapped);
|
|
402
|
+
if (Is.empty(wrapped)) {
|
|
403
|
+
throw new GeneralError(Factory.CLASS_NAME, "noFacadeWrap", {
|
|
404
|
+
typeName: this._typeName,
|
|
405
|
+
name: this._facades[i].name
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return wrapped;
|
|
289
410
|
}
|
|
290
411
|
/**
|
|
291
412
|
* Match the requested name to the generator name.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/factories/factory.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { nameof } from \"@twin.org/nameof\";\nimport { GeneralError } from \"../errors/generalError.js\";\nimport { Guards } from \"../utils/guards.js\";\nimport { Is } from \"../utils/is.js\";\nimport { SharedStore } from \"../utils/sharedStore.js\";\n\n/**\n * Factory for creating implementation of generic types.\n */\nexport class Factory<T> {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<Factory<unknown>>();\n\n\t/**\n\t * Type name for the instances.\n\t * @internal\n\t */\n\tprivate readonly _typeName: string;\n\n\t/**\n\t * Store the generators.\n\t * @internal\n\t */\n\tprivate _generators: {\n\t\t[name: string]: {\n\t\t\tgenerator: (args?: unknown) => T;\n\t\t\torder: number;\n\t\t};\n\t};\n\n\t/**\n\t * Store the created instances.\n\t * @internal\n\t */\n\tprivate _instances: { [name: string]: T };\n\n\t/**\n\t * Counter for the ordering.\n\t * @internal\n\t */\n\tprivate _orderCounter: number;\n\n\t/**\n\t * Automatically created an instance when registered.\n\t * @internal\n\t */\n\tprivate readonly _autoInstance: boolean;\n\n\t/**\n\t * Match the name of the instance.\n\t * @internal\n\t */\n\tprivate readonly _matcher: (names: string[], name: string) => string | undefined;\n\n\t/**\n\t * Create a new instance of Factory, private use createFactory.\n\t * @param typeName The type name for the instances.\n\t * @param autoInstance Automatically create an instance when registered.\n\t * @param matcher Match the name of the instance.\n\t * @internal\n\t */\n\tprivate constructor(\n\t\ttypeName: string,\n\t\tautoInstance: boolean = false,\n\t\tmatcher?: (names: string[], name: string) => string | undefined\n\t) {\n\t\tthis._typeName = typeName;\n\t\tthis._generators = {};\n\t\tthis._instances = {};\n\t\tthis._orderCounter = 0;\n\t\tthis._autoInstance = autoInstance;\n\t\tthis._matcher = matcher ?? this.defaultMatcher.bind(this);\n\t}\n\n\t/**\n\t * Create a new factory, which is shared throughout all library instances.\n\t * @param typeName The type name for the instances.\n\t * @param autoInstance Automatically create an instance when registered.\n\t * @param matcher Match the name of the instance.\n\t * @returns The factory instance.\n\t */\n\tpublic static createFactory<U>(\n\t\ttypeName: string,\n\t\tautoInstance: boolean = false,\n\t\tmatcher?: (names: string[], name: string) => string | undefined\n\t): Factory<U> {\n\t\tconst factories = Factory.getFactories();\n\n\t\tif (Is.undefined(factories[typeName])) {\n\t\t\tfactories[typeName] = new Factory<U>(typeName, autoInstance, matcher);\n\t\t}\n\t\treturn factories[typeName] as Factory<U>;\n\t}\n\n\t/**\n\t * Get all the factories.\n\t * @returns All the factories.\n\t */\n\tpublic static getFactories(): { [typeName: string]: Factory<unknown> } {\n\t\treturn SharedStore.get<{\n\t\t\t[typeName: string]: Factory<unknown>;\n\t\t}>(\"factories\", () => ({}));\n\t}\n\n\t/**\n\t * Get a specific factory by type name.\n\t * @param typeName The type name of the factory.\n\t * @returns The factory instance if it exists, otherwise undefined.\n\t */\n\tpublic static getFactory<T = unknown>(typeName: string): Factory<T> | undefined {\n\t\tconst factories = Factory.getFactories();\n\n\t\treturn factories[typeName] as Factory<T> | undefined;\n\t}\n\n\t/**\n\t * Reset all the factories, which removes any created instances, but not the registrations.\n\t */\n\tpublic static resetFactories(): void {\n\t\tconst factories = Factory.getFactories();\n\n\t\tfor (const typeName in factories) {\n\t\t\tfactories[typeName].reset();\n\t\t}\n\t}\n\n\t/**\n\t * Clear all the factories, which removes anything registered with the factories.\n\t */\n\tpublic static clearFactories(): void {\n\t\tconst factories = Factory.getFactories();\n\n\t\tfor (const typeName in factories) {\n\t\t\tfactories[typeName].clear();\n\t\t}\n\t}\n\n\t/**\n\t * Get the type name of the factory.\n\t * @returns The type name of the factory.\n\t */\n\tpublic typeName(): string {\n\t\treturn this._typeName;\n\t}\n\n\t/**\n\t * Register a new generator.\n\t * @param name The name of the generator.\n\t * @param generator The function to create an instance.\n\t */\n\tpublic register<U extends T>(name: string, generator: (args?: unknown) => U): void {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\t\tGuards.function(Factory.CLASS_NAME, nameof(generator), generator);\n\t\tthis._generators[name] = {\n\t\t\tgenerator,\n\t\t\torder: this._orderCounter++\n\t\t};\n\n\t\t// Remove any existing instance\n\t\tthis.removeInstance(name);\n\t\tif (this._autoInstance) {\n\t\t\tthis._instances[name] = generator();\n\t\t}\n\t}\n\n\t/**\n\t * Unregister a generator.\n\t * @param name The name of the generator to unregister.\n\t * @throws GuardError if the parameters are invalid.\n\t * @throws GeneralError if no generator exists.\n\t */\n\tpublic unregister(name: string): void {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\t\tif (!this._generators[name]) {\n\t\t\tthrow new GeneralError(Factory.CLASS_NAME, \"noUnregister\", {\n\t\t\t\ttypeName: this._typeName,\n\t\t\t\tname\n\t\t\t});\n\t\t}\n\t\tdelete this._generators[name];\n\t\t// Remove any existing instance\n\t\tthis.removeInstance(name);\n\t}\n\n\t/**\n\t * Get a generator instance.\n\t * @param name The name of the instance to generate.\n\t * @returns An instance of the item.\n\t * @throws GuardError if the parameters are invalid.\n\t * @throws GeneralError if no item exists to get.\n\t */\n\tpublic get<U extends T>(name: string): U {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\t\tconst instance = this.getIfExists(name);\n\t\tif (!instance) {\n\t\t\tthrow new GeneralError(Factory.CLASS_NAME, \"noGet\", {\n\t\t\t\ttypeName: this._typeName,\n\t\t\t\tname\n\t\t\t});\n\t\t}\n\t\treturn instance as U;\n\t}\n\n\t/**\n\t * Get a generator instance with no exceptions.\n\t * @param name The name of the instance to generate.\n\t * @returns An instance of the item or undefined if it does not exist.\n\t */\n\tpublic getIfExists<U extends T>(name?: string): U | undefined {\n\t\tif (Is.empty(name)) {\n\t\t\treturn;\n\t\t}\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\n\t\tconst matchName = this._matcher(Object.keys(this._generators), name);\n\n\t\tif (Is.stringValue(matchName) && this._generators[matchName]) {\n\t\t\tif (!this._instances[matchName]) {\n\t\t\t\tthis._instances[matchName] = this._generators[matchName].generator();\n\t\t\t}\n\t\t\tif (this._instances[matchName]) {\n\t\t\t\treturn this._instances[matchName] as U;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Create a new instance without caching it.\n\t * @param name The name of the instance to generate.\n\t * @param args The arguments to pass to the generator.\n\t * @returns A new instance of the item.\n\t * @throws GuardError if the parameters are invalid.\n\t * @throws GeneralError if no item exists to create.\n\t */\n\tpublic create<U extends T>(name: string, args?: unknown): U {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\t\tconst instance = this.createIfExists(name, args);\n\t\tif (!instance) {\n\t\t\tthrow new GeneralError(Factory.CLASS_NAME, \"noCreate\", {\n\t\t\t\ttypeName: this._typeName,\n\t\t\t\tname,\n\t\t\t\targs: Is.undefined(args) ? \"\" : JSON.stringify(args)\n\t\t\t});\n\t\t}\n\t\treturn instance as U;\n\t}\n\n\t/**\n\t * Create a new instance without caching it if it exists.\n\t * @param name The name of the instance to generate.\n\t * @param args The arguments to pass to the generator.\n\t * @returns A new instance of the item if it exists.\n\t * @throws GuardError if the parameters are invalid.\n\t */\n\tpublic createIfExists<U extends T>(name: string, args?: unknown): U | undefined {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\t\tconst matchName = this._matcher(Object.keys(this._generators), name);\n\n\t\tif (Is.stringValue(matchName) && this._generators[matchName]) {\n\t\t\treturn this._generators[matchName].generator(args) as U;\n\t\t}\n\t}\n\n\t/**\n\t * Remove all the instances and leave the generators intact.\n\t */\n\tpublic reset(): void {\n\t\tfor (const name in this._generators) {\n\t\t\tthis.removeInstance(name);\n\t\t}\n\t\tthis._instances = {};\n\t}\n\n\t/**\n\t * Remove all the instances and the generators.\n\t */\n\tpublic clear(): void {\n\t\tthis._instances = {};\n\t\tthis._generators = {};\n\t\tthis._orderCounter = 0;\n\t}\n\n\t/**\n\t * Get all the instances as a map.\n\t * @returns The instances as a map.\n\t */\n\tpublic instancesMap(): { [name: string]: T } {\n\t\treturn this._instances;\n\t}\n\n\t/**\n\t * Get all the instances as a list in the order they were registered.\n\t * @returns The instances as a list in the order they were registered.\n\t */\n\tpublic instancesList(): T[] {\n\t\tconst orderedInstances: { instance: T; order: number }[] = [];\n\t\tfor (const instanceName in this._instances) {\n\t\t\torderedInstances.push({\n\t\t\t\tinstance: this._instances[instanceName],\n\t\t\t\torder: this._generators[instanceName].order\n\t\t\t});\n\t\t}\n\t\treturn orderedInstances.sort((a, b) => a.order - b.order).map(o => o.instance);\n\t}\n\n\t/**\n\t * Get all the generator names in the order they were registered.\n\t * @returns The ordered generator names.\n\t */\n\tpublic names(): string[] {\n\t\tconst orderedNames: { name: string; order: number }[] = [];\n\t\tfor (const generator in this._generators) {\n\t\t\torderedNames.push({\n\t\t\t\tname: generator,\n\t\t\t\torder: this._generators[generator].order\n\t\t\t});\n\t\t}\n\t\treturn orderedNames.sort((a, b) => a.order - b.order).map(o => o.name);\n\t}\n\n\t/**\n\t * Does the factory contain the name.\n\t * @param name The name of the instance to find.\n\t * @returns True if the factory has a matching name.\n\t */\n\tpublic hasName(name: string): boolean {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\t\treturn Is.stringValue(this._matcher(Object.keys(this._generators), name));\n\t}\n\n\t/**\n\t * Remove any instances of the given name.\n\t * @param name The name of the instances to remove.\n\t * @internal\n\t */\n\tprivate removeInstance(name: string): void {\n\t\tdelete this._instances[name];\n\t}\n\n\t/**\n\t * Match the requested name to the generator name.\n\t * @param names The list of names for all the generators.\n\t * @param name The name to match.\n\t * @returns The matched name or undefined if no match.\n\t * @internal\n\t */\n\tprivate defaultMatcher(names: string[], name: string): string | undefined {\n\t\treturn this._generators[name] ? name : undefined;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/factories/factory.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { nameof } from \"@twin.org/nameof\";\nimport { GeneralError } from \"../errors/generalError.js\";\nimport type { IFacade } from \"../models/IFacade.js\";\nimport { Guards } from \"../utils/guards.js\";\nimport { Is } from \"../utils/is.js\";\nimport { SharedStore } from \"../utils/sharedStore.js\";\n\n/**\n * Factory for creating implementation of generic types.\n */\nexport class Factory<T> {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<Factory<unknown>>();\n\n\t/**\n\t * Type name for the instances.\n\t * @internal\n\t */\n\tprivate readonly _typeName: string;\n\n\t/**\n\t * Store the generators.\n\t * @internal\n\t */\n\tprivate _generators: {\n\t\t[name: string]: {\n\t\t\tgenerator: (args?: unknown) => T;\n\t\t\torder: number;\n\t\t};\n\t};\n\n\t/**\n\t * Store the created instances.\n\t * @internal\n\t */\n\tprivate _instances: { [name: string]: T };\n\n\t/**\n\t * The factory the facades are resolved from, held once resolved. It cannot be resolved in the\n\t * constructor, because createFactory only stores a factory after constructing it, so the facade\n\t * factory would recurse while constructing itself.\n\t * @internal\n\t */\n\tprivate _facadeFactory?: Factory<IFacade>;\n\n\t/**\n\t * The facades applied to the instances, in the order they were activated. The facade is\n\t * resolved when it is activated rather than when an instance is produced, so unregistering or\n\t * replacing it afterwards cannot affect a factory which has already activated it.\n\t * @internal\n\t */\n\tprivate _facades: { name: string; facade: IFacade }[];\n\n\t/**\n\t * Store the instances with the facades applied, keyed as the instances are.\n\t * The unwrapped instances stay in _instances, so activating or deactivating a facade only has\n\t * to discard this cache rather than rebuild the components themselves.\n\t * @internal\n\t */\n\tprivate _wrappedInstances: { [name: string]: T };\n\n\t/**\n\t * Counter for the ordering.\n\t * @internal\n\t */\n\tprivate _orderCounter: number;\n\n\t/**\n\t * Automatically created an instance when registered.\n\t * @internal\n\t */\n\tprivate readonly _autoInstance: boolean;\n\n\t/**\n\t * Match the name of the instance.\n\t * @internal\n\t */\n\tprivate readonly _matcher: (names: string[], name: string) => string | undefined;\n\n\t/**\n\t * Create a new instance of Factory, private use createFactory.\n\t * @param typeName The type name for the instances.\n\t * @param autoInstance Automatically create an instance when registered.\n\t * @param matcher Match the name of the instance.\n\t * @internal\n\t */\n\tprivate constructor(\n\t\ttypeName: string,\n\t\tautoInstance: boolean = false,\n\t\tmatcher?: (names: string[], name: string) => string | undefined\n\t) {\n\t\tthis._typeName = typeName;\n\t\tthis._generators = {};\n\t\tthis._instances = {};\n\t\tthis._facades = [];\n\t\tthis._wrappedInstances = {};\n\t\tthis._orderCounter = 0;\n\t\tthis._autoInstance = autoInstance;\n\t\tthis._matcher = matcher ?? this.defaultMatcher.bind(this);\n\t}\n\n\t/**\n\t * Create a new factory, which is shared throughout all library instances.\n\t * @param typeName The type name for the instances.\n\t * @param autoInstance Automatically create an instance when registered.\n\t * @param matcher Match the name of the instance.\n\t * @returns The factory instance.\n\t */\n\tpublic static createFactory<U>(\n\t\ttypeName: string,\n\t\tautoInstance: boolean = false,\n\t\tmatcher?: (names: string[], name: string) => string | undefined\n\t): Factory<U> {\n\t\tconst factories = Factory.getFactories();\n\n\t\tif (Is.undefined(factories[typeName])) {\n\t\t\tfactories[typeName] = new Factory<U>(typeName, autoInstance, matcher);\n\t\t}\n\t\treturn factories[typeName] as Factory<U>;\n\t}\n\n\t/**\n\t * Get all the factories.\n\t * @returns All the factories.\n\t */\n\tpublic static getFactories(): { [typeName: string]: Factory<unknown> } {\n\t\treturn SharedStore.get<{\n\t\t\t[typeName: string]: Factory<unknown>;\n\t\t}>(\"factories\", () => ({}));\n\t}\n\n\t/**\n\t * Get a specific factory by type name.\n\t * @param typeName The type name of the factory.\n\t * @returns The factory instance if it exists, otherwise undefined.\n\t */\n\tpublic static getFactory<T = unknown>(typeName: string): Factory<T> | undefined {\n\t\tconst factories = Factory.getFactories();\n\n\t\treturn factories[typeName] as Factory<T> | undefined;\n\t}\n\n\t/**\n\t * Reset all the factories, which removes any created instances, but not the registrations.\n\t */\n\tpublic static resetFactories(): void {\n\t\tconst factories = Factory.getFactories();\n\n\t\tfor (const typeName in factories) {\n\t\t\tfactories[typeName].reset();\n\t\t}\n\t}\n\n\t/**\n\t * Clear all the factories, which removes anything registered with the factories.\n\t */\n\tpublic static clearFactories(): void {\n\t\tconst factories = Factory.getFactories();\n\n\t\tfor (const typeName in factories) {\n\t\t\tfactories[typeName].clear();\n\t\t}\n\t}\n\n\t/**\n\t * Get the type name of the factory.\n\t * @returns The type name of the factory.\n\t */\n\tpublic typeName(): string {\n\t\treturn this._typeName;\n\t}\n\n\t/**\n\t * Register a new generator.\n\t * @param name The name of the generator.\n\t * @param generator The function to create an instance.\n\t */\n\tpublic register<U extends T>(name: string, generator: (args?: unknown) => U): void {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\t\tGuards.function(Factory.CLASS_NAME, nameof(generator), generator);\n\t\tthis._generators[name] = {\n\t\t\tgenerator,\n\t\t\torder: this._orderCounter++\n\t\t};\n\n\t\t// Remove any existing instance\n\t\tthis.removeInstance(name);\n\t\tif (this._autoInstance) {\n\t\t\tthis._instances[name] = generator();\n\t\t}\n\t}\n\n\t/**\n\t * Unregister a generator.\n\t * @param name The name of the generator to unregister.\n\t * @throws GuardError if the parameters are invalid.\n\t * @throws GeneralError if no generator exists.\n\t */\n\tpublic unregister(name: string): void {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\t\tif (!this._generators[name]) {\n\t\t\tthrow new GeneralError(Factory.CLASS_NAME, \"noUnregister\", {\n\t\t\t\ttypeName: this._typeName,\n\t\t\t\tname\n\t\t\t});\n\t\t}\n\t\tdelete this._generators[name];\n\t\t// Remove any existing instance\n\t\tthis.removeInstance(name);\n\t}\n\n\t/**\n\t * Get a generator instance.\n\t * @param name The name of the instance to generate.\n\t * @returns An instance of the item.\n\t * @throws GuardError if the parameters are invalid.\n\t * @throws GeneralError if no item exists to get.\n\t */\n\tpublic get<U extends T>(name: string): U {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\t\tconst instance = this.getIfExists(name);\n\t\tif (!instance) {\n\t\t\tthrow new GeneralError(Factory.CLASS_NAME, \"noGet\", {\n\t\t\t\ttypeName: this._typeName,\n\t\t\t\tname\n\t\t\t});\n\t\t}\n\t\treturn instance as U;\n\t}\n\n\t/**\n\t * Get a generator instance with no exceptions.\n\t * @param name The name of the instance to generate.\n\t * @returns An instance of the item or undefined if it does not exist.\n\t */\n\tpublic getIfExists<U extends T>(name?: string): U | undefined {\n\t\tif (Is.empty(name)) {\n\t\t\treturn;\n\t\t}\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\n\t\tconst matchName = this._matcher(Object.keys(this._generators), name);\n\n\t\tif (Is.stringValue(matchName) && this._generators[matchName]) {\n\t\t\tif (!this._instances[matchName]) {\n\t\t\t\tthis._instances[matchName] = this._generators[matchName].generator();\n\t\t\t}\n\t\t\tif (this._instances[matchName]) {\n\t\t\t\treturn this.wrappedInstance(matchName) as U;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Create a new instance without caching it.\n\t * @param name The name of the instance to generate.\n\t * @param args The arguments to pass to the generator.\n\t * @returns A new instance of the item.\n\t * @throws GuardError if the parameters are invalid.\n\t * @throws GeneralError if no item exists to create.\n\t */\n\tpublic create<U extends T>(name: string, args?: unknown): U {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\t\tconst instance = this.createIfExists(name, args);\n\t\tif (!instance) {\n\t\t\tthrow new GeneralError(Factory.CLASS_NAME, \"noCreate\", {\n\t\t\t\ttypeName: this._typeName,\n\t\t\t\tname,\n\t\t\t\targs: Is.undefined(args) ? \"\" : JSON.stringify(args)\n\t\t\t});\n\t\t}\n\t\treturn instance as U;\n\t}\n\n\t/**\n\t * Create a new instance without caching it if it exists.\n\t * @param name The name of the instance to generate.\n\t * @param args The arguments to pass to the generator.\n\t * @returns A new instance of the item if it exists.\n\t * @throws GuardError if the parameters are invalid.\n\t */\n\tpublic createIfExists<U extends T>(name: string, args?: unknown): U | undefined {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\t\tconst matchName = this._matcher(Object.keys(this._generators), name);\n\n\t\tif (Is.stringValue(matchName) && this._generators[matchName]) {\n\t\t\tconst instance = this._generators[matchName].generator(args);\n\n\t\t\tif (!Is.empty(instance)) {\n\t\t\t\treturn this.applyFacades(instance) as U;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Remove all the instances and leave the generators intact.\n\t */\n\tpublic reset(): void {\n\t\tfor (const name in this._generators) {\n\t\t\tthis.removeInstance(name);\n\t\t}\n\t\tthis._instances = {};\n\t\tthis._wrappedInstances = {};\n\t}\n\n\t/**\n\t * Remove all the instances and the generators.\n\t */\n\tpublic clear(): void {\n\t\tthis._instances = {};\n\t\tthis._wrappedInstances = {};\n\t\tthis._facades = [];\n\t\tthis._generators = {};\n\t\tthis._orderCounter = 0;\n\t}\n\n\t/**\n\t * Activate a facade for this factory, so every instance it produces is wrapped by it.\n\t * An instance is passed through the facades in the order they were activated, so the facade\n\t * activated first is the outermost. Activating a facade which is already active does nothing.\n\t * @param name The name of the facade, as registered with the facade factory.\n\t * @throws GuardError if the parameters are invalid.\n\t * @throws GeneralError if no facade is registered with the name, or the factory is the facade\n\t * factory itself.\n\t */\n\tpublic useFacade(name: string): void {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\n\t\tconst facadeFactory = this.facadeFactory();\n\n\t\t// Wrapping the facades themselves would mean resolving a facade in order to apply it.\n\t\tif (this._typeName === facadeFactory.typeName()) {\n\t\t\tthrow new GeneralError(Factory.CLASS_NAME, \"noFacadeOnFacades\");\n\t\t}\n\n\t\tif (!facadeFactory.hasName(name)) {\n\t\t\tthrow new GeneralError(Factory.CLASS_NAME, \"noFacade\", {\n\t\t\t\ttypeName: this._typeName,\n\t\t\t\tname\n\t\t\t});\n\t\t}\n\n\t\tif (!this._facades.some(f => f.name === name)) {\n\t\t\tthis._facades.push({ name, facade: facadeFactory.get(name) });\n\t\t\t// Instances handed out before this point keep the facades they were built with, the\n\t\t\t// change applies to instances produced from here on.\n\t\t\tthis._wrappedInstances = {};\n\t\t}\n\t}\n\n\t/**\n\t * Deactivate a facade for this factory. Deactivating a facade which is not active does nothing.\n\t * @param name The name of the facade to deactivate.\n\t * @throws GuardError if the parameters are invalid.\n\t */\n\tpublic unuseFacade(name: string): void {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\n\t\tconst index = this._facades.findIndex(f => f.name === name);\n\t\tif (index >= 0) {\n\t\t\tthis._facades.splice(index, 1);\n\t\t\tthis._wrappedInstances = {};\n\t\t}\n\t}\n\n\t/**\n\t * Get all the instances as a map.\n\t * @param withFacade Return the instances with the active facades applied, defaults to false.\n\t * @returns The instances as a map.\n\t */\n\tpublic instancesMap(withFacade?: boolean): { [name: string]: T } {\n\t\tif (!withFacade) {\n\t\t\treturn this._instances;\n\t\t}\n\n\t\tconst instances: { [name: string]: T } = {};\n\t\tfor (const instanceName in this._instances) {\n\t\t\tinstances[instanceName] = this.wrappedInstance(instanceName);\n\t\t}\n\t\treturn instances;\n\t}\n\n\t/**\n\t * Get all the instances as a list in the order they were registered.\n\t * @param withFacade Return the instances with the active facades applied, defaults to false.\n\t * @returns The instances as a list in the order they were registered.\n\t */\n\tpublic instancesList(withFacade?: boolean): T[] {\n\t\tconst orderedInstances: { instance: T; order: number }[] = [];\n\t\tfor (const instanceName in this._instances) {\n\t\t\torderedInstances.push({\n\t\t\t\tinstance: withFacade ? this.wrappedInstance(instanceName) : this._instances[instanceName],\n\t\t\t\torder: this._generators[instanceName].order\n\t\t\t});\n\t\t}\n\t\treturn orderedInstances.sort((a, b) => a.order - b.order).map(o => o.instance);\n\t}\n\n\t/**\n\t * Get all the generator names in the order they were registered.\n\t * @returns The ordered generator names.\n\t */\n\tpublic names(): string[] {\n\t\tconst orderedNames: { name: string; order: number }[] = [];\n\t\tfor (const generator in this._generators) {\n\t\t\torderedNames.push({\n\t\t\t\tname: generator,\n\t\t\t\torder: this._generators[generator].order\n\t\t\t});\n\t\t}\n\t\treturn orderedNames.sort((a, b) => a.order - b.order).map(o => o.name);\n\t}\n\n\t/**\n\t * Does the factory contain the name.\n\t * @param name The name of the instance to find.\n\t * @returns True if the factory has a matching name.\n\t */\n\tpublic hasName(name: string): boolean {\n\t\tGuards.stringValue(Factory.CLASS_NAME, nameof(name), name);\n\t\treturn Is.stringValue(this._matcher(Object.keys(this._generators), name));\n\t}\n\n\t/**\n\t * Remove any instances of the given name.\n\t * @param name The name of the instances to remove.\n\t * @internal\n\t */\n\tprivate removeInstance(name: string): void {\n\t\tdelete this._instances[name];\n\t\tdelete this._wrappedInstances[name];\n\t}\n\n\t/**\n\t * Get the factory the facades are resolved from, resolving it on first use.\n\t * @returns The facade factory.\n\t * @internal\n\t */\n\tprivate facadeFactory(): Factory<IFacade> {\n\t\tthis._facadeFactory ??= Factory.createFactory<IFacade>(\"facade\");\n\n\t\treturn this._facadeFactory;\n\t}\n\n\t/**\n\t * Get an instance with the active facades applied, wrapping it on first use.\n\t * @param name The name of the instance, which must already have been created.\n\t * @returns The wrapped instance.\n\t * @throws GeneralError if a facade does not return a wrapped instance.\n\t * @internal\n\t */\n\tprivate wrappedInstance(name: string): T {\n\t\tthis._wrappedInstances[name] ??= this.applyFacades(this._instances[name]);\n\n\t\treturn this._wrappedInstances[name];\n\t}\n\n\t/**\n\t * Wrap an instance in the active facades. The first activated facade ends up outermost.\n\t * @param instance The instance to wrap.\n\t * @returns The wrapped instance, or the instance itself when no facades are active.\n\t * @throws GeneralError if a facade does not return a wrapped instance.\n\t * @internal\n\t */\n\tprivate applyFacades(instance: T): T {\n\t\tlet wrapped = instance;\n\n\t\tfor (let i = this._facades.length - 1; i >= 0; i--) {\n\t\t\twrapped = this._facades[i].facade.wrap(wrapped) as T;\n\n\t\t\tif (Is.empty(wrapped)) {\n\t\t\t\tthrow new GeneralError(Factory.CLASS_NAME, \"noFacadeWrap\", {\n\t\t\t\t\ttypeName: this._typeName,\n\t\t\t\t\tname: this._facades[i].name\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn wrapped;\n\t}\n\n\t/**\n\t * Match the requested name to the generator name.\n\t * @param names The list of names for all the generators.\n\t * @param name The name to match.\n\t * @returns The matched name or undefined if no match.\n\t * @internal\n\t */\n\tprivate defaultMatcher(names: string[], name: string): string | undefined {\n\t\treturn this._generators[name] ? name : undefined;\n\t}\n}\n"]}
|
package/dist/es/index.js
CHANGED
|
@@ -16,6 +16,7 @@ export * from "./errors/unauthorizedError.js";
|
|
|
16
16
|
export * from "./errors/unprocessableError.js";
|
|
17
17
|
export * from "./errors/validationError.js";
|
|
18
18
|
export * from "./factories/componentFactory.js";
|
|
19
|
+
export * from "./factories/facadeFactory.js";
|
|
19
20
|
export * from "./factories/factory.js";
|
|
20
21
|
export * from "./helpers/arrayHelper.js";
|
|
21
22
|
export * from "./helpers/envHelper.js";
|
|
@@ -32,6 +33,7 @@ export * from "./models/coerceType.js";
|
|
|
32
33
|
export * from "./models/IDuration.js";
|
|
33
34
|
export * from "./models/compressionType.js";
|
|
34
35
|
export * from "./models/IComponent.js";
|
|
36
|
+
export * from "./models/IFacade.js";
|
|
35
37
|
export * from "./models/IError.js";
|
|
36
38
|
export * from "./models/II18nShared.js";
|
|
37
39
|
export * from "./models/IKeyValue.js";
|
package/dist/es/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iCAAiC,CAAC;AAChD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wCAAwC,CAAC;AACvD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,uBAAuB,CAAC;AACtC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4CAA4C,CAAC;AAC3D,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,kCAAkC,CAAC;AACjD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./encoding/base32.js\";\nexport * from \"./encoding/base58.js\";\nexport * from \"./encoding/base64.js\";\nexport * from \"./encoding/base64Url.js\";\nexport * from \"./errors/alreadyExistsError.js\";\nexport * from \"./errors/baseError.js\";\nexport * from \"./errors/conflictError.js\";\nexport * from \"./errors/generalError.js\";\nexport * from \"./errors/guardError.js\";\nexport * from \"./errors/notFoundError.js\";\nexport * from \"./errors/notImplementedError.js\";\nexport * from \"./errors/notSupportedError.js\";\nexport * from \"./errors/unauthorizedError.js\";\nexport * from \"./errors/unprocessableError.js\";\nexport * from \"./errors/validationError.js\";\nexport * from \"./factories/componentFactory.js\";\nexport * from \"./factories/factory.js\";\nexport * from \"./helpers/arrayHelper.js\";\nexport * from \"./helpers/envHelper.js\";\nexport * from \"./helpers/errorHelper.js\";\nexport * from \"./helpers/filenameHelper.js\";\nexport * from \"./helpers/hexHelper.js\";\nexport * from \"./helpers/jsonHelper.js\";\nexport * from \"./helpers/numberHelper.js\";\nexport * from \"./helpers/objectHelper.js\";\nexport * from \"./helpers/randomHelper.js\";\nexport * from \"./helpers/stringHelper.js\";\nexport * from \"./helpers/uint8ArrayHelper.js\";\nexport * from \"./models/coerceType.js\";\nexport * from \"./models/IDuration.js\";\nexport * from \"./models/compressionType.js\";\nexport * from \"./models/IComponent.js\";\nexport * from \"./models/IError.js\";\nexport * from \"./models/II18nShared.js\";\nexport * from \"./models/IKeyValue.js\";\nexport * from \"./models/ILabelledValue.js\";\nexport * from \"./models/ILocale.js\";\nexport * from \"./models/ILocaleDictionary.js\";\nexport * from \"./models/ILocalesIndex.js\";\nexport * from \"./models/IMutexWorkerMessage.js\";\nexport * from \"./models/IPatchOperation.js\";\nexport * from \"./models/ISharedObjectBufferOptions.js\";\nexport * from \"./models/ISharedObjectBufferWorkerMessage.js\";\nexport * from \"./models/IUrlParts.js\";\nexport * from \"./models/IValidationFailure.js\";\nexport * from \"./models/mutexMessageTypes.js\";\nexport * from \"./models/sharedObjectBufferMessageTypes.js\";\nexport * from \"./types/bitString.js\";\nexport * from \"./types/duration.js\";\nexport * from \"./types/durationRegExp.js\";\nexport * from \"./types/objectOrArray.js\";\nexport * from \"./types/singleOccurrenceArray.js\";\nexport * from \"./types/singleOccurrenceArrayDepthHelper.js\";\nexport * from \"./types/url.js\";\nexport * from \"./types/urn.js\";\nexport * from \"./utils/asyncCache.js\";\nexport * from \"./utils/lfuCache.js\";\nexport * from \"./utils/lruCache.js\";\nexport * from \"./utils/coerce.js\";\nexport * from \"./utils/compression.js\";\nexport * from \"./utils/converter.js\";\nexport * from \"./utils/guards.js\";\nexport * from \"./utils/i18n.js\";\nexport * from \"./utils/is.js\";\nexport * from \"./utils/mutex.js\";\nexport * from \"./utils/sharedObjectBuffer.js\";\nexport * from \"./utils/sharedStore.js\";\nexport * from \"./utils/validation.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iCAAiC,CAAC;AAChD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wCAAwC,CAAC;AACvD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,uBAAuB,CAAC;AACtC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4CAA4C,CAAC;AAC3D,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,kCAAkC,CAAC;AACjD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./encoding/base32.js\";\nexport * from \"./encoding/base58.js\";\nexport * from \"./encoding/base64.js\";\nexport * from \"./encoding/base64Url.js\";\nexport * from \"./errors/alreadyExistsError.js\";\nexport * from \"./errors/baseError.js\";\nexport * from \"./errors/conflictError.js\";\nexport * from \"./errors/generalError.js\";\nexport * from \"./errors/guardError.js\";\nexport * from \"./errors/notFoundError.js\";\nexport * from \"./errors/notImplementedError.js\";\nexport * from \"./errors/notSupportedError.js\";\nexport * from \"./errors/unauthorizedError.js\";\nexport * from \"./errors/unprocessableError.js\";\nexport * from \"./errors/validationError.js\";\nexport * from \"./factories/componentFactory.js\";\nexport * from \"./factories/facadeFactory.js\";\nexport * from \"./factories/factory.js\";\nexport * from \"./helpers/arrayHelper.js\";\nexport * from \"./helpers/envHelper.js\";\nexport * from \"./helpers/errorHelper.js\";\nexport * from \"./helpers/filenameHelper.js\";\nexport * from \"./helpers/hexHelper.js\";\nexport * from \"./helpers/jsonHelper.js\";\nexport * from \"./helpers/numberHelper.js\";\nexport * from \"./helpers/objectHelper.js\";\nexport * from \"./helpers/randomHelper.js\";\nexport * from \"./helpers/stringHelper.js\";\nexport * from \"./helpers/uint8ArrayHelper.js\";\nexport * from \"./models/coerceType.js\";\nexport * from \"./models/IDuration.js\";\nexport * from \"./models/compressionType.js\";\nexport * from \"./models/IComponent.js\";\nexport * from \"./models/IFacade.js\";\nexport * from \"./models/IError.js\";\nexport * from \"./models/II18nShared.js\";\nexport * from \"./models/IKeyValue.js\";\nexport * from \"./models/ILabelledValue.js\";\nexport * from \"./models/ILocale.js\";\nexport * from \"./models/ILocaleDictionary.js\";\nexport * from \"./models/ILocalesIndex.js\";\nexport * from \"./models/IMutexWorkerMessage.js\";\nexport * from \"./models/IPatchOperation.js\";\nexport * from \"./models/ISharedObjectBufferOptions.js\";\nexport * from \"./models/ISharedObjectBufferWorkerMessage.js\";\nexport * from \"./models/IUrlParts.js\";\nexport * from \"./models/IValidationFailure.js\";\nexport * from \"./models/mutexMessageTypes.js\";\nexport * from \"./models/sharedObjectBufferMessageTypes.js\";\nexport * from \"./types/bitString.js\";\nexport * from \"./types/duration.js\";\nexport * from \"./types/durationRegExp.js\";\nexport * from \"./types/objectOrArray.js\";\nexport * from \"./types/singleOccurrenceArray.js\";\nexport * from \"./types/singleOccurrenceArrayDepthHelper.js\";\nexport * from \"./types/url.js\";\nexport * from \"./types/urn.js\";\nexport * from \"./utils/asyncCache.js\";\nexport * from \"./utils/lfuCache.js\";\nexport * from \"./utils/lruCache.js\";\nexport * from \"./utils/coerce.js\";\nexport * from \"./utils/compression.js\";\nexport * from \"./utils/converter.js\";\nexport * from \"./utils/guards.js\";\nexport * from \"./utils/i18n.js\";\nexport * from \"./utils/is.js\";\nexport * from \"./utils/mutex.js\";\nexport * from \"./utils/sharedObjectBuffer.js\";\nexport * from \"./utils/sharedStore.js\";\nexport * from \"./utils/validation.js\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IFacade.js","sourceRoot":"","sources":["../../../src/models/IFacade.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * A facade wraps a component so a cross cutting concern can be applied to it without the\n * component being modified. The wrapped component is returned in place of the original.\n */\nexport interface IFacade<T = unknown> {\n\t/**\n\t * Wrap the target, returning a replacement.\n\t * @param target The component to wrap.\n\t * @returns The wrapped component.\n\t */\n\twrap(target: T): T;\n}\n"]}
|
|
@@ -14,6 +14,8 @@ import { RandomHelper } from "../helpers/randomHelper.js";
|
|
|
14
14
|
* The timer only runs while there are entries; it stops automatically when the cache empties.
|
|
15
15
|
*
|
|
16
16
|
* `get` and `set` increment an entry's access frequency and reset its idle timer.
|
|
17
|
+
* `set` and `getOrSet` accept an optional hard expiry timestamp; the entry is removed once that
|
|
18
|
+
* time is reached however recently it was used, and the TTI still applies alongside it.
|
|
17
19
|
* `has` and `keys` are pure peeks they evict idle entries but do not affect frequency or TTI.
|
|
18
20
|
* Call `destroy` when the cache is no longer needed to stop the background timer.
|
|
19
21
|
*/
|
|
@@ -65,6 +67,16 @@ export class LfuCache {
|
|
|
65
67
|
* @internal
|
|
66
68
|
*/
|
|
67
69
|
_minFreq;
|
|
70
|
+
/**
|
|
71
|
+
* The earliest hard expiry timestamp among the live entries, used to pace the sweep timer.
|
|
72
|
+
* @internal
|
|
73
|
+
*/
|
|
74
|
+
_nextExpires;
|
|
75
|
+
/**
|
|
76
|
+
* The timestamp the pending sweep is due to run at.
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
_scheduledDueAt;
|
|
68
80
|
/**
|
|
69
81
|
* Handle for the pending idle-sweep timeout, or undefined if no timer is scheduled.
|
|
70
82
|
* @internal
|
|
@@ -103,6 +115,8 @@ export class LfuCache {
|
|
|
103
115
|
this._keyMap = new Map();
|
|
104
116
|
this._freqMap = new Map();
|
|
105
117
|
this._minFreq = 0;
|
|
118
|
+
this._nextExpires = undefined;
|
|
119
|
+
this._scheduledDueAt = 0;
|
|
106
120
|
this._sweepTimer = undefined;
|
|
107
121
|
}
|
|
108
122
|
/**
|
|
@@ -120,11 +134,12 @@ export class LfuCache {
|
|
|
120
134
|
* @returns The cached value, or undefined on a miss or idle eviction.
|
|
121
135
|
*/
|
|
122
136
|
get(key) {
|
|
137
|
+
Guards.stringValue(LfuCache.CLASS_NAME, "key", key);
|
|
123
138
|
const entry = this._keyMap.get(key);
|
|
124
|
-
if (entry
|
|
139
|
+
if (Is.empty(entry)) {
|
|
125
140
|
return undefined;
|
|
126
141
|
}
|
|
127
|
-
if (
|
|
142
|
+
if (this.isExpired(entry, Date.now())) {
|
|
128
143
|
this.removeEntry(key);
|
|
129
144
|
return undefined;
|
|
130
145
|
}
|
|
@@ -138,17 +153,26 @@ export class LfuCache {
|
|
|
138
153
|
* least-frequently-used entry is evicted (LRU among ties).
|
|
139
154
|
* @param key The key to store.
|
|
140
155
|
* @param value The value to cache.
|
|
156
|
+
* @param expires Hard expiry timestamp in milliseconds since the epoch. The entry is removed
|
|
157
|
+
* once this time is reached regardless of how recently it was used. Must be an integer.
|
|
141
158
|
*/
|
|
142
|
-
set(key, value) {
|
|
159
|
+
set(key, value, expires) {
|
|
160
|
+
Guards.stringValue(LfuCache.CLASS_NAME, "key", key);
|
|
161
|
+
if (!Is.empty(expires)) {
|
|
162
|
+
Guards.integer(LfuCache.CLASS_NAME, "expires", expires);
|
|
163
|
+
}
|
|
143
164
|
const existing = this._keyMap.get(key);
|
|
144
165
|
if (existing !== undefined) {
|
|
145
|
-
if (
|
|
146
|
-
// Idle: evict and fall through to add as a fresh entry
|
|
166
|
+
if (this.isExpired(existing, Date.now())) {
|
|
167
|
+
// Idle or expired: evict and fall through to add as a fresh entry
|
|
147
168
|
this.removeEntry(key);
|
|
148
169
|
}
|
|
149
170
|
else {
|
|
150
171
|
existing.value = value;
|
|
172
|
+
existing.expires = expires;
|
|
173
|
+
this.trackExpires(expires);
|
|
151
174
|
this.promote(key, existing);
|
|
175
|
+
this.startTimer();
|
|
152
176
|
return;
|
|
153
177
|
}
|
|
154
178
|
}
|
|
@@ -158,7 +182,7 @@ export class LfuCache {
|
|
|
158
182
|
if (this._keyMap.size >= this._capacity) {
|
|
159
183
|
this.evictLfu();
|
|
160
184
|
}
|
|
161
|
-
const entry = { value, freq: 1, lastAccessed: Date.now() };
|
|
185
|
+
const entry = { value, freq: 1, lastAccessed: Date.now(), expires };
|
|
162
186
|
this._keyMap.set(key, entry);
|
|
163
187
|
let bucket = this._freqMap.get(1);
|
|
164
188
|
if (bucket === undefined) {
|
|
@@ -167,6 +191,7 @@ export class LfuCache {
|
|
|
167
191
|
}
|
|
168
192
|
bucket.add(key);
|
|
169
193
|
this._minFreq = 1;
|
|
194
|
+
this.trackExpires(expires);
|
|
170
195
|
this.startTimer();
|
|
171
196
|
}
|
|
172
197
|
/**
|
|
@@ -174,11 +199,16 @@ export class LfuCache {
|
|
|
174
199
|
* Concurrent calls for the same key are serialized via a mutex.
|
|
175
200
|
* @param key The key to get or create.
|
|
176
201
|
* @param valueFactory Async callback used to build a value when the key is absent.
|
|
202
|
+
* @param expires Hard expiry timestamp in milliseconds since the epoch, applied to the entry
|
|
203
|
+
* when one is created. Must be an integer.
|
|
177
204
|
* @returns The existing or newly created value.
|
|
178
205
|
*/
|
|
179
|
-
async getOrSet(key, valueFactory) {
|
|
206
|
+
async getOrSet(key, valueFactory, expires) {
|
|
180
207
|
Guards.stringValue(LfuCache.CLASS_NAME, "key", key);
|
|
181
208
|
Guards.function(LfuCache.CLASS_NAME, "valueFactory", valueFactory);
|
|
209
|
+
if (!Is.empty(expires)) {
|
|
210
|
+
Guards.integer(LfuCache.CLASS_NAME, "expires", expires);
|
|
211
|
+
}
|
|
182
212
|
const mutexKey = `${this._mutexScope}:${key}`;
|
|
183
213
|
await Mutex.lock(mutexKey, {
|
|
184
214
|
timeoutMs: this._mutexTimeoutMs,
|
|
@@ -189,7 +219,7 @@ export class LfuCache {
|
|
|
189
219
|
return this.get(key);
|
|
190
220
|
}
|
|
191
221
|
const value = await valueFactory();
|
|
192
|
-
this.set(key, value);
|
|
222
|
+
this.set(key, value, expires);
|
|
193
223
|
return value;
|
|
194
224
|
}
|
|
195
225
|
finally {
|
|
@@ -203,11 +233,12 @@ export class LfuCache {
|
|
|
203
233
|
* @returns True if the key is present and not idle.
|
|
204
234
|
*/
|
|
205
235
|
has(key) {
|
|
236
|
+
Guards.stringValue(LfuCache.CLASS_NAME, "key", key);
|
|
206
237
|
const entry = this._keyMap.get(key);
|
|
207
238
|
if (entry === undefined) {
|
|
208
239
|
return false;
|
|
209
240
|
}
|
|
210
|
-
if (
|
|
241
|
+
if (this.isExpired(entry, Date.now())) {
|
|
211
242
|
this.removeEntry(key);
|
|
212
243
|
return false;
|
|
213
244
|
}
|
|
@@ -222,7 +253,7 @@ export class LfuCache {
|
|
|
222
253
|
keys() {
|
|
223
254
|
const now = Date.now();
|
|
224
255
|
for (const [k, entry] of this._keyMap) {
|
|
225
|
-
if (
|
|
256
|
+
if (this.isExpired(entry, now)) {
|
|
226
257
|
this.removeEntry(k);
|
|
227
258
|
}
|
|
228
259
|
}
|
|
@@ -244,6 +275,7 @@ export class LfuCache {
|
|
|
244
275
|
* @param key The key to remove.
|
|
245
276
|
*/
|
|
246
277
|
delete(key) {
|
|
278
|
+
Guards.stringValue(LfuCache.CLASS_NAME, "key", key);
|
|
247
279
|
this.removeEntry(key);
|
|
248
280
|
if (this._keyMap.size === 0) {
|
|
249
281
|
this.cancelTimer();
|
|
@@ -257,6 +289,7 @@ export class LfuCache {
|
|
|
257
289
|
this._keyMap.clear();
|
|
258
290
|
this._freqMap.clear();
|
|
259
291
|
this._minFreq = 0;
|
|
292
|
+
this._nextExpires = undefined;
|
|
260
293
|
}
|
|
261
294
|
/**
|
|
262
295
|
* Stop the background idle-sweep timer and release all entries.
|
|
@@ -266,6 +299,8 @@ export class LfuCache {
|
|
|
266
299
|
this.cancelTimer();
|
|
267
300
|
this._keyMap.clear();
|
|
268
301
|
this._freqMap.clear();
|
|
302
|
+
this._minFreq = 0;
|
|
303
|
+
this._nextExpires = undefined;
|
|
269
304
|
}
|
|
270
305
|
/**
|
|
271
306
|
* Increment the frequency of an entry and move it to the correct frequency bucket.
|
|
@@ -275,6 +310,7 @@ export class LfuCache {
|
|
|
275
310
|
* @param entry.value The cached value.
|
|
276
311
|
* @param entry.freq The current access frequency.
|
|
277
312
|
* @param entry.lastAccessed The last-accessed timestamp in milliseconds.
|
|
313
|
+
* @param entry.expires The hard expiry timestamp in milliseconds, or undefined for none.
|
|
278
314
|
* @internal
|
|
279
315
|
*/
|
|
280
316
|
promote(key, entry) {
|
|
@@ -350,21 +386,63 @@ export class LfuCache {
|
|
|
350
386
|
sweepIdle() {
|
|
351
387
|
this.cancelTimer();
|
|
352
388
|
const now = Date.now();
|
|
389
|
+
let nextExpires;
|
|
353
390
|
for (const [k, entry] of this._keyMap) {
|
|
354
|
-
if (
|
|
391
|
+
if (this.isExpired(entry, now)) {
|
|
355
392
|
this.removeEntry(k);
|
|
356
393
|
}
|
|
394
|
+
else if (Is.notEmpty(entry.expires) &&
|
|
395
|
+
(Is.empty(nextExpires) || entry.expires < nextExpires)) {
|
|
396
|
+
nextExpires = entry.expires;
|
|
397
|
+
}
|
|
357
398
|
}
|
|
399
|
+
this._nextExpires = nextExpires;
|
|
358
400
|
if (this._keyMap.size > 0) {
|
|
359
401
|
this.startTimer();
|
|
360
402
|
}
|
|
361
403
|
}
|
|
362
404
|
/**
|
|
363
|
-
*
|
|
405
|
+
* Record an entry expiry timestamp if it is earlier than the currently tracked one.
|
|
406
|
+
* @param expires The expiry timestamp in milliseconds, or undefined for none.
|
|
407
|
+
* @internal
|
|
408
|
+
*/
|
|
409
|
+
trackExpires(expires) {
|
|
410
|
+
if (Is.notEmpty(expires) && (Is.empty(this._nextExpires) || expires < this._nextExpires)) {
|
|
411
|
+
this._nextExpires = expires;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Determine whether an entry has idled out or reached its hard expiry timestamp.
|
|
416
|
+
* @param entry The entry to test.
|
|
417
|
+
* @param entry.lastAccessed The last-accessed timestamp in milliseconds.
|
|
418
|
+
* @param entry.expires The hard expiry timestamp in milliseconds, or undefined for none.
|
|
419
|
+
* @param now The current time in milliseconds.
|
|
420
|
+
* @returns True if the entry should be removed.
|
|
421
|
+
* @internal
|
|
422
|
+
*/
|
|
423
|
+
isExpired(entry, now) {
|
|
424
|
+
return (now - entry.lastAccessed >= this._ttiMs ||
|
|
425
|
+
(Is.notEmpty(entry.expires) && now >= entry.expires));
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Schedule the next sweep if no timer is already pending, bringing a pending one forward
|
|
429
|
+
* when an entry with an earlier hard expiry has since been added.
|
|
364
430
|
* @internal
|
|
365
431
|
*/
|
|
366
432
|
startTimer() {
|
|
367
|
-
|
|
433
|
+
const now = Date.now();
|
|
434
|
+
let delay = this._ttiMs;
|
|
435
|
+
if (Is.notEmpty(this._nextExpires)) {
|
|
436
|
+
delay = Math.min(delay, Math.max(0, this._nextExpires - now));
|
|
437
|
+
}
|
|
438
|
+
if (Is.empty(this._sweepTimer)) {
|
|
439
|
+
this._scheduledDueAt = now + delay;
|
|
440
|
+
this._sweepTimer = setTimeout(() => this.sweepIdle(), delay);
|
|
441
|
+
}
|
|
442
|
+
else if (now + delay < this._scheduledDueAt) {
|
|
443
|
+
this.cancelTimer();
|
|
444
|
+
this.startTimer();
|
|
445
|
+
}
|
|
368
446
|
}
|
|
369
447
|
/**
|
|
370
448
|
* Cancel the pending idle-sweep timer.
|