tsoa
This commit is contained in:
11
node_modules/@hapi/catbox/LICENSE.md
generated
vendored
Executable file
11
node_modules/@hapi/catbox/LICENSE.md
generated
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
Copyright (c) 2012-2022, Project contributors
|
||||
Copyright (c) 2012-2020, Sideway Inc
|
||||
Copyright (c) 2012-2014, Walmart.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS OFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
17
node_modules/@hapi/catbox/README.md
generated
vendored
Executable file
17
node_modules/@hapi/catbox/README.md
generated
vendored
Executable file
@@ -0,0 +1,17 @@
|
||||
<a href="https://hapi.dev"><img src="https://raw.githubusercontent.com/hapijs/assets/master/images/family.png" width="180px" align="right" /></a>
|
||||
|
||||
# @hapi/catbox
|
||||
|
||||
#### Multi-strategy object caching service.
|
||||
|
||||
**catbox** is part of the **hapi** ecosystem and was designed to work seamlessly with the [hapi web framework](https://hapi.dev) and its other components (but works great on its own or with other frameworks). If you are using a different web framework and find this module useful, check out [hapi](https://hapi.dev) – they work even better together.
|
||||
|
||||
### Visit the [hapi.dev](https://hapi.dev) Developer Portal for tutorials, documentation, and support
|
||||
|
||||
## Useful resources
|
||||
|
||||
- [Documentation and API](https://hapi.dev/family/catbox/)
|
||||
- [Versions status](https://hapi.dev/resources/status/#catbox) (builds, dependencies, node versions, licenses, eol)
|
||||
- [Changelog](https://hapi.dev/family/catbox/changelog/)
|
||||
- [Project policies](https://hapi.dev/policies/)
|
||||
- [Free and commercial support options](https://hapi.dev/support/)
|
||||
114
node_modules/@hapi/catbox/lib/client.js
generated
vendored
Executable file
114
node_modules/@hapi/catbox/lib/client.js
generated
vendored
Executable file
@@ -0,0 +1,114 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
const Boom = require('@hapi/boom');
|
||||
|
||||
|
||||
const internals = {
|
||||
validate: Symbol('validate')
|
||||
};
|
||||
|
||||
|
||||
internals.defaults = {
|
||||
partition: 'catbox'
|
||||
};
|
||||
|
||||
|
||||
module.exports = class {
|
||||
|
||||
constructor(engine, options) {
|
||||
|
||||
Hoek.assert(engine, 'Missing catbox client engine');
|
||||
Hoek.assert((typeof engine === 'object' && typeof engine.start === 'function') || typeof engine === 'function', 'engine must be an engine object or engine prototype (function)');
|
||||
Hoek.assert(typeof engine === 'function' || !options, 'Can only specify options with function engine config');
|
||||
|
||||
const settings = Object.assign({}, internals.defaults, options);
|
||||
Hoek.assert(settings.partition.match(/^[\w\-]+$/), 'Invalid partition name:' + settings.partition);
|
||||
|
||||
this.connection = (typeof engine === 'object' ? engine : new engine(settings));
|
||||
}
|
||||
|
||||
async start() {
|
||||
|
||||
await this.connection.start();
|
||||
}
|
||||
|
||||
async stop() {
|
||||
|
||||
await this.connection.stop();
|
||||
}
|
||||
|
||||
isReady() {
|
||||
|
||||
return this.connection.isReady();
|
||||
}
|
||||
|
||||
validateSegmentName(name) {
|
||||
|
||||
return this.connection.validateSegmentName(name);
|
||||
}
|
||||
|
||||
async get(key) {
|
||||
|
||||
this[internals.validate](key, null);
|
||||
|
||||
if (key === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const result = await this.connection.get(key);
|
||||
if (!result ||
|
||||
result.item === undefined ||
|
||||
result.item === null) {
|
||||
|
||||
return null; // Not found
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const expires = result.stored + result.ttl;
|
||||
const ttl = expires - now;
|
||||
if (ttl <= 0) {
|
||||
return null; // Expired
|
||||
}
|
||||
|
||||
const cached = {
|
||||
item: result.item,
|
||||
stored: result.stored,
|
||||
ttl
|
||||
};
|
||||
|
||||
return cached; // Valid
|
||||
}
|
||||
|
||||
async set(key, value, ttl) {
|
||||
|
||||
this[internals.validate](key);
|
||||
|
||||
if (ttl <= 0) {
|
||||
return; // Not cachable (or bad rules)
|
||||
}
|
||||
|
||||
await this.connection.set(key, value, ttl);
|
||||
}
|
||||
|
||||
async drop(key) {
|
||||
|
||||
this[internals.validate](key);
|
||||
|
||||
await this.connection.drop(key); // Always drop, regardless of caching rules
|
||||
}
|
||||
|
||||
[internals.validate](key, allow = {}) {
|
||||
|
||||
if (!this.isReady()) {
|
||||
throw Boom.internal('Disconnected'); // Disconnected
|
||||
}
|
||||
|
||||
const isValidKey = (key && typeof key.id === 'string' &&
|
||||
key.segment && typeof key.segment === 'string');
|
||||
|
||||
if (!isValidKey && key !== allow) {
|
||||
throw Boom.internal('Invalid key');
|
||||
}
|
||||
}
|
||||
};
|
||||
299
node_modules/@hapi/catbox/lib/index.d.ts
generated
vendored
Normal file
299
node_modules/@hapi/catbox/lib/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
// TypeScript Version: 4.9
|
||||
|
||||
/// <reference types="node" />
|
||||
|
||||
import { Podium } from "@hapi/podium";
|
||||
|
||||
/**
|
||||
* Client
|
||||
* The Client object provides a low-level cache abstraction. The object is constructed using new Client(engine, options) where:
|
||||
* engine - is an object or a prototype function implementing the cache strategy:
|
||||
* * function - a prototype function with the signature function(options). catbox will call new func(options).
|
||||
* * object - a pre instantiated client implementation object. Does not support passing options.
|
||||
* options - the strategy configuration object. Each strategy defines its own configuration options with the following common options:
|
||||
* * partition - the partition name used to isolate the cached results across multiple clients. The partition name is used as the MongoDB database name,
|
||||
* the Riak bucket, or as a key prefix in Redis and Memcached. To share the cache across multiple clients, use the same partition name.
|
||||
* @see {@link https://github.com/hapijs/catbox#client}
|
||||
*/
|
||||
export class Client<T> implements ClientApi<T> {
|
||||
constructor(engine: ClientApi<any>);
|
||||
constructor(engine: EnginePrototype<any>, options?: ClientOptions);
|
||||
|
||||
/** start() - creates a connection to the cache server. Must be called before any other method is available. */
|
||||
start(): Promise<void>;
|
||||
/** stop() - terminates the connection to the cache server. */
|
||||
stop(): Promise<void>;
|
||||
/**
|
||||
* get(key, callback) - retrieve an item from the cache engine if found where:
|
||||
* * key - a cache key object (see [ICacheKey]).
|
||||
*/
|
||||
get(key: CacheKey): Promise<null | CachedObject<T>>;
|
||||
/**
|
||||
* set(key, value, ttl, callback) - store an item in the cache for a specified length of time, where:
|
||||
* * key - a cache key object (see [ICacheKey]).
|
||||
* * value - the string or object value to be stored.
|
||||
* * ttl - a time-to-live value in milliseconds after which the item is automatically removed from the cache (or is marked invalid).
|
||||
*/
|
||||
set(key: CacheKey, value: T, ttl: number): Promise<void>;
|
||||
/**
|
||||
* drop(key, callback) - remove an item from cache where:
|
||||
* * key - a cache key object (see [ICacheKey]).
|
||||
*/
|
||||
drop(key: CacheKey): Promise<void>;
|
||||
/** isReady() - returns true if cache engine determines itself as ready, false if it is not ready. */
|
||||
isReady(): boolean;
|
||||
/** validateSegmentName(segment) - returns null if the segment name is valid (see below), otherwise should return an instance of Error with an appropriate message. */
|
||||
validateSegmentName(segment: string): null | Error;
|
||||
}
|
||||
|
||||
/**
|
||||
* A prototype CatBox engine function
|
||||
*/
|
||||
export interface EnginePrototype<T> {
|
||||
new(settings: ClientOptions): ClientApi<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client API
|
||||
* The Client object provides the following methods:
|
||||
* @see {@link https://github.com/hapijs/catbox#api}
|
||||
*/
|
||||
export interface ClientApi<T> {
|
||||
/** start() - creates a connection to the cache server. Must be called before any other method is available. */
|
||||
start(): Promise<void>;
|
||||
/** stop() - terminates the connection to the cache server. */
|
||||
stop(): void;
|
||||
/**
|
||||
* get(key, callback) - retrieve an item from the cache engine if found where:
|
||||
* * key - a cache key object (see [ICacheKey]).
|
||||
*/
|
||||
get(key: CacheKey): Promise<null | CachedObject<T>>;
|
||||
/**
|
||||
* set(key, value, ttl) - store an item in the cache for a specified length of time, where:
|
||||
* * key - a cache key object (see [ICacheKey]).
|
||||
* * value - the string or object value to be stored.
|
||||
* * ttl - a time-to-live value in milliseconds after which the item is automatically removed from the cache (or is marked invalid).
|
||||
*/
|
||||
set(key: CacheKey, value: T, ttl: number): Promise<void>;
|
||||
/**
|
||||
* drop(key) - remove an item from cache where:
|
||||
* * key - a cache key object (see [ICacheKey]).
|
||||
*/
|
||||
drop(key: CacheKey): Promise<void>;
|
||||
/** isReady() - returns true if cache engine determines itself as ready, false if it is not ready. */
|
||||
isReady(): boolean;
|
||||
/** validateSegmentName(segment) - returns null if the segment name is valid (see below), otherwise should return an instance of Error with an appropriate message. */
|
||||
validateSegmentName(segment: string): null | Error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Any method with a key argument takes an object with the following required properties:
|
||||
*/
|
||||
export interface CacheKey {
|
||||
/** segment - a caching segment name string. Enables using a single cache server for storing different sets of items with overlapping ids. */
|
||||
segment: string;
|
||||
/** id - a unique item identifier string (per segment). Can be an empty string. */
|
||||
id: string;
|
||||
}
|
||||
|
||||
/** Cached object contains the following: */
|
||||
export interface CachedObject<T> {
|
||||
/** item - the value stored in the cache using set(). */
|
||||
item: T;
|
||||
/** stored - the timestamp when the item was stored in the cache (in milliseconds). */
|
||||
stored: number;
|
||||
/** ttl - the remaining time-to-live (not the original value used when storing the object). */
|
||||
ttl: number;
|
||||
}
|
||||
|
||||
export interface ClientOptions {
|
||||
/**
|
||||
* this will store items under keys that start with this value.
|
||||
*/
|
||||
partition?: string | undefined;
|
||||
}
|
||||
|
||||
export type PolicyOptionVariants<T> = PolicyOptions<T> | DecoratedPolicyOptions<T>;
|
||||
|
||||
export type Id = string | { id: string };
|
||||
|
||||
/**
|
||||
* The Policy object provides a convenient cache interface by setting a
|
||||
* global policy which is automatically applied to every storage action.
|
||||
* The object is constructed using new Policy(options, [cache, segment]) where:
|
||||
* * options - an object with the IPolicyOptions structure
|
||||
* * cache - a Client instance (which has already been started).
|
||||
* * segment - required when cache is provided. The segment name used to
|
||||
* isolate cached items within the cache partition.
|
||||
* @see {@link https://github.com/hapijs/catbox#policy}
|
||||
*/
|
||||
export class Policy<T, O extends PolicyOptionVariants<T>> {
|
||||
constructor(options: O, cache: Client<T>, segment: string);
|
||||
/**
|
||||
* retrieve an item from the cache. If the item is not
|
||||
* found and the generateFunc method was provided,
|
||||
* a new value is generated, stored in the cache, and returned.
|
||||
* Multiple concurrent requests are queued and processed once. The method arguments are:
|
||||
* @param id the unique item identifier (within the policy segment).
|
||||
* Can be a string or an object with the required 'id' key.
|
||||
*/
|
||||
get(id: Id): Promise<O extends DecoratedPolicyOptions<T> ? DecoratedResult<T> : T | null>;
|
||||
|
||||
/**
|
||||
* store an item in the cache where:
|
||||
* @param id - the unique item identifier (within the policy segment).
|
||||
* @param value - the string or object value to be stored.
|
||||
* @param ttl - a time-to-live override value in milliseconds after which the item is automatically
|
||||
* removed from the cache (or is marked invalid).
|
||||
* This should be set to 0 in order to use the caching rules configured when creating the Policy object.
|
||||
*/
|
||||
set(id: Id, value: T, ttl?: number): Promise<void>;
|
||||
/**
|
||||
* remove the item from cache where:
|
||||
* @param id the unique item identifier (within the policy segment).
|
||||
*/
|
||||
drop(id: Id): Promise<void>;
|
||||
/**
|
||||
* given a created timestamp in milliseconds, returns the time-to-live left
|
||||
* based on the configured rules.
|
||||
*/
|
||||
ttl(created: number): number;
|
||||
/** changes the policy rules after construction (note that items already stored will not be affected) */
|
||||
rules(options: PolicyOptions<T>): void;
|
||||
/**
|
||||
* returns true if cache engine determines itself as ready, false if it is not ready or if
|
||||
* here is no cache engine set.
|
||||
*/
|
||||
isReady(): boolean;
|
||||
/** an object with cache statistics */
|
||||
stats: CacheStatisticsObject;
|
||||
/**
|
||||
* a podium event emitter, emitting 'error' events under the 'persist' (emits any cache errors
|
||||
* thrown during the generation of new values as a result of a get() request) and 'generate'
|
||||
* (emits any new value generation errors thrown as a result of a get() request) channels.
|
||||
*/
|
||||
events: Podium;
|
||||
/**
|
||||
* a reference to the cache client if set.
|
||||
*/
|
||||
client: Client<T>;
|
||||
}
|
||||
|
||||
export interface DecoratedResult<T> {
|
||||
value: T;
|
||||
cached: PolicyGetCachedOptions<T>;
|
||||
report: PolicyGetReportLog;
|
||||
}
|
||||
|
||||
export interface PolicyGetCachedOptions<T> {
|
||||
/** item - the cached value. */
|
||||
item: T;
|
||||
/** stored - the timestamp when the item was stored in the cache. */
|
||||
stored: number;
|
||||
/** ttl - the cache ttl value for the record. */
|
||||
ttl: number;
|
||||
/** isStale - true if the item is stale. */
|
||||
isStale: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link https://github.com/hapijs/catbox#policy}
|
||||
*/
|
||||
export interface PolicyOptions<T> {
|
||||
/** expiresIn - relative expiration expressed in the number of milliseconds since the item was saved in the cache. Cannot be used together with expiresAt. */
|
||||
expiresIn?: number | undefined;
|
||||
/** expiresAt - time of day expressed in 24h notation using the 'HH:MM' format, at which point all cache records for the route expire. Uses local time. Cannot be used together with expiresIn. */
|
||||
expiresAt?: string | undefined;
|
||||
/** generateFunc - a function used to generate a new cache item if one is not found in the cache when calling get(). The method's signature is function(id, next) where: */
|
||||
generateFunc?: GenerateFunc<T> | undefined;
|
||||
/**
|
||||
* staleIn - number of milliseconds to mark an item stored in cache as stale and attempt to regenerate it when generateFunc is provided.
|
||||
* Must be less than expiresIn. Alternatively function that returns staleIn value in milliseconds. The function signature is function(stored, ttl) where:
|
||||
* * stored - the timestamp when the item was stored in the cache (in milliseconds).
|
||||
* * ttl - the remaining time-to-live (not the original value used when storing the object).
|
||||
*/
|
||||
staleIn?: number | ((stored: number, ttl: number) => number) | undefined;
|
||||
/** staleTimeout - number of milliseconds to wait before returning a stale value while generateFunc is generating a fresh value. */
|
||||
staleTimeout?: number | undefined;
|
||||
/**
|
||||
* generateTimeout - number of milliseconds to wait before returning a timeout error when the generateFunc function takes too long to return a value.
|
||||
* When the value is eventually returned, it is stored in the cache for future requests. Required if generateFunc is present.
|
||||
* Set to false to disable timeouts which may cause all get() requests to get stuck forever.
|
||||
*/
|
||||
generateTimeout?: number | false | undefined;
|
||||
/** dropOnError - if true, an error or timeout in the generateFunc causes the stale value to be evicted from the cache. Defaults to true. */
|
||||
dropOnError?: boolean | undefined;
|
||||
/** generateOnReadError - if false, an upstream cache read error will stop the get() method from calling the generate function and will instead pass back the cache error. Defaults to true. */
|
||||
generateOnReadError?: boolean | undefined;
|
||||
/** generateIgnoreWriteError - if false, an upstream cache write error will be passed back with the generated value when calling the get() method. Defaults to true. */
|
||||
generateIgnoreWriteError?: boolean | undefined;
|
||||
/**
|
||||
* pendingGenerateTimeout - number of milliseconds while generateFunc call is in progress for a given id, before a subsequent generateFunc call is allowed.
|
||||
* @default 0, no blocking of concurrent generateFunc calls beyond staleTimeout.
|
||||
*/
|
||||
pendingGenerateTimeout?: number | undefined;
|
||||
}
|
||||
|
||||
export interface DecoratedPolicyOptions<T> extends PolicyOptions<T> {
|
||||
/**
|
||||
* @default false
|
||||
*/
|
||||
getDecoratedValue: boolean | undefined;
|
||||
}
|
||||
|
||||
export interface GenerateFuncFlags {
|
||||
ttl: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* generateFunc
|
||||
* Is used in PolicyOptions
|
||||
* A function used to generate a new cache item if one is not found in the cache when calling get(). The method's signature is function(id)
|
||||
* @param id - the id string or object provided to the get() method.
|
||||
* @param next - the method called when the new item is returned with the signature function(err, value, ttl) where:
|
||||
* * err - an error condition.
|
||||
* * value - the new value generated.
|
||||
* * ttl - the cache ttl value in milliseconds. Set to 0 to skip storing in the cache. Defaults to the cache global policy.
|
||||
* @see {@link https://github.com/hapijs/catbox#policy}
|
||||
*/
|
||||
export type GenerateFunc<T> = (id: Id, flags: GenerateFuncFlags) => Promise<T>;
|
||||
|
||||
/**
|
||||
* An object with logging information about the generation operation containing the following keys (as relevant):
|
||||
*/
|
||||
export interface PolicyGetReportLog {
|
||||
/** msec - the cache lookup time in milliseconds. */
|
||||
msec: number;
|
||||
/** stored - the timestamp when the item was stored in the cache. */
|
||||
stored: number;
|
||||
/** isStale - true if the item is stale. */
|
||||
isStale: boolean;
|
||||
/** ttl - the cache ttl value for the record. */
|
||||
ttl: number;
|
||||
/** error - lookup error. */
|
||||
error?: Error | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* an object with cache statistics where:
|
||||
*/
|
||||
export interface CacheStatisticsObject {
|
||||
/** sets - number of cache writes. */
|
||||
sets: number;
|
||||
/** gets - number of cache get() requests. */
|
||||
gets: number;
|
||||
/** hits - number of cache get() requests in which the requested id was found in the cache (can be stale). */
|
||||
hits: number;
|
||||
/** stales - number of cache reads with stale requests (only counts the first request in a queued get() operation). */
|
||||
stales: number;
|
||||
/** generates - number of calls to the generate function. */
|
||||
generates: number;
|
||||
/** errors - cache operations errors. TODO check this */
|
||||
errors: number;
|
||||
}
|
||||
|
||||
// Definitions adapted from DefinitelyTyped, originally created by:
|
||||
// Jason Swearingen <https://github.com/jasonswearingen>
|
||||
// AJP <https://github.com/AJamesPhillips>
|
||||
// Rodrigo Saboya <https://github.com/saboya>
|
||||
// Silas Rech <https://github.com/lenovouser>
|
||||
13
node_modules/@hapi/catbox/lib/index.js
generated
vendored
Executable file
13
node_modules/@hapi/catbox/lib/index.js
generated
vendored
Executable file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const Client = require('./client');
|
||||
const Policy = require('./policy');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports.Client = Client;
|
||||
|
||||
|
||||
exports.Policy = exports.policy = Policy;
|
||||
61
node_modules/@hapi/catbox/lib/pending.js
generated
vendored
Executable file
61
node_modules/@hapi/catbox/lib/pending.js
generated
vendored
Executable file
@@ -0,0 +1,61 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports = module.exports = class {
|
||||
|
||||
id = null;
|
||||
timeout = null;
|
||||
count = 1;
|
||||
rule = null;
|
||||
resolve = null;
|
||||
reject = null;
|
||||
|
||||
constructor(id, rule) {
|
||||
|
||||
this.id = id;
|
||||
this.rule = rule;
|
||||
|
||||
this.promise = new Promise((resolve, reject) => {
|
||||
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
});
|
||||
}
|
||||
|
||||
join() {
|
||||
|
||||
++this.count;
|
||||
return this.promise;
|
||||
}
|
||||
|
||||
send(err, value, cached, report) {
|
||||
|
||||
clearTimeout(this.timeout);
|
||||
|
||||
if (err &&
|
||||
!cached) {
|
||||
|
||||
this.reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.rule.getDecoratedValue) {
|
||||
this.resolve(value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
report.error = err;
|
||||
}
|
||||
|
||||
this.resolve({ value, cached, report });
|
||||
}
|
||||
|
||||
setTimeout(fn, timeoutMs) {
|
||||
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(fn, timeoutMs);
|
||||
}
|
||||
};
|
||||
478
node_modules/@hapi/catbox/lib/policy.js
generated
vendored
Executable file
478
node_modules/@hapi/catbox/lib/policy.js
generated
vendored
Executable file
@@ -0,0 +1,478 @@
|
||||
'use strict';
|
||||
|
||||
const Boom = require('@hapi/boom');
|
||||
const Hoek = require('@hapi/hoek');
|
||||
const Podium = require('@hapi/podium');
|
||||
const Validate = require('@hapi/validate');
|
||||
|
||||
const Pending = require('./pending');
|
||||
|
||||
|
||||
const internals = {
|
||||
day: 24 * 60 * 60 * 1000,
|
||||
events: Podium.validate([
|
||||
{ name: 'error', channels: ['generate', 'persist'] }
|
||||
])
|
||||
};
|
||||
|
||||
|
||||
internals.schema = Validate.object({
|
||||
expiresIn: Validate.number().integer().min(1),
|
||||
expiresAt: Validate.string().regex(/^\d\d?\:\d\d$/),
|
||||
staleIn: [
|
||||
Validate.number().integer().min(1).when('expiresAt', { is: Validate.required(), then: Validate.number().max(86400000 - 1) }), // One day - 1 (max is inclusive)
|
||||
Validate.func()
|
||||
],
|
||||
staleTimeout: Validate.number().integer().min(1),
|
||||
generateFunc: Validate.func(),
|
||||
generateTimeout: Validate.number().integer().min(1).allow(false),
|
||||
generateOnReadError: Validate.boolean(),
|
||||
generateIgnoreWriteError: Validate.boolean(),
|
||||
dropOnError: Validate.boolean(),
|
||||
pendingGenerateTimeout: Validate.number().integer().min(1),
|
||||
getDecoratedValue: Validate.boolean().default(false),
|
||||
|
||||
// Ignored external keys (hapi)
|
||||
|
||||
privacy: Validate.any(),
|
||||
cache: Validate.any(),
|
||||
segment: Validate.any(),
|
||||
shared: Validate.any()
|
||||
})
|
||||
.without('expiresIn', 'expiresAt')
|
||||
.with('staleIn', 'generateFunc')
|
||||
.with('generateOnReadError', 'generateFunc')
|
||||
.with('generateIgnoreWriteError', 'generateFunc')
|
||||
.with('dropOnError', 'generateFunc')
|
||||
.and('generateFunc', 'generateTimeout')
|
||||
.and('staleIn', 'staleTimeout');
|
||||
|
||||
|
||||
exports = module.exports = internals.Policy = class {
|
||||
|
||||
rule = null;
|
||||
stats = {
|
||||
sets: 0,
|
||||
gets: 0,
|
||||
hits: 0,
|
||||
stales: 0,
|
||||
generates: 0,
|
||||
errors: 0
|
||||
};
|
||||
|
||||
_events = null;
|
||||
_cache = null;
|
||||
_segment = null;
|
||||
_pendings = new Map(); // id -> Pending
|
||||
_pendingGenerateCall = new Map(); // id -> timer
|
||||
|
||||
constructor(options, cache, segment) {
|
||||
|
||||
this._cache = cache;
|
||||
this.rules(options);
|
||||
|
||||
if (cache) {
|
||||
const nameErr = cache.validateSegmentName(segment);
|
||||
Hoek.assert(nameErr === null, 'Invalid segment name: ' + segment + (nameErr ? ' (' + nameErr.message + ')' : ''));
|
||||
|
||||
this._segment = segment;
|
||||
}
|
||||
}
|
||||
|
||||
get client() {
|
||||
|
||||
return this._cache;
|
||||
}
|
||||
|
||||
get events() {
|
||||
|
||||
if (!this._events) {
|
||||
this._events = new Podium.Podium(internals.events, { validate: false });
|
||||
}
|
||||
|
||||
return this._events;
|
||||
}
|
||||
|
||||
_error(source, error) {
|
||||
|
||||
if (!this._events) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._events.emit({ name: 'error', channel: source }, { source, error });
|
||||
}
|
||||
|
||||
rules(options) {
|
||||
|
||||
this.rule = internals.Policy.compile(options, !!this._cache);
|
||||
}
|
||||
|
||||
async get(key) { // key: string or { id: 'id' }
|
||||
|
||||
++this.stats.gets;
|
||||
|
||||
// Check if request is already pending
|
||||
|
||||
if (!key ||
|
||||
typeof key === 'string') {
|
||||
|
||||
key = { id: key, string: true };
|
||||
}
|
||||
|
||||
let pending = this._pendings.get(key.id);
|
||||
if (pending !== undefined) {
|
||||
return pending.join();
|
||||
}
|
||||
|
||||
pending = new Pending(key.id, this.rule);
|
||||
this._pendings.set(key.id, pending);
|
||||
|
||||
try {
|
||||
await this._get(pending, key);
|
||||
}
|
||||
catch (err) {
|
||||
this._send(key, err); // Safeguard to ensure that the pending rejects on any processing errors
|
||||
}
|
||||
|
||||
return pending.promise;
|
||||
}
|
||||
|
||||
async _get(pending, key) {
|
||||
|
||||
// Prepare report
|
||||
|
||||
const report = {};
|
||||
|
||||
// Lookup in cache
|
||||
|
||||
const timer = new Hoek.Bench();
|
||||
|
||||
if (this._cache) {
|
||||
try {
|
||||
var cached = await this._cache.get({ segment: this._segment, id: key.id });
|
||||
}
|
||||
catch (err) {
|
||||
report.error = err;
|
||||
++this.stats.errors;
|
||||
this._error('persist', err);
|
||||
}
|
||||
}
|
||||
|
||||
report.msec = timer.elapsed();
|
||||
|
||||
if (cached) {
|
||||
report.stored = cached.stored;
|
||||
report.ttl = cached.ttl;
|
||||
const staleIn = typeof this.rule.staleIn === 'function' ? this.rule.staleIn(cached.stored, cached.ttl) : this.rule.staleIn;
|
||||
cached.isStale = staleIn ? Date.now() - cached.stored >= staleIn : false;
|
||||
report.isStale = cached.isStale;
|
||||
|
||||
if (cached.isStale) {
|
||||
++this.stats.stales;
|
||||
}
|
||||
}
|
||||
|
||||
// No generate method
|
||||
|
||||
if (!this.rule.generateFunc ||
|
||||
report.error && !this.rule.generateOnReadError) {
|
||||
|
||||
this._send(key, report.error, cached ? cached.item : null, cached, report);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if found and fresh
|
||||
|
||||
if (cached &&
|
||||
!cached.isStale) {
|
||||
|
||||
this._send(key, null, cached.item, cached, report);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait until generated or otherwise resolved
|
||||
|
||||
return Promise.race([
|
||||
pending.promise,
|
||||
this._generate(pending, key, cached, report)
|
||||
]);
|
||||
}
|
||||
|
||||
_generate(pending, key, cached, report) {
|
||||
|
||||
if (cached) { // Must be stale
|
||||
|
||||
// Set stale timeout
|
||||
|
||||
cached.ttl = cached.ttl - this.rule.staleTimeout; // Adjust TTL for when the timeout is invoked (staleTimeout must be valid if isStale is true)
|
||||
}
|
||||
|
||||
if (cached &&
|
||||
cached.ttl > 0) {
|
||||
|
||||
pending.setTimeout(() => this._send(key, null, cached.item, cached, report), this.rule.staleTimeout);
|
||||
}
|
||||
else if (this.rule.generateTimeout) {
|
||||
|
||||
// Set item generation timeout (when not in cache)
|
||||
|
||||
pending.setTimeout(() => this._send(key, Boom.serverUnavailable(), null, null, report), this.rule.generateTimeout);
|
||||
}
|
||||
|
||||
// Check if a generate call is already in progress
|
||||
|
||||
if (this._pendingGenerateCall.has(key.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate new value
|
||||
|
||||
++this.stats.generates; // Record generation before call in case it times out
|
||||
|
||||
if (this.rule.pendingGenerateTimeout) {
|
||||
const timeout = setTimeout(() => this._pendingGenerateCall.delete(key.id), this.rule.pendingGenerateTimeout);
|
||||
this._pendingGenerateCall.set(key.id, timeout);
|
||||
}
|
||||
|
||||
return this._callGenerateFunc(key, cached, report);
|
||||
}
|
||||
|
||||
async _callGenerateFunc(key, cached, report) {
|
||||
|
||||
const flags = {};
|
||||
|
||||
try {
|
||||
var value = await this.rule.generateFunc(key.string ? key.id : key, flags);
|
||||
}
|
||||
catch (err) {
|
||||
var generateError = err;
|
||||
this._error('generate', err);
|
||||
}
|
||||
|
||||
const pendingTimeout = this._pendingGenerateCall.get(key.id);
|
||||
if (pendingTimeout) {
|
||||
clearTimeout(pendingTimeout);
|
||||
this._pendingGenerateCall.delete(key.id);
|
||||
}
|
||||
|
||||
// Error (if dropOnError is not set to false) or not cached
|
||||
|
||||
try {
|
||||
if (flags.ttl === 0 || // null or undefined means use policy
|
||||
generateError && this.rule.dropOnError) {
|
||||
|
||||
await this.drop(key.id); // Invalidate cache
|
||||
}
|
||||
else if (!generateError) {
|
||||
await this.set(key.id, value, flags.ttl); // Replace stale cache copy with late-coming fresh copy
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
var persistError = err;
|
||||
this._error('persist', err);
|
||||
}
|
||||
|
||||
const error = generateError || (this.rule.generateIgnoreWriteError ? null : persistError);
|
||||
if (cached &&
|
||||
error &&
|
||||
!this.rule.dropOnError) {
|
||||
|
||||
this._send(key, error, cached.item, cached, report);
|
||||
return;
|
||||
}
|
||||
|
||||
this._send(key, error, value, null, report); // Ignored if stale value already returned
|
||||
}
|
||||
|
||||
_send(key, err, value, cached, report) {
|
||||
|
||||
const pending = this._pendings.get(key.id);
|
||||
if (!pending) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._pendings.delete(key.id);
|
||||
pending.send(err, value, cached, report);
|
||||
|
||||
if (report?.isStale !== undefined) {
|
||||
this.stats.hits = this.stats.hits + pending.count;
|
||||
}
|
||||
}
|
||||
|
||||
async set(key, value, ttl) {
|
||||
|
||||
++this.stats.sets;
|
||||
|
||||
if (!this._cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this._cache.set({ segment: this._segment, id: internals.id(key) }, value, ttl || internals.Policy.ttl(this.rule));
|
||||
}
|
||||
catch (err) {
|
||||
++this.stats.errors;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async drop(key) {
|
||||
|
||||
if (!this._cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this._cache.drop({ segment: this._segment, id: internals.id(key) });
|
||||
return;
|
||||
}
|
||||
catch (err) {
|
||||
++this.stats.errors;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
ttl(created) {
|
||||
|
||||
return internals.Policy.ttl(this.rule, created);
|
||||
}
|
||||
|
||||
isReady() {
|
||||
|
||||
if (!this._cache) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this._cache.connection.isReady();
|
||||
}
|
||||
|
||||
static compile(options, serverSide) {
|
||||
|
||||
/*
|
||||
{
|
||||
expiresIn: 30000,
|
||||
expiresAt: '13:00',
|
||||
generateFunc: (id, flags) => { throw err; } / { return result; } / { flags.ttl = ttl; return result; }
|
||||
generateTimeout: 500,
|
||||
generateOnReadError: true,
|
||||
generateIgnoreWriteError: true,
|
||||
staleIn: 20000,
|
||||
staleTimeout: 500,
|
||||
dropOnError: true,
|
||||
getDecoratedValue: false
|
||||
}
|
||||
*/
|
||||
|
||||
const rule = {};
|
||||
|
||||
if (!options ||
|
||||
!Object.keys(options).length) {
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
||||
// Validate rule
|
||||
|
||||
options = Validate.attempt(options, internals.schema, 'Invalid cache policy configuration');
|
||||
|
||||
const hasExpiresIn = options.expiresIn !== undefined && options.expiresIn !== null;
|
||||
const hasExpiresAt = options.expiresAt !== undefined && options.expiresAt !== null;
|
||||
|
||||
Hoek.assert(!hasExpiresIn || !options.staleIn || typeof options.staleIn === 'function' || options.staleIn < options.expiresIn, 'staleIn must be less than expiresIn');
|
||||
Hoek.assert(!options.staleIn || serverSide, 'Cannot use stale options without server-side caching');
|
||||
Hoek.assert(!options.staleTimeout || !hasExpiresIn || options.staleTimeout < options.expiresIn, 'staleTimeout must be less than expiresIn');
|
||||
Hoek.assert(!options.staleTimeout || !hasExpiresIn || typeof options.staleIn === 'function' || options.staleTimeout < options.expiresIn - options.staleIn, 'staleTimeout must be less than the delta between expiresIn and staleIn');
|
||||
Hoek.assert(!options.staleTimeout || !options.pendingGenerateTimeout || options.staleTimeout < options.pendingGenerateTimeout, 'pendingGenerateTimeout must be greater than staleTimeout if specified');
|
||||
|
||||
// Expiration
|
||||
|
||||
if (hasExpiresAt) {
|
||||
|
||||
// expiresAt
|
||||
|
||||
const time = /^(\d\d?):(\d\d)$/.exec(options.expiresAt);
|
||||
rule.expiresAt = {
|
||||
hours: parseInt(time[1], 10),
|
||||
minutes: parseInt(time[2], 10)
|
||||
};
|
||||
}
|
||||
else {
|
||||
|
||||
// expiresIn
|
||||
|
||||
rule.expiresIn = options.expiresIn ?? 0;
|
||||
}
|
||||
|
||||
// generateTimeout
|
||||
|
||||
if (options.generateFunc) {
|
||||
rule.generateFunc = options.generateFunc;
|
||||
rule.generateTimeout = options.generateTimeout;
|
||||
|
||||
// Stale
|
||||
|
||||
if (options.staleIn) {
|
||||
rule.staleIn = options.staleIn;
|
||||
rule.staleTimeout = options.staleTimeout;
|
||||
}
|
||||
|
||||
rule.dropOnError = options.dropOnError !== undefined ? options.dropOnError : true; // Defaults to true
|
||||
rule.pendingGenerateTimeout = options.pendingGenerateTimeout !== undefined ? options.pendingGenerateTimeout : 0; // Defaults to zero
|
||||
}
|
||||
|
||||
rule.generateOnReadError = options.generateOnReadError !== undefined ? options.generateOnReadError : true; // Defaults to true
|
||||
rule.generateIgnoreWriteError = options.generateIgnoreWriteError !== undefined ? options.generateIgnoreWriteError : true; // Defaults to true
|
||||
|
||||
// Decorations
|
||||
|
||||
rule.getDecoratedValue = options.getDecoratedValue;
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
||||
static ttl(rule, created, now) {
|
||||
|
||||
now = now ?? Date.now();
|
||||
created = created ?? now;
|
||||
const age = now - created;
|
||||
|
||||
if (age < 0) {
|
||||
return 0; // Created in the future, assume expired/bad
|
||||
}
|
||||
|
||||
if (rule.expiresIn) {
|
||||
return Math.max(rule.expiresIn - age, 0);
|
||||
}
|
||||
|
||||
if (rule.expiresAt) {
|
||||
if (age > internals.day) { // If the item was created more than a 24 hours ago
|
||||
return 0;
|
||||
}
|
||||
|
||||
const expiresAt = new Date(created); // Compare expiration time on the same day
|
||||
expiresAt.setHours(rule.expiresAt.hours);
|
||||
expiresAt.setMinutes(rule.expiresAt.minutes);
|
||||
expiresAt.setSeconds(0);
|
||||
expiresAt.setMilliseconds(0);
|
||||
let expires = expiresAt.getTime();
|
||||
|
||||
if (expires <= created) {
|
||||
expires = expires + internals.day; // Move to tomorrow
|
||||
}
|
||||
|
||||
if (now >= expires) { // Expired
|
||||
return 0;
|
||||
}
|
||||
|
||||
return expires - now;
|
||||
}
|
||||
|
||||
return 0; // No rule
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.id = function (key) {
|
||||
|
||||
return key && typeof key === 'object' ? key.id : key;
|
||||
};
|
||||
39
node_modules/@hapi/catbox/package.json
generated
vendored
Executable file
39
node_modules/@hapi/catbox/package.json
generated
vendored
Executable file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "@hapi/catbox",
|
||||
"description": "Multi-strategy object caching service",
|
||||
"version": "12.1.1",
|
||||
"repository": "git://github.com/hapijs/catbox",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"keywords": [
|
||||
"cache",
|
||||
"generic",
|
||||
"adapter"
|
||||
],
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"plugin:@hapi/module"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@hapi/boom": "^10.0.1",
|
||||
"@hapi/hoek": "^11.0.2",
|
||||
"@hapi/podium": "^5.0.0",
|
||||
"@hapi/validate": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hapi/code": "^9.0.3",
|
||||
"@hapi/eslint-plugin": "*",
|
||||
"@hapi/lab": "^25.1.2",
|
||||
"@types/node": "^18.11.9",
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "lab -a @hapi/code -t 100 -L -m 5000 -Y",
|
||||
"test-cov-html": "lab -a @hapi/code -r html -o coverage.html"
|
||||
},
|
||||
"license": "BSD-3-Clause"
|
||||
}
|
||||
Reference in New Issue
Block a user