diff --git a/library/package.json b/library/package.json index 1d8c386df..cae05db59 100644 --- a/library/package.json +++ b/library/package.json @@ -62,6 +62,7 @@ "eslint-plugin-security": "^3.0.1", "jsdom": "^27.2.0", "tsdown": "^0.16.6", + "quick-lru": "^7.0.1", "tsm": "^2.3.0", "typescript": "^5.9.3", "typescript-eslint": "^8.47.0", diff --git a/library/src/methods/cache/cache.test-d.ts b/library/src/methods/cache/cache.test-d.ts new file mode 100644 index 000000000..67cd169fb --- /dev/null +++ b/library/src/methods/cache/cache.test-d.ts @@ -0,0 +1,62 @@ +import QuickLRU from 'quick-lru'; +import { describe, expectTypeOf, test } from 'vitest'; +import type { StringIssue, StringSchema } from '../../schemas/index.ts'; +import { string } from '../../schemas/index.ts'; +import type { + InferInput, + InferIssue, + InferOutput, + OutputDataset, +} from '../../types/index.ts'; +import type { _Cache } from '../../utils/index.ts'; +import type { SchemaWithCache } from './cache.ts'; +import { cache } from './cache.ts'; + +describe('cache', () => { + describe('should return schema object', () => { + test('without options', () => { + const schema = string(); + expectTypeOf(cache(schema)).toEqualTypeOf< + SchemaWithCache + >(); + expectTypeOf(cache(schema, undefined)).toEqualTypeOf< + SchemaWithCache + >(); + }); + test('with options', () => { + const schema = string(); + expectTypeOf(cache(schema, { maxSize: 10 })).toEqualTypeOf< + SchemaWithCache + >(); + }); + test('with cache instance', () => { + const schema = string(); + + expectTypeOf( + cache(schema, { cache: new QuickLRU({ maxSize: 1000 }) }) + ).toEqualTypeOf< + SchemaWithCache< + typeof schema, + { cache: QuickLRU> } + > + >(); + }); + }); + describe('should infer correct types', () => { + type Schema = SchemaWithCache, undefined>; + test('of input', () => { + expectTypeOf>().toEqualTypeOf(); + }); + test('of output', () => { + expectTypeOf>().toEqualTypeOf(); + }); + test('of issue', () => { + expectTypeOf>().toEqualTypeOf(); + }); + test('of cache', () => { + expectTypeOf().toEqualTypeOf< + _Cache> + >(); + }); + }); +}); diff --git a/library/src/methods/cache/cache.test.ts b/library/src/methods/cache/cache.test.ts new file mode 100644 index 000000000..6b6c26fa6 --- /dev/null +++ b/library/src/methods/cache/cache.test.ts @@ -0,0 +1,79 @@ +import QuickLRU from 'quick-lru'; +import { afterAll, beforeEach, describe, expect, test, vi } from 'vitest'; +import { string } from '../../schemas/index.ts'; +import { cache } from './cache.ts'; + +describe('cache', () => { + test('should cache output', () => { + const baseSchema = string(); + const runSpy = vi.spyOn(baseSchema, '~run'); + const schema = cache(baseSchema); + expect(schema['~run']({ value: 'foo' }, {})).toBe( + schema['~run']({ value: 'foo' }, {}) + ); + expect(runSpy).toHaveBeenCalledTimes(1); + }); + test('should allow custom max size', () => { + const schema = cache(string(), { maxSize: 2 }); + expect(schema.options.maxSize).toBe(2); + + const fooDataset = schema['~run']({ value: 'foo' }, {}); + expect(schema['~run']({ value: 'foo' }, {})).toBe(fooDataset); + + expect(schema['~run']({ value: 'bar' }, {})).toBe( + schema['~run']({ value: 'bar' }, {}) + ); + + expect(schema['~run']({ value: 'baz' }, {})).toBe( + schema['~run']({ value: 'baz' }, {}) + ); + + expect(schema['~run']({ value: 'foo' }, {})).not.toBe(fooDataset); + }); + describe('should allow custom duration', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + test('and clear expired values', () => { + const schema = cache(string(), { duration: 1000 }); + + const fooDataset = schema['~run']({ value: 'foo' }, {}); + expect(schema['~run']({ value: 'foo' }, {})).toBe(fooDataset); + vi.advanceTimersByTime(1001); + expect(schema['~run']({ value: 'foo' }, {})).not.toBe(fooDataset); + }); + + test('and reset expiry on get', () => { + const schema = cache(string(), { duration: 1000 }); + const fooDataset = schema['~run']({ value: 'foo' }, {}); + expect(schema['~run']({ value: 'foo' }, {})).toBe(fooDataset); + vi.advanceTimersByTime(500); + expect(schema['~run']({ value: 'foo' }, {})).toBe(fooDataset); + vi.advanceTimersByTime(1001); + expect(schema['~run']({ value: 'foo' }, {})).not.toBe(fooDataset); + }); + }); + test('should expose cache for manual clearing', () => { + const schema = cache(string()); + const fooDataset = schema['~run']({ value: 'foo' }, {}); + expect(schema['~run']({ value: 'foo' }, {})).toBe(fooDataset); + schema.cache.clear(); + expect(schema['~run']({ value: 'foo' }, {})).not.toBe(fooDataset); + }); + test('should allow custom cache instance', () => { + const schema = cache(string(), { + cache: new QuickLRU({ maxSize: 1000 }), + }); + expect(schema.cache).toBeInstanceOf(QuickLRU); + + const fooDataset = schema['~run']({ value: 'foo' }, {}); + expect(schema['~run']({ value: 'foo' }, {})).toBe(fooDataset); + + schema.cache.clear(); + expect(schema['~run']({ value: 'foo' }, {})).not.toBe(fooDataset); + }); +}); diff --git a/library/src/methods/cache/cache.ts b/library/src/methods/cache/cache.ts new file mode 100644 index 000000000..5d806dc85 --- /dev/null +++ b/library/src/methods/cache/cache.ts @@ -0,0 +1,85 @@ +import type { + BaseIssue, + BaseSchema, + InferIssue, + InferOutput, + OutputDataset, +} from '../../types/index.ts'; +import type { CacheOptions } from '../../utils/index.ts'; +import { _Cache, _getStandardProps } from '../../utils/index.ts'; +import type { CacheInstanceOptions } from './types.ts'; + +export type SchemaWithCache< + TSchema extends BaseSchema>, + TOptions extends CacheOptions | CacheInstanceOptions | undefined, +> = TSchema & { + /** + * The cache options. + */ + readonly options: Readonly; + /** + * The cache instance. + */ + readonly cache: TOptions extends { cache: infer TCache } + ? TCache + : _Cache, InferIssue>>; +}; + +/** + * Caches the output of a schema. + * + * @param schema The schema to cache. + * + * @returns The cached schema. + */ +export function cache< + const TSchema extends BaseSchema>, +>(schema: TSchema): SchemaWithCache; + +/** + * Caches the output of a schema. + * + * @param schema The schema to cache. + * @param options Either the cache options or an instance. + * + * @returns The cached schema. + */ +export function cache< + const TSchema extends BaseSchema>, + const TOptions extends + | CacheOptions + | CacheInstanceOptions + | undefined, +>(schema: TSchema, options: TOptions): SchemaWithCache; + +// @__NO_SIDE_EFFECTS__ +export function cache( + schema: BaseSchema>, + options?: + | CacheOptions + | CacheInstanceOptions>> +): SchemaWithCache< + BaseSchema>, + | CacheOptions + | CacheInstanceOptions>> + | undefined +> { + return { + ...schema, + options, + cache: options && 'cache' in options ? options.cache : new _Cache(options), + get '~standard'() { + return _getStandardProps(this); + }, + '~run'(dataset, config) { + let outputDataset = this.cache.get(dataset.value); + if (!outputDataset) { + this.cache.set( + dataset.value, + (outputDataset = schema['~run'](dataset, config)) + ); + } + return outputDataset; + }, + }; +} diff --git a/library/src/methods/cache/cacheAsync.test-d.ts b/library/src/methods/cache/cacheAsync.test-d.ts new file mode 100644 index 000000000..9e226411e --- /dev/null +++ b/library/src/methods/cache/cacheAsync.test-d.ts @@ -0,0 +1,63 @@ +import QuickLRU from 'quick-lru'; +import { describe, expectTypeOf, test } from 'vitest'; +import type { StringIssue, StringSchema } from '../../schemas/index.ts'; +import { string } from '../../schemas/index.ts'; +import type { + InferInput, + InferIssue, + InferOutput, + OutputDataset, +} from '../../types/index.ts'; +import type { _Cache } from '../../utils/index.ts'; +import type { SchemaWithCacheAsync } from './cacheAsync.ts'; +import { cacheAsync } from './cacheAsync.ts'; + +describe('cacheAsync', () => { + describe('should return schema object', () => { + test('without options', () => { + const schema = string(); + expectTypeOf(cacheAsync(schema)).toEqualTypeOf< + SchemaWithCacheAsync + >(); + expectTypeOf(cacheAsync(schema, undefined)).toEqualTypeOf< + SchemaWithCacheAsync + >(); + }); + test('with options', () => { + const schema = string(); + expectTypeOf(cacheAsync(schema, { maxSize: 10 })).toEqualTypeOf< + SchemaWithCacheAsync + >(); + }); + test('with cache instance', () => { + const schema = string(); + + expectTypeOf( + cacheAsync(schema, { cache: new QuickLRU({ maxSize: 1000 }) }) + ).toEqualTypeOf< + SchemaWithCacheAsync< + typeof schema, + { cache: QuickLRU> } + > + >(); + }); + }); + + describe('should infer correct types', () => { + type Schema = SchemaWithCacheAsync, undefined>; + test('of input', () => { + expectTypeOf>().toEqualTypeOf(); + }); + test('of output', () => { + expectTypeOf>().toEqualTypeOf(); + }); + test('of issue', () => { + expectTypeOf>().toEqualTypeOf(); + }); + test('of cache', () => { + expectTypeOf().toEqualTypeOf< + _Cache> + >(); + }); + }); +}); diff --git a/library/src/methods/cache/cacheAsync.test.ts b/library/src/methods/cache/cacheAsync.test.ts new file mode 100644 index 000000000..2cc4512ad --- /dev/null +++ b/library/src/methods/cache/cacheAsync.test.ts @@ -0,0 +1,78 @@ +import QuickLRU from 'quick-lru'; +import { afterAll, beforeEach, describe, expect, test, vi } from 'vitest'; +import { string } from '../../schemas/index.ts'; +import { cacheAsync } from './cacheAsync.ts'; + +describe('cacheAsync', () => { + test('should cache output', async () => { + const baseSchema = string(); + const runSpy = vi.spyOn(baseSchema, '~run'); + const schema = cacheAsync(baseSchema); + expect(await schema['~run']({ value: 'foo' }, {})).toBe( + await schema['~run']({ value: 'foo' }, {}) + ); + expect(runSpy).toHaveBeenCalledTimes(1); + }); + test('should allow custom max size', async () => { + const schema = cacheAsync(string(), { maxSize: 2 }); + expect(schema.options.maxSize).toBe(2); + + const fooDataset = await schema['~run']({ value: 'foo' }, {}); + expect(await schema['~run']({ value: 'foo' }, {})).toBe(fooDataset); + + expect(await schema['~run']({ value: 'bar' }, {})).toBe( + await schema['~run']({ value: 'bar' }, {}) + ); + + expect(await schema['~run']({ value: 'baz' }, {})).toBe( + await schema['~run']({ value: 'baz' }, {}) + ); + + expect(await schema['~run']({ value: 'foo' }, {})).not.toBe(fooDataset); + }); + describe('should allow custom duration', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + test('and clear expired values', async () => { + const schema = cacheAsync(string(), { duration: 1000 }); + const fooDataset = await schema['~run']({ value: 'foo' }, {}); + expect(await schema['~run']({ value: 'foo' }, {})).toBe(fooDataset); + vi.advanceTimersByTime(1001); + expect(await schema['~run']({ value: 'foo' }, {})).not.toBe(fooDataset); + }); + + test('and reset expiry on get', async () => { + const schema = cacheAsync(string(), { duration: 1000 }); + const fooDataset = await schema['~run']({ value: 'foo' }, {}); + expect(await schema['~run']({ value: 'foo' }, {})).toBe(fooDataset); + vi.advanceTimersByTime(501); + expect(await schema['~run']({ value: 'foo' }, {})).toBe(fooDataset); + vi.advanceTimersByTime(1001); + expect(await schema['~run']({ value: 'foo' }, {})).not.toBe(fooDataset); + }); + }); + test('should expose cache for manual clearing', async () => { + const schema = cacheAsync(string()); + const fooDataset = await schema['~run']({ value: 'foo' }, {}); + expect(await schema['~run']({ value: 'foo' }, {})).toBe(fooDataset); + schema.cache.clear(); + expect(await schema['~run']({ value: 'foo' }, {})).not.toBe(fooDataset); + }); + test('should allow custom cache instance', async () => { + const schema = cacheAsync(string(), { + cache: new QuickLRU({ maxSize: 1000 }), + }); + expect(schema.cache).toBeInstanceOf(QuickLRU); + + const fooDataset = await schema['~run']({ value: 'foo' }, {}); + expect(await schema['~run']({ value: 'foo' }, {})).toBe(fooDataset); + + schema.cache.clear(); + expect(await schema['~run']({ value: 'foo' }, {})).not.toBe(fooDataset); + }); +}); diff --git a/library/src/methods/cache/cacheAsync.ts b/library/src/methods/cache/cacheAsync.ts new file mode 100644 index 000000000..23011fa7c --- /dev/null +++ b/library/src/methods/cache/cacheAsync.ts @@ -0,0 +1,126 @@ +import type { + BaseIssue, + BaseSchema, + BaseSchemaAsync, + Config, + InferIssue, + InferOutput, + OutputDataset, + UnknownDataset, +} from '../../types/index.ts'; +import type { CacheOptions } from '../../utils/index.ts'; +import { _Cache, _getStandardProps } from '../../utils/index.ts'; +import type { CacheInstanceOptions } from './types.ts'; + +export type SchemaWithCacheAsync< + TSchema extends + | BaseSchema> + | BaseSchemaAsync>, + TOptions extends CacheOptions | CacheInstanceOptions | undefined, +> = Omit & { + /** + * Whether it's async. + */ + readonly async: true; + + /** + * The cache options. + */ + readonly options: Readonly; + + /** + * The cache instance. + */ + readonly cache: TOptions extends { cache: infer TCache } + ? TCache + : _Cache, InferIssue>>; + + /** + * Parses unknown input values. + * + * @param dataset The input dataset. + * @param config The configuration. + * + * @returns The output dataset. + * + * @internal + */ + readonly '~run': ( + dataset: UnknownDataset, + config: Config> + ) => Promise, InferIssue>>; +}; + +/** + * Caches the output of a schema. + * + * @param schema The schema to cache. + * + * @returns The cached schema. + */ +// @ts-expect-error +export function cacheAsync< + const TSchema extends + | BaseSchema> + | BaseSchemaAsync>, +>(schema: TSchema): SchemaWithCacheAsync; + +/** + * Caches the output of a schema. + * + * @param schema The schema to cache. + * @param options Either the cache options or an instance. + * + * @returns The cached schema. + */ +export function cacheAsync< + const TSchema extends + | BaseSchema> + | BaseSchemaAsync>, + const TOptions extends + | CacheOptions + | CacheInstanceOptions + | undefined, +>(schema: TSchema, options: TOptions): SchemaWithCacheAsync; + +// @__NO_SIDE_EFFECTS__ +export function cacheAsync( + schema: + | BaseSchema> + | BaseSchemaAsync>, + options?: + | CacheOptions + | CacheInstanceOptions< + | BaseSchema> + | BaseSchemaAsync> + > +): SchemaWithCacheAsync< + | BaseSchema> + | BaseSchemaAsync>, + | CacheOptions + | CacheInstanceOptions< + | BaseSchema> + | BaseSchemaAsync> + > + | undefined +> { + return { + ...schema, + async: true, + options, + cache: options && 'cache' in options ? options.cache : new _Cache(options), + get '~standard'() { + return _getStandardProps(this); + }, + async '~run'(dataset, config) { + let outputDataset = this.cache.get(dataset.value); + if (!outputDataset) { + this.cache.set( + dataset.value, + (outputDataset = await schema['~run'](dataset, config)) + ); + } + return outputDataset; + }, + }; +} diff --git a/library/src/methods/cache/index.ts b/library/src/methods/cache/index.ts new file mode 100644 index 000000000..3a18cdc4f --- /dev/null +++ b/library/src/methods/cache/index.ts @@ -0,0 +1,3 @@ +export * from './cache.ts'; +export * from './cacheAsync.ts'; +export * from './types.ts'; diff --git a/library/src/methods/cache/types.ts b/library/src/methods/cache/types.ts new file mode 100644 index 000000000..d781d4026 --- /dev/null +++ b/library/src/methods/cache/types.ts @@ -0,0 +1,26 @@ +import type { OutputDataset } from '../../types/dataset.ts'; +import type { + BaseCache, + BaseIssue, + BaseSchema, + BaseSchemaAsync, + InferIssue, + InferOutput, +} from '../../types/index.ts'; + +/** + * Cache instance options type. + */ +export interface CacheInstanceOptions< + TSchema extends + | BaseSchema> + | BaseSchemaAsync>, +> { + /** + * The cache instance. + */ + cache: BaseCache< + unknown, + OutputDataset, InferIssue> + >; +} diff --git a/library/src/methods/index.ts b/library/src/methods/index.ts index 40b6cb2cf..658d877ff 100644 --- a/library/src/methods/index.ts +++ b/library/src/methods/index.ts @@ -1,4 +1,5 @@ export * from './assert/index.ts'; +export * from './cache/index.ts'; export * from './config/index.ts'; export * from './fallback/index.ts'; export * from './flatten/index.ts'; diff --git a/library/src/types/other.ts b/library/src/types/other.ts index 445915080..4318f74a9 100644 --- a/library/src/types/other.ts +++ b/library/src/types/other.ts @@ -70,3 +70,11 @@ export type DefaultValue< ? Awaited> : TDefault : never; + +/** + * A minimal cache interface, for custom cache implementations. + */ +export interface BaseCache { + get(key: TKey): TValue | undefined; + set(key: TKey, value: TValue): void; +} diff --git a/library/src/utils/_Cache/_Cache.test.ts b/library/src/utils/_Cache/_Cache.test.ts new file mode 100644 index 000000000..e6b84d9bc --- /dev/null +++ b/library/src/utils/_Cache/_Cache.test.ts @@ -0,0 +1,101 @@ +import { afterAll, beforeEach, describe, expect, test, vi } from 'vitest'; +import { _Cache } from './_Cache.ts'; + +describe('_Cache', () => { + test('should cache values', () => { + const cache = new _Cache({ maxSize: 1 }); + + cache.set('foo', 123); + expect(cache.get('foo')).toBe(123); + + cache.set('bar', 456); + + expect(cache.get('bar')).toBe(456); + + expect(cache.get('foo')).toBeUndefined(); + }); + test('should allow deleting', () => { + const cache = new _Cache(); + + cache.set('foo', 123); + + cache.set('bar', 456); + + cache.delete('foo'); + + expect(cache.get('foo')).toBeUndefined(); + expect(cache.get('bar')).toBe(456); + }); + test('should allow clearing', () => { + const cache = new _Cache(); + cache.set('foo', 123).set('bar', 456); + + cache.clear(); + + expect(cache.get('foo')).toBeUndefined(); + expect(cache.get('bar')).toBeUndefined(); + }); + test('should allow checking', () => { + const cache = new _Cache(); + + cache.set('foo', 123); + + expect(cache.has('foo')).toBe(true); + expect(cache.has('bar')).toBe(false); + }); + test('should allow getting size', () => { + const cache = new _Cache({ maxSize: 2 }); + + cache.set('foo', 123).set('bar', 456); + + expect(cache.size).toBe(2); + }); + test('should allow custom max size', () => { + const cache = new _Cache({ maxSize: 2 }); + + cache.set('foo', 123); + + cache.set('bar', 456); + + cache.set('baz', 789); + + expect(cache.get('foo')).toBeUndefined(); + expect(cache.get('bar')).toBe(456); + expect(cache.get('baz')).toBe(789); + }); + + describe('should allow custom duration', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + test('and clear expired values', () => { + const cache = new _Cache({ duration: 1000 }); + + cache.set('foo', 123); + expect(cache.get('foo')).toBe(123); + + vi.advanceTimersByTime(1001); + expect(cache.get('foo')).toBeUndefined(); + }); + + test('and reset expiry on get and has', () => { + const cache = new _Cache({ duration: 1000 }); + + cache.set('foo', 123); + expect(cache.get('foo')).toBe(123); + + vi.advanceTimersByTime(501); + expect(cache.get('foo')).toBe(123); + + vi.advanceTimersByTime(501); + expect(cache.has('foo')).toBe(true); + + vi.advanceTimersByTime(1001); + expect(cache.has('foo')).toBe(false); + }); + }); +}); diff --git a/library/src/utils/_Cache/_Cache.ts b/library/src/utils/_Cache/_Cache.ts new file mode 100644 index 000000000..b3f11eb50 --- /dev/null +++ b/library/src/utils/_Cache/_Cache.ts @@ -0,0 +1,91 @@ +import type { BaseCache } from '../../types/index.ts'; + +/** + * Cache options. + */ +export interface CacheOptions { + /** + * The maximum number of items to cache. + * + * @default 1000 + */ + maxSize?: number; + /** + * The maximum age of a cache entry in milliseconds. + * + * @default undefined + */ + duration?: number; +} + +interface _CacheEntry { + value: TValue; + lastAccess: number; +} + +/** + * A basic cache with optional max size and expiry. + */ +export class _Cache implements BaseCache { + maxSize: number; + duration: number | undefined; + constructor(options?: CacheOptions) { + this.maxSize = options?.maxSize ?? 1000; + this.duration = options?.duration; + } + get size(): number { + return this['~cache'].size; + } + get(key: TKey): TValue | undefined { + return this['~get'](key)?.value; + } + set(key: TKey, value: TValue): this { + this['~insert'](key, value); + this['~clearExcess'](); + return this; + } + has(key: TKey): boolean { + return !!this['~get'](key); + } + delete(key: TKey): boolean { + return this['~cache'].delete(key); + } + clear(): void { + this['~cache'].clear(); + } + + '~cache': Map> = new Map< + TKey, + _CacheEntry + >(); + private '~isStale'(entry: _CacheEntry): boolean { + return !!this.duration && Date.now() - entry.lastAccess > this.duration; + } + private '~get'(key: TKey): _CacheEntry | undefined { + const entry = this['~cache'].get(key); + if (!entry) return; + if (this['~isStale'](entry)) { + this['~cache'].delete(key); + return; + } + // move to end + this['~insert'](key, entry.value); + return entry; + } + private '~insert'(key: TKey, value: TValue): void { + // make sure this is always an insertion, not an update + // important for map ordering + this['~cache'].delete(key); + this['~cache'].set(key, { + value, + lastAccess: Date.now(), + }); + } + private '~clearExcess'(): void { + if (this.size <= this.maxSize) return; + const keys = this['~cache'].keys(); + while (this.size > this.maxSize) { + this['~cache'].delete(keys.next().value!); + } + } +} diff --git a/library/src/utils/_Cache/index.ts b/library/src/utils/_Cache/index.ts new file mode 100644 index 000000000..a0cc20b4c --- /dev/null +++ b/library/src/utils/_Cache/index.ts @@ -0,0 +1 @@ +export * from './_Cache.ts'; diff --git a/library/src/utils/index.ts b/library/src/utils/index.ts index 8f569b67f..2538fc5c9 100644 --- a/library/src/utils/index.ts +++ b/library/src/utils/index.ts @@ -1,4 +1,5 @@ export * from './_addIssue/index.ts'; +export * from './_Cache/index.ts'; export * from './_getByteCount/index.ts'; export * from './_getGraphemeCount/index.ts'; export * from './_getLastMetadata/index.ts'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5c3d18b18..e223be297 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -93,6 +93,9 @@ importers: jsdom: specifier: ^27.2.0 version: 27.2.0 + quick-lru: + specifier: ^7.0.1 + version: 7.0.1 tsdown: specifier: ^0.16.6 version: 0.16.6(typescript@5.9.3) @@ -146,7 +149,7 @@ importers: version: 4.4.4(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-import: specifier: ^2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) + version: 2.32.0(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-jsdoc: specifier: ^61.4.0 version: 61.4.0(eslint@9.39.1(jiti@2.6.1)) @@ -4938,6 +4941,10 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quick-lru@7.0.1: + resolution: {integrity: sha512-kLjThirJMkWKutUKbZ8ViqFc09tDQhlbQo2MNuVeLWbRauqYP96Sm6nzlQ24F0HFjUNZ4i9+AgldJ9H6DZXi7g==} + engines: {node: '>=18'} + randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} @@ -5912,6 +5919,7 @@ packages: whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-mimetype@4.0.0: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} @@ -9006,32 +9014,31 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -9042,7 +9049,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -9060,7 +9067,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -9071,7 +9078,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -9082,8 +9089,6 @@ snapshots: semver: 6.3.1 string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -11002,6 +11007,8 @@ snapshots: queue-microtask@1.2.3: {} + quick-lru@7.0.1: {} + randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 diff --git a/website/src/routes/api/(async)/cacheAsync/index.mdx b/website/src/routes/api/(async)/cacheAsync/index.mdx new file mode 100644 index 000000000..2a12bfcac --- /dev/null +++ b/website/src/routes/api/(async)/cacheAsync/index.mdx @@ -0,0 +1,145 @@ +--- +title: cacheAsync +description: Creates a version of a schema that caches its output. +source: /methods/cache/cacheAsync.ts +contributors: + - EskiMojo14 +--- + +import { ApiList, Property } from '~/components'; +import { properties } from './properties'; + +# cacheAsync + +Creates a version of a schema that caches its output. + +```ts +const Schema = v.cacheAsync(schema, options); +``` + +## Generics + +- `TSchema` +- `TOptions` + +## Parameters + +- `schema` +- `options` + +### Explanation + +The `cacheAsync` method creates a version of the given `schema` that caches its output. This can be useful for performance optimization, for example when validation involves a network request. + +## Returns + +- `Schema` + +## Examples + +The following examples show how `cacheAsync` can be used. + +### Cache schema + +Schema that caches its output. + +```ts +const CacheSchema = v.cacheAsync(v.string()); +``` + +### Max size schema + +Schema that caches its output for a maximum of 100 items. + +```ts +const MaxSizeSchema = v.cacheAsync(v.string(), { maxSize: 100 }); +``` + +### Max age schema + +Schema that caches its output for a maximum of 10 seconds. + +```ts +const MaxAgeSchema = v.cacheAsync(v.string(), { duration: 10_000 }); +``` + +### Custom cache schema + +Schema that uses a custom cache instance, in this case [QuickLRU](https://github.com/sindresorhus/quick-lru). + +```ts +import QuickLRU from 'quick-lru'; + +const CustomCacheSchema = v.cacheAsync(v.string(), { + cache: new QuickLRU({ maxSize: 1000 }), +}); +``` + +## Related + +The following APIs can be combined with `cacheAsync`. + +### Schemas + + + +### Methods + + + +### Actions + + + +### Async + + diff --git a/website/src/routes/api/(async)/cacheAsync/properties.ts b/website/src/routes/api/(async)/cacheAsync/properties.ts new file mode 100644 index 000000000..d577972a4 --- /dev/null +++ b/website/src/routes/api/(async)/cacheAsync/properties.ts @@ -0,0 +1,96 @@ +import type { PropertyProps } from '~/components'; + +export const properties: Record = { + TSchema: { + modifier: 'extends', + type: { + type: 'union', + options: [ + { + type: 'custom', + name: 'BaseSchema', + href: '../BaseSchema/', + generics: [ + 'unknown', + 'unknown', + { + type: 'custom', + name: 'BaseIssue', + href: '../BaseIssue/', + generics: ['unknown'], + }, + ], + }, + { + type: 'custom', + name: 'BaseSchemaAsync', + href: '../BaseSchemaAsync/', + generics: [ + 'unknown', + 'unknown', + { + type: 'custom', + name: 'BaseIssue', + href: '../BaseIssue/', + generics: ['unknown'], + }, + ], + }, + ], + }, + }, + TOptions: { + modifier: 'extends', + type: { + type: 'union', + options: [ + { + type: 'custom', + name: 'CacheOptions', + href: '../CacheOptions/', + }, + { + type: 'custom', + name: 'CacheInstanceOptions', + href: '../CacheInstanceOptions/', + generics: [ + { + type: 'custom', + name: 'TSchema', + }, + ], + }, + 'undefined', + ], + }, + }, + schema: { + type: { + type: 'custom', + name: 'TSchema', + }, + }, + options: { + type: { + type: 'custom', + name: 'TOptions', + }, + }, + Schema: { + type: { + type: 'custom', + name: 'SchemaWithCacheAsync', + href: '../SchemaWithCacheAsync/', + generics: [ + { + type: 'custom', + name: 'TSchema', + }, + { + type: 'custom', + name: 'TOptions', + }, + ], + }, + }, +}; diff --git a/website/src/routes/api/(methods)/cache/index.mdx b/website/src/routes/api/(methods)/cache/index.mdx new file mode 100644 index 000000000..154069ab6 --- /dev/null +++ b/website/src/routes/api/(methods)/cache/index.mdx @@ -0,0 +1,141 @@ +--- +title: cache +description: Creates a version of a schema that caches its output. +source: /methods/cache/cache.ts +contributors: + - EskiMojo14 +--- + +import { ApiList, Property } from '~/components'; +import { properties } from './properties'; + +# cache + +Creates a version of a schema that caches its output. + +```ts +const Schema = v.cache(schema, options); +``` + +## Generics + +- `TSchema` +- `TOptions` + +## Parameters + +- `schema` +- `options` + +### Explanation + +The `cache` method creates a version of the given `schema` that caches its output. This can be useful for performance optimization, for example when validation involves a network request. + +## Returns + +- `Schema` + +## Examples + +The following examples show how `cache` can be used. + +### Cache schema + +Schema that caches its output. + +```ts +const CacheSchema = v.cache(v.string()); +``` + +### Max size schema + +Schema that caches its output for a maximum of 100 items. + +```ts +const MaxSizeSchema = v.cache(v.string(), { maxSize: 100 }); +``` + +### Max age schema + +Schema that caches its output for a maximum of 10 seconds. + +```ts +const MaxAgeSchema = v.cache(v.string(), { duration: 10_000 }); +``` + +### Custom cache schema + +Schema that uses a custom cache instance, in this case [QuickLRU](https://github.com/sindresorhus/quick-lru). + +```ts +import QuickLRU from 'quick-lru'; + +const CustomCacheSchema = v.cache(v.string(), { + cache: new QuickLRU({ maxSize: 1000 }), +}); +``` + +## Related + +The following APIs can be combined with `cache`. + +### Schemas + + + +### Methods + + + +### Actions + + diff --git a/website/src/routes/api/(methods)/cache/properties.ts b/website/src/routes/api/(methods)/cache/properties.ts new file mode 100644 index 000000000..a76bdb439 --- /dev/null +++ b/website/src/routes/api/(methods)/cache/properties.ts @@ -0,0 +1,76 @@ +import type { PropertyProps } from '~/components'; + +export const properties: Record = { + TSchema: { + modifier: 'extends', + type: { + type: 'custom', + name: 'BaseSchema', + href: '../BaseSchema/', + generics: [ + 'unknown', + 'unknown', + { + type: 'custom', + name: 'BaseIssue', + href: '../BaseIssue/', + generics: ['unknown'], + }, + ], + }, + }, + TOptions: { + modifier: 'extends', + type: { + type: 'union', + options: [ + { + type: 'custom', + name: 'CacheOptions', + href: '../CacheOptions/', + }, + { + type: 'custom', + name: 'CacheInstanceOptions', + href: '../CacheInstanceOptions/', + generics: [ + { + type: 'custom', + name: 'TSchema', + }, + ], + }, + 'undefined', + ], + }, + }, + schema: { + type: { + type: 'custom', + name: 'TSchema', + }, + }, + options: { + type: { + type: 'custom', + name: 'TOptions', + }, + }, + Schema: { + type: { + type: 'custom', + name: 'SchemaWithCache', + href: '../SchemaWithCache/', + generics: [ + { + type: 'custom', + name: 'TSchema', + }, + { + type: 'custom', + name: 'TOptions', + }, + ], + }, + }, +}; diff --git a/website/src/routes/api/(types)/BaseCache/index.mdx b/website/src/routes/api/(types)/BaseCache/index.mdx new file mode 100644 index 000000000..9c59b6b43 --- /dev/null +++ b/website/src/routes/api/(types)/BaseCache/index.mdx @@ -0,0 +1,24 @@ +--- +title: BaseCache +description: Base cache interface. +contributors: + - EskiMojo14 +--- + +import { Property } from '~/components'; +import { properties } from './properties'; + +# BaseCache + +Base cache interface. + +## Generics + +- `TKey` +- `TValue` + +## Definition + +- `BaseCache` + - `get` + - `set` diff --git a/website/src/routes/api/(types)/BaseCache/properties.ts b/website/src/routes/api/(types)/BaseCache/properties.ts new file mode 100644 index 000000000..25052f18c --- /dev/null +++ b/website/src/routes/api/(types)/BaseCache/properties.ts @@ -0,0 +1,58 @@ +import type { PropertyProps } from '~/components'; + +export const properties: Record = { + TKey: { + modifier: 'extends', + type: 'any', + }, + TValue: { + modifier: 'extends', + type: 'any', + }, + get: { + type: { + type: 'function', + params: [ + { + name: 'key', + type: { + type: 'custom', + name: 'TKey', + }, + }, + ], + return: { + type: 'union', + options: [ + { + type: 'custom', + name: 'TValue', + }, + 'undefined', + ], + }, + }, + }, + set: { + type: { + type: 'function', + params: [ + { + name: 'key', + type: { + type: 'custom', + name: 'TKey', + }, + }, + { + name: 'value', + type: { + type: 'custom', + name: 'TValue', + }, + }, + ], + return: 'void', + }, + }, +}; diff --git a/website/src/routes/api/(types)/CacheInstanceOptions/index.mdx b/website/src/routes/api/(types)/CacheInstanceOptions/index.mdx new file mode 100644 index 000000000..263dccb9d --- /dev/null +++ b/website/src/routes/api/(types)/CacheInstanceOptions/index.mdx @@ -0,0 +1,22 @@ +--- +title: CacheInstanceOptions +description: Cache instance options interface. +contributors: + - EskiMojo14 +--- + +import { Property } from '~/components'; +import { properties } from './properties'; + +# CacheInstanceOptions + +Cache instance options interface. + +## Generics + +- `TSchema` + +## Definition + +- `CacheInstanceOptions` + - `cache` diff --git a/website/src/routes/api/(types)/CacheInstanceOptions/properties.ts b/website/src/routes/api/(types)/CacheInstanceOptions/properties.ts new file mode 100644 index 000000000..0f332052c --- /dev/null +++ b/website/src/routes/api/(types)/CacheInstanceOptions/properties.ts @@ -0,0 +1,81 @@ +import type { PropertyProps } from '~/components'; + +export const properties: Record = { + TSchema: { + modifier: 'extends', + type: { + type: 'union', + options: [ + { + type: 'custom', + name: 'BaseSchema', + href: '../BaseSchema/', + generics: [ + 'unknown', + 'unknown', + { + type: 'custom', + name: 'BaseIssue', + href: '../BaseIssue/', + generics: ['unknown'], + }, + ], + }, + { + type: 'custom', + name: 'BaseSchemaAsync', + href: '../BaseSchemaAsync/', + generics: [ + 'unknown', + 'unknown', + { + type: 'custom', + name: 'BaseIssue', + href: '../BaseIssue/', + generics: ['unknown'], + }, + ], + }, + ], + }, + }, + cache: { + type: { + type: 'custom', + name: 'BaseCache', + href: '../BaseCache/', + generics: [ + 'unknown', + { + type: 'custom', + name: 'OutputDataset', + href: '../OutputDataset/', + generics: [ + { + type: 'custom', + name: 'InferOutput', + href: '../InferOutput/', + generics: [ + { + type: 'custom', + name: 'TSchema', + }, + ], + }, + { + type: 'custom', + name: 'InferIssue', + href: '../InferIssue/', + generics: [ + { + type: 'custom', + name: 'TSchema', + }, + ], + }, + ], + }, + ], + }, + }, +}; diff --git a/website/src/routes/api/(types)/CacheOptions/index.mdx b/website/src/routes/api/(types)/CacheOptions/index.mdx new file mode 100644 index 000000000..9f18bed9a --- /dev/null +++ b/website/src/routes/api/(types)/CacheOptions/index.mdx @@ -0,0 +1,19 @@ +--- +title: CacheOptions +description: Cache options interface. +contributors: + - EskiMojo14 +--- + +import { Property } from '~/components'; +import { properties } from './properties'; + +# CacheOptions + +Cache options interface. + +## Definition + +- `CacheOptions` + - `maxSize?` + - `duration?` diff --git a/website/src/routes/api/(types)/CacheOptions/properties.ts b/website/src/routes/api/(types)/CacheOptions/properties.ts new file mode 100644 index 000000000..d7d30583d --- /dev/null +++ b/website/src/routes/api/(types)/CacheOptions/properties.ts @@ -0,0 +1,10 @@ +import type { PropertyProps } from '~/components'; + +export const properties: Record = { + maxSize: { + type: 'number', + }, + duration: { + type: 'number', + }, +}; diff --git a/website/src/routes/api/(types)/SchemaWithCache/index.mdx b/website/src/routes/api/(types)/SchemaWithCache/index.mdx new file mode 100644 index 000000000..bd35e2a95 --- /dev/null +++ b/website/src/routes/api/(types)/SchemaWithCache/index.mdx @@ -0,0 +1,24 @@ +--- +title: SchemaWithCache +description: Schema with cache type. +contributors: + - EskiMojo14 +--- + +import { Property } from '~/components'; +import { properties } from './properties'; + +# SchemaWithCache + +Schema with cache type. + +## Generics + +- `TSchema` +- `TOptions` + +## Definition + +- `SchemaWithCache` + - `options` + - `cache` diff --git a/website/src/routes/api/(types)/SchemaWithCache/properties.ts b/website/src/routes/api/(types)/SchemaWithCache/properties.ts new file mode 100644 index 000000000..f6932ed4b --- /dev/null +++ b/website/src/routes/api/(types)/SchemaWithCache/properties.ts @@ -0,0 +1,126 @@ +import type { PropertyProps } from '~/components'; + +export const properties: Record = { + TSchema: { + modifier: 'extends', + type: { + type: 'custom', + name: 'BaseSchema', + href: '../BaseSchema/', + generics: [ + 'unknown', + 'unknown', + { + type: 'custom', + name: 'BaseIssue', + href: '../BaseIssue/', + generics: ['unknown'], + }, + ], + }, + }, + TOptions: { + modifier: 'extends', + type: { + type: 'union', + options: [ + { + type: 'custom', + name: 'CacheOptions', + href: '../CacheOptions/', + }, + { + type: 'custom', + name: 'CacheInstanceOptions', + href: '../CacheInstanceOptions/', + generics: [ + { + type: 'custom', + name: 'TSchema', + }, + ], + }, + 'undefined', + ], + }, + }, + options: { + type: { + type: 'custom', + name: 'TOptions', + }, + }, + cache: { + type: { + type: 'conditional', + conditions: [ + { + type: { + type: 'custom', + name: 'TOptions', + }, + extends: { + type: 'object', + entries: [ + { + key: 'cache', + value: { + type: 'custom', + modifier: 'infer', + name: 'TCache', + }, + }, + ], + }, + true: { + type: 'custom', + name: 'TCache', + }, + }, + ], + false: { + type: 'custom', + name: '_Cache', + generics: [ + 'unknown', + { + type: 'custom', + name: 'OutputDataset', + href: '../OutputDataset/', + generics: [ + { + type: 'custom', + name: 'InferOutput', + href: '../InferOutput/', + generics: [ + { + type: 'custom', + name: 'TSchema', + }, + ], + }, + { + type: 'custom', + name: 'InferIssue', + href: '../InferIssue/', + generics: [ + { + type: 'custom', + name: 'TSchema', + }, + ], + }, + ], + }, + ], + }, + }, + }, + SchemaWithCache: { + modifier: 'extends', + type: { + type: 'custom', + name: 'TSchema', + }, + }, +}; diff --git a/website/src/routes/api/(types)/SchemaWithCacheAsync/index.mdx b/website/src/routes/api/(types)/SchemaWithCacheAsync/index.mdx new file mode 100644 index 000000000..b0c35c1a7 --- /dev/null +++ b/website/src/routes/api/(types)/SchemaWithCacheAsync/index.mdx @@ -0,0 +1,25 @@ +--- +title: SchemaWithCacheAsync +description: Schema with cache async type. +contributors: + - EskiMojo14 +--- + +import { Property } from '~/components'; +import { properties } from './properties'; + +# SchemaWithCacheAsync + +Schema with cache async type. + +## Generics + +- `TSchema` +- `TOptions` + +## Definition + +- `SchemaWithCacheAsync` + - `async` + - `options` + - `cache` diff --git a/website/src/routes/api/(types)/SchemaWithCacheAsync/properties.ts b/website/src/routes/api/(types)/SchemaWithCacheAsync/properties.ts new file mode 100644 index 000000000..e796e4283 --- /dev/null +++ b/website/src/routes/api/(types)/SchemaWithCacheAsync/properties.ts @@ -0,0 +1,152 @@ +import type { PropertyProps } from '~/components'; + +export const properties: Record = { + async: { + type: { + type: 'boolean', + value: true, + }, + }, + TSchema: { + modifier: 'extends', + type: { + type: 'union', + options: [ + { + type: 'custom', + name: 'BaseSchema', + href: '../BaseSchema/', + generics: [ + 'unknown', + 'unknown', + { + type: 'custom', + name: 'BaseIssue', + href: '../BaseIssue/', + generics: ['unknown'], + }, + ], + }, + { + type: 'custom', + name: 'BaseSchemaAsync', + href: '../BaseSchemaAsync/', + generics: [ + 'unknown', + 'unknown', + { + type: 'custom', + name: 'BaseIssue', + href: '../BaseIssue/', + generics: ['unknown'], + }, + ], + }, + ], + }, + }, + TOptions: { + modifier: 'extends', + type: { + type: 'union', + options: [ + { + type: 'custom', + name: 'CacheOptions', + href: '../CacheOptions/', + }, + { + type: 'custom', + name: 'CacheInstanceOptions', + href: '../CacheInstanceOptions/', + generics: [ + { + type: 'custom', + name: 'TSchema', + }, + ], + }, + 'undefined', + ], + }, + }, + options: { + type: { + type: 'custom', + name: 'TOptions', + }, + }, + cache: { + type: { + type: 'conditional', + conditions: [ + { + type: { + type: 'custom', + name: 'TOptions', + }, + extends: { + type: 'object', + entries: [ + { + key: 'cache', + value: { + type: 'custom', + modifier: 'infer', + name: 'TCache', + }, + }, + ], + }, + true: { + type: 'custom', + name: 'TCache', + }, + }, + ], + false: { + type: 'custom', + name: '_Cache', + generics: [ + 'unknown', + { + type: 'custom', + name: 'OutputDataset', + href: '../OutputDataset/', + generics: [ + { + type: 'custom', + name: 'InferOutput', + href: '../InferOutput/', + generics: [ + { + type: 'custom', + name: 'TSchema', + }, + ], + }, + { + type: 'custom', + name: 'InferIssue', + href: '../InferIssue/', + generics: [ + { + type: 'custom', + name: 'TSchema', + }, + ], + }, + ], + }, + ], + }, + }, + }, + SchemaWithCacheAsync: { + modifier: 'extends', + type: { + type: 'custom', + name: 'TSchema', + }, + }, +}; diff --git a/website/src/routes/api/menu.md b/website/src/routes/api/menu.md index e61c5f3db..c12b89f14 100644 --- a/website/src/routes/api/menu.md +++ b/website/src/routes/api/menu.md @@ -52,6 +52,7 @@ ## Methods - [assert](/api/assert/) +- [cache](/api/cache/) - [config](/api/config/) - [fallback](/api/fallback/) - [flatten](/api/flatten/) @@ -226,6 +227,7 @@ - [argsAsync](/api/argsAsync/) - [arrayAsync](/api/arrayAsync/) - [awaitAsync](/api/awaitAsync/) +- [cacheAsync](/api/cacheAsync/) - [checkAsync](/api/checkAsync/) - [checkItemsAsync](/api/checkItemsAsync/) - [customAsync](/api/customAsync/) @@ -284,6 +286,7 @@ - [AwaitActionAsync](/api/AwaitActionAsync/) - [Base64Action](/api/Base64Action/) - [Base64Issue](/api/Base64Issue/) +- [BaseCache](/api/BaseCache/) - [BaseIssue](/api/BaseIssue/) - [BaseMetadata](/api/BaseMetadata/) - [BaseSchema](/api/BaseSchema/) @@ -305,6 +308,8 @@ - [BrandName](/api/BrandName/) - [BytesAction](/api/BytesAction/) - [BytesIssue](/api/BytesIssue/) +- [CacheInstanceOptions](/api/CacheInstanceOptions/) +- [CacheOptions](/api/CacheOptions/) - [CheckAction](/api/CheckAction/) - [CheckActionAsync](/api/CheckActionAsync/) - [CheckIssue](/api/CheckIssue/) @@ -641,6 +646,8 @@ - [SafeParser](/api/SafeParser/) - [SafeParserAsync](/api/SafeParserAsync/) - [SafeParseResult](/api/SafeParseResult/) +- [SchemaWithCache](/api/SchemaWithCache/) +- [SchemaWithCacheAsync](/api/SchemaWithCacheAsync/) - [SchemaWithFallback](/api/SchemaWithFallback/) - [SchemaWithFallbackAsync](/api/SchemaWithFallbackAsync/) - [SchemaWithoutPipe](/api/SchemaWithoutPipe/)