@conduit-client/type-normalization 3.12.0 → 3.13.1

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.
@@ -1,8 +1,8 @@
1
1
  import { type Result } from '@conduit-client/utils';
2
+ import type { NamedPubSubService } from '@conduit-client/service-pubsub/v1';
3
+ import type { InstrumentationAttributes, NamedInstrumentationService } from '@conduit-client/service-instrumentation/v1';
4
+ import type { NamedCacheControllerService } from '@conduit-client/service-cache-control/v1';
2
5
  import type { Key, CanonicalCacheControlMetadata, ReadonlyCache, CacheEntry, Cache } from '@conduit-client/service-cache/v1';
3
- import { NamedPubSubService } from '@conduit-client/service-pubsub/v1';
4
- import { InstrumentationAttributes, NamedInstrumentationService } from '@conduit-client/service-instrumentation/v1';
5
- import { NamedCacheControllerService } from '@conduit-client/service-cache-control/v1';
6
6
  export interface NormalizedLink {
7
7
  type: 'link';
8
8
  linkedKey: Key;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/v1/index.ts"],"sourcesContent":["import { type Result, ok, err, deepEquals, stableJSONStringify } from '@conduit-client/utils';\nimport type {\n Key,\n CanonicalCacheControlMetadata,\n ReadonlyCache,\n CacheEntry,\n Cache,\n} from '@conduit-client/service-cache/v1';\n\nimport { ArrayIsArray } from '@conduit-client/utils';\nimport { NamedPubSubService } from '@conduit-client/service-pubsub/v1';\nimport {\n InstrumentationAttributes,\n NamedInstrumentationService,\n} from '@conduit-client/service-instrumentation/v1';\nimport { NamedCacheControllerService } from '@conduit-client/service-cache-control/v1';\n\nexport interface NormalizedLink {\n type: 'link';\n linkedKey: Key;\n}\n\ntype UnknownError = {\n type: 'unknown';\n error: Error;\n};\n\n/**\n * An error that occurs when a type tries to read from the cache, and the cache metadata does\n * not match the current type\n */\ntype IncorrectTypeError = {\n type: 'incorrectType';\n expectedType: string;\n actualType: string;\n};\n\n/**\n * An error that occurs when a type tries to read from the cache and data is missing\n */\ntype MissingDataError = {\n type: 'missingData';\n namespace: string;\n typeName: string;\n data: unknown;\n};\n\n/**\n * A union of errors that can be returned during the write process\n */\nexport type WriteErrors = Array<UnknownError>;\n\n/**\n * A union of errors that can be returned during the read process\n */\nexport type ReadErrors = Array<UnknownError | IncorrectTypeError | MissingDataError>;\n\nexport type ReadResult<Data> = Result<Data, ReadErrors>;\nexport type WriteResult<Data> = Result<Data, WriteErrors>;\n\n/**\n * A Type defines a collection of functions associated with a particular\n * data type. Types are not normalized but may contain normalized data.\n *\n * @typeParam Data TypeScript type that corresponds to the data\n * @typeParam NormalizedData TypeScript type that corresponds to the normalized data\n * @typeParam NormalizeInputData TypeScript type that corresponds to the data that the normalize method expects as input. Defaults to the same as Data\n */\nexport type TypeRepository<Data, ReadInput, WriteResponse, WriteInput> = {\n readonly namespace: string;\n readonly typeName: string;\n\n equals(x: Data, y: Data): boolean;\n\n /**\n * Normalizes data for storage.\n */\n write(cache: Cache, input: WriteInput): WriteResult<WriteResponse>;\n\n /**\n * De-normalizes a Type's data. A type could normalize into a Link, or return Data to the caller.\n * A caller must not be relying on the referenced type being normalized or not.\n * If denormalization fails, it will return undefined.\n */\n read(cache: ReadonlyCache, input: ReadInput): ReadResult<Data>;\n};\n\n/**\n * An InvalidatableType defines an invalidate function that takes a generic input,\n * and returns a promise that resolves when the invalidation is complete\n *\n * @typeParam T is another type to extend\n * @typeParam InvalidateInput is the input to run an invalidation\n */\nexport type Invalidatable<T extends TypeRepository<any, any, any, any>, InvalidateInput> = T & {\n /**\n *\n * @param configs Partial KeyConfigs for this Type (partial KeyConfig treats missing parts as wildcard)\n */\n invalidate(configs: InvalidateInput[]): Promise<void>;\n};\n\n/**\n * A QueryableType defines a collection of functions associated with a particular\n * data type that gets its own cache entry.\n *\n * @typeParam Data TypeScript type that corresponds to the data\n * @typeParam KeyConfig TypeScript type for the inputs needed to construct\n * a Key for the data\n * @typeParam NormalizeInputData TypeScript type that corresponds to the data that the normalize method expects as input. Defaults to the same as Data\n */\nexport type Queryable<T extends TypeRepository<any, any, any, any>, QueryInput> = T & {\n /**\n *\n * @param cache The cache to run the query against\n * @param query The query to run\n */\n query(cache: Cache, query: QueryInput): ReadResult<DataOf<T>>;\n};\n\nexport type DataOf<T extends TypeRepository<any, any, any, any>> =\n T extends TypeRepository<infer Data, any, any, any> ? Data : never;\nexport type ReadInputOf<T extends TypeRepository<any, any, any, any>> =\n T extends TypeRepository<any, infer ReadInput, any, any> ? ReadInput : never;\nexport type WriteResponseOf<T extends TypeRepository<any, any, any, any>> =\n T extends TypeRepository<any, any, infer WriteResponse, any> ? WriteResponse : never;\nexport type WriteResultOf<T extends TypeRepository<any, any, any, any>> = WriteResult<\n WriteResponseOf<T>\n>;\nexport type WriteInputOf<T extends TypeRepository<any, any, any, any>> =\n T extends TypeRepository<any, any, any, infer WriteInput> ? WriteInput : never;\nexport type InvalidateInputOf<T extends TypeRepository<any, any, any, any>> =\n T extends Invalidatable<any, infer InvalidateInput> ? InvalidateInput : never;\n\n/**\n * A utility method for ensuring that a given entry in the cache matches a type based on namespace and name information\n * @param cacheEntry the cache entry to validate against\n * @param type the type to validate the cache entry against\n * @returns\n */\nexport function isCacheEntryForType(\n cacheEntry: CacheEntry<unknown>,\n type: TypeRepository<unknown, unknown, unknown, unknown>\n): boolean {\n return (\n cacheEntry.metadata.type.namespace === type.namespace &&\n cacheEntry.metadata.type.name === type.typeName\n );\n}\n\nexport type KeyParamsOf<T> =\n T extends IdentifiableTypeRepository<any, any, infer KeyParams, any, any> ? KeyParams : never;\n\nexport type WriteInput<Data, Extension = Record<string, unknown>> = { data: Data } & Extension;\nexport type NormalizeDataInput<\n Data,\n NormalizedData,\n Extension extends Record<string, any> = Record<string, unknown>,\n> = WriteInput<Data, Extension & { existingNormalizedData?: NormalizedData }>;\n\nexport type ReadInput<Extension extends Record<string, any> = Record<string, unknown>> = {\n normalizedData: NormalizedLink;\n} & Extension;\nexport type ReadByKeyParamsInput<KeyParams> = {\n keyParams: KeyParams;\n};\nexport type DenormalizeInput<Extension extends Record<string, any> = Record<string, unknown>> =\n Extension & {\n key: Key;\n };\nexport type DenormalizeDataInput<\n NormalizedData,\n Extension extends Record<string, any> = Record<string, unknown>,\n> = Extension & { normalizedData: NormalizedData };\n\nexport type KeyParamQuery<KeyParams, ReadInputExtension> = {\n type: 'keyParams';\n keyParams: KeyParams;\n} & ReadInputExtension;\nexport type QueryFor<T> = T extends Queryable<any, infer Query> ? Query : never;\n\n/**\n * An abstract base class that implements Type, defining additional abstract properties and methods\n * for building a key for a given set of parameters, and defining the canonical cache control metadata\n * for any data written\n */\nexport abstract class IdentifiableTypeRepository<\n Data,\n NormalizedData,\n KeyParams,\n ReadInputExtension extends Record<string, any> = Record<string, unknown>,\n WriteInputExtension extends Record<string, any> = Record<string, unknown>,\n Query extends KeyParamQuery<KeyParams, ReadInputExtension> = KeyParamQuery<\n KeyParams,\n ReadInputExtension\n >,\n> implements\n Queryable<\n TypeRepository<\n Data,\n ReadInput<ReadInputExtension>,\n NormalizedLink,\n WriteInput<Data, WriteInputExtension>\n >,\n Query\n >\n{\n constructor(protected services: {}) {}\n abstract readonly cacheControl: CanonicalCacheControlMetadata;\n abstract readonly namespace: string;\n abstract readonly typeName: string;\n protected abstract denormalizeData(\n cache: ReadonlyCache,\n input: DenormalizeDataInput<NormalizedData, ReadInput<ReadInputExtension>>\n ): Result<Data, ReadErrors>;\n protected abstract normalizeData(\n cache: Cache,\n input: NormalizeDataInput<\n Data,\n NormalizedData,\n WriteInputExtension & { existingNormalizedData?: NormalizedData }\n >\n ): Result<NormalizedData, WriteErrors>;\n get cacheMetadata() {\n return {\n cacheControl: { ...this.cacheControl },\n type: { namespace: this.namespace, name: this.typeName },\n };\n }\n\n buildKey(params: KeyParams): Key {\n return `${this.namespace}::${this.typeName}(${stableJSONStringify(params)})`;\n }\n abstract buildKeyParams(input: WriteInput<Data, WriteInputExtension>): KeyParams;\n\n write(cache: Cache, input: WriteInput<Data, WriteInputExtension>): WriteResult<NormalizedLink> {\n // Identifiable types can have a key that is not the same as the input normalized data\n // so we need to build the key from the input, and then get the existing normalized data from the cache\n // and pass it to the normalizeData method. This should ALWAYS be done for identifiable types.\n const key = this.buildKey(this.buildKeyParams(input));\n const existingNormalizedData: NormalizedData | undefined = cache.get<NormalizedData>(key)\n ?.value as any;\n const normalized = this.normalizeData(cache, { ...input, existingNormalizedData });\n if (normalized.isErr()) {\n return err(normalized.error);\n }\n\n cache.set<NormalizedData>(key, {\n value: normalized.value,\n metadata: this.cacheMetadata,\n });\n\n return ok({\n type: 'link',\n linkedKey: key,\n } as const);\n }\n\n protected validateEntry(entry: CacheEntry<unknown>): Result<undefined, ReadErrors> {\n if (!isCacheEntryForType(entry, this)) {\n return err([\n {\n type: 'incorrectType',\n expectedType: this.typeName,\n actualType: entry.metadata.type.name,\n },\n ]);\n }\n return ok(undefined);\n }\n\n query(cache: Cache, query: Query): ReadResult<Data> {\n return this.read(cache, {\n ...query,\n normalizedData: { type: 'link', linkedKey: this.buildKey(query.keyParams) },\n });\n }\n\n read(cache: ReadonlyCache, input: ReadInput<ReadInputExtension>): Result<Data, ReadErrors> {\n return this.denormalize(cache, { ...input, key: input.normalizedData.linkedKey });\n }\n\n denormalize(cache: ReadonlyCache, input: DenormalizeInput<ReadInput<ReadInputExtension>>) {\n const cacheEntry = cache.get<NormalizedData>(input.key, {\n copy: false,\n }) as CacheEntry<NormalizedData>;\n\n if (cacheEntry) {\n const validationResult = this.validateEntry(cacheEntry);\n if (validationResult.isErr()) {\n return err([...validationResult.error]);\n }\n const normalizedData = cacheEntry.value;\n return this.denormalizeData(cache, { ...input, normalizedData });\n }\n\n return err([this.buildMissingDataError()]);\n }\n\n equals(x: Data, y: Data) {\n return deepEquals(x, y);\n }\n\n buildMissingDataError(): MissingDataError {\n return {\n type: 'missingData',\n typeName: this.typeName,\n namespace: this.namespace,\n data: undefined,\n };\n }\n}\n\n/**\n * An abstract base class that extends IdentifiableType, adding the additional constraint that the\n * only data required to write to the cache is an instance of the data itself, i.e. the identity of\n * the data lives inside the data\n */\nexport abstract class SelfIdentifiableTypeRepository<\n Data,\n NormalizedData,\n KeyParams,\n ReadInputExtension extends Record<string, any> = Record<string, unknown>,\n WriteInputExtension extends Record<string, any> = Record<string, unknown>,\n> extends IdentifiableTypeRepository<\n Data,\n NormalizedData,\n KeyParams,\n ReadInputExtension,\n WriteInputExtension\n> {}\n\n/**\n * An abstract base class that extends SelfIdentifiableType. Adds the constraint that the structure of\n * the key parameters must be a flat object, with no objects nested. This class provides invalidation and keybuilding 'for free'.\n * Should be preferred over IdentifiableType and SelfIdentifiableType\n */\nexport abstract class FlattenedKeySelfIdentifiableTypeRepository<\n Data,\n NormalizedData,\n KeyParams extends FlattenedKeyParams,\n ReadInputExtension extends Record<string, any> = Record<string, unknown>,\n WriteInputExtension extends Record<string, any> = Record<string, unknown>,\n >\n extends SelfIdentifiableTypeRepository<\n Data,\n NormalizedData,\n KeyParams,\n ReadInputExtension,\n WriteInputExtension\n >\n implements\n Invalidatable<\n SelfIdentifiableTypeRepository<\n Data,\n NormalizedData,\n KeyParams,\n ReadInputExtension,\n WriteInputExtension\n >,\n Partial<KeyParams>\n >\n{\n instrumentationAttributes: InstrumentationAttributes | undefined;\n constructor(\n protected services: NamedCacheControllerService &\n NamedPubSubService &\n Partial<NamedInstrumentationService>\n ) {\n super(services);\n }\n\n abstract readonly keySchema: string[];\n\n async invalidate(configs: Partial<KeyParams>[]): Promise<void> {\n const startTime = this.services.instrumentation\n ? this.services.instrumentation.currentTimeMs()\n : 0;\n const result = this.runInvalidate(configs).then(() => {\n if (this.services.instrumentation) {\n const duration = this.services.instrumentation.currentTimeMs() - startTime;\n this.collectInstrumentation(duration);\n }\n });\n return result;\n }\n\n protected collectInstrumentation(duration: number) {\n if (this.services.instrumentation) {\n const meter = this.services.instrumentation.metrics.getMeter('onestore');\n const attributes = {\n ...this.instrumentationAttributes,\n namespace: this.namespace,\n typeName: this.typeName,\n };\n meter\n .createCounter<InstrumentationAttributes>(`type.invalidate.count`)\n .add(1, attributes);\n meter\n .createHistogram<InstrumentationAttributes>(`type.invalidate.duration`)\n .record(duration, attributes);\n }\n }\n\n protected async runInvalidate(configs: Partial<KeyParams>[]): Promise<void> {\n const regex = buildRegexForNamespacedKeys(\n this.namespace,\n this.typeName,\n configs,\n this.keySchema\n );\n\n const results: Set<Key> = new Set();\n for await (const entry of this.services.cacheController.findAndModify(\n { key: { $regex: regex } },\n { type: 'invalidate' }\n )) {\n results.add(entry);\n }\n if (results.size > 0) {\n await this.services.pubSub.publish({\n type: 'cacheInvalidation',\n data: results,\n });\n }\n }\n\n buildKey(config: KeyParams): Key {\n return buildNamespacedTypeKey(this.namespace, this.typeName, config, this.keySchema);\n }\n}\ntype ScalarKeyValue = string | number | boolean | null | undefined;\ntype FlattenedKeyParams = Record<string, ScalarKeyValue | ScalarKeyValue[]>;\nexport function buildNamespacedTypeKey(\n namespace: string,\n typeName: string,\n keyConfig: FlattenedKeyParams,\n keySchema: string[]\n): string {\n const keyParts = keySchema\n .map((key) => {\n const value = keyConfig[key];\n return `${ArrayIsArray(value) ? value.join() : value}`;\n })\n .join('|');\n return `${namespace}::${typeName}(${keyParts})`;\n}\n\nexport function buildRegexForNamespacedKeys(\n namespace: string,\n typeName: string,\n partialKeyConfigs: Array<FlattenedKeyParams>,\n keySchema: string[]\n): RegExp {\n const patterns = partialKeyConfigs.map((partial) => {\n // Build a regex pattern for a single KeyConfig\n const requiredParts = keySchema\n .map((key) => {\n if (!(key in partial)) {\n return `[^|]*?`; // Allow any value or absence of value (optional match)\n }\n const value = partial[key];\n return `(?:${(ArrayIsArray(value) ? value.join() : String(value)).replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')})`;\n })\n .join('\\\\|'); // Ensure properties are matched in order with literal separator\n\n return `^${namespace}::${typeName}\\\\((?:${requiredParts})\\\\)$`;\n });\n\n return new RegExp(`^(?:${patterns.join('|')})$`); // Apply OR logic across separate partial configs\n}\n\nexport function extractReadWriteData<Data>(\n result: ReadResult<Data> | WriteResult<Data>,\n errorCollector: any[]\n): Data | undefined {\n if (result.isOk()) {\n return result.value;\n }\n\n errorCollector.push(...result.error);\n return undefined;\n}\n\nexport function buildReadWriteResult<Data, Errors extends WriteErrors | ReadErrors>(\n data: unknown,\n errors: Errors\n): Result<Data, Errors> {\n if (errors.length > 0) {\n return err(errors);\n }\n\n return ok(data as Data);\n}\n\n// This type is the normalized representation of union data (either a pure union, or a discriminated union)\n// This data shape will live in the cache, and contains the discriminator (i.e. an identity of what type of data this entry represents)\n// and the normalized data for that entry\nexport type UnionRepresentation<Discriminator extends string, Data> = {\n discriminator: Discriminator;\n data: Data;\n};\n\n// A utility type that represents all the information required for normalizing and\n// denormalizing types into a cache, including the discriminator value, the\n// canonical representation (Data) and the normalized representation (Normalized)\nexport type DiscriminatorMap<Discriminator extends string, Data, Normalized> = {\n [k in Discriminator]: {\n data: Data;\n normalized: Normalized;\n };\n};\n\n// A utility type for inferring the union of normalized representations\n// possible based on a given DiscriminatorType\nexport type NormalizedRepresentationOf<M extends DiscriminatorMap<any, any, any>> =\n M extends DiscriminatorMap<infer D, any, any>\n ? { [k in D]: UnionRepresentation<k, M[k]['normalized']> }[D]\n : never;\n\n// A utility type for inferring the discriminator -> denormalize function map\n// required by the `denormalizeUnionRepresentation` function\nexport type DenormalizeMapOf<M extends DiscriminatorMap<any, any, any>> =\n M extends DiscriminatorMap<infer D, any, any>\n ? { [k in D]: (normalized: M[k]['normalized']) => M[k]['data'] }\n : never;\n\n// A utility type for inferring the canonical data type union based on\n// a DiscriminatorMap type\nexport type UnionDataOf<M extends DiscriminatorMap<any, any, any>> =\n M extends DiscriminatorMap<any, infer D, any> ? D : never;\n\n// A utility type for inferring the discriminator string union from a\n// given discriminator map type\nexport type DiscriminatorOf<M extends DiscriminatorMap<any, any, any>> =\n M extends DiscriminatorMap<infer D, any, any> ? D : never;\n\n// A utilty type for inferring the test/normalize map type from a\n// discriminator map type to be used in the `normalizeUnionRepresentation` function\nexport type DiscriminatorNormalizeFunctionsMap<M extends DiscriminatorMap<any, any, any>> =\n M extends DiscriminatorMap<infer D, any, any>\n ? {\n [k in D]: {\n test: (data: M[D]['data']) => boolean;\n normalize: (data: M[k]['data']) => M[k]['normalized'];\n };\n }\n : never;\n\n/**\n *\n * @param normalized The normalized data representation, including the discriminator value\n * @param denormalizeMap A map of discriminator -> denormalize functions to be used based on the datas discriminator\n * @returns\n */\nexport function denormalizeUnionRepresentation<\n M extends DiscriminatorMap<string, unknown, unknown>,\n>(normalized: NormalizedRepresentationOf<M>, denormalizeMap: DenormalizeMapOf<M>): UnionDataOf<M> {\n const discriminator = normalized.discriminator as DiscriminatorOf<M>;\n return denormalizeMap[discriminator](normalized.data) as UnionDataOf<M>;\n}\n\n/**\n *\n * @param data The canonical/denormalized data representation\n * @param normalizeTestMap A map of discriminator to test method which return a boolean indicating if the data adheres to this \"discriminator\"\n * and a normalize function to call if the data matches this discriminator\n * @returns\n */\nexport function normalizeUnionRepresentation<M extends DiscriminatorMap<string, unknown, unknown>>(\n data: UnionDataOf<M>,\n normalizeTestMap: DiscriminatorNormalizeFunctionsMap<M>\n): NormalizedRepresentationOf<M> {\n for (const discriminator of Object.keys(normalizeTestMap) as DiscriminatorOf<M>[]) {\n const discriminatorLogic = normalizeTestMap[discriminator];\n if (discriminatorLogic.test(data)) {\n return {\n discriminator,\n data: discriminatorLogic.normalize(data),\n } as unknown as NormalizedRepresentationOf<M>;\n }\n }\n\n throw new Error('No matching union member found');\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/v1/index.ts"],"sourcesContent":["import { type Result, ok, err, deepEquals, stableJSONStringify } from '@conduit-client/utils';\n\nimport { ArrayIsArray } from '@conduit-client/utils';\nimport type { NamedPubSubService } from '@conduit-client/service-pubsub/v1';\nimport type {\n InstrumentationAttributes,\n NamedInstrumentationService,\n} from '@conduit-client/service-instrumentation/v1';\nimport type { NamedCacheControllerService } from '@conduit-client/service-cache-control/v1';\nimport type {\n Key,\n CanonicalCacheControlMetadata,\n ReadonlyCache,\n CacheEntry,\n Cache,\n} from '@conduit-client/service-cache/v1';\n\nexport interface NormalizedLink {\n type: 'link';\n linkedKey: Key;\n}\n\ntype UnknownError = {\n type: 'unknown';\n error: Error;\n};\n\n/**\n * An error that occurs when a type tries to read from the cache, and the cache metadata does\n * not match the current type\n */\ntype IncorrectTypeError = {\n type: 'incorrectType';\n expectedType: string;\n actualType: string;\n};\n\n/**\n * An error that occurs when a type tries to read from the cache and data is missing\n */\ntype MissingDataError = {\n type: 'missingData';\n namespace: string;\n typeName: string;\n data: unknown;\n};\n\n/**\n * A union of errors that can be returned during the write process\n */\nexport type WriteErrors = Array<UnknownError>;\n\n/**\n * A union of errors that can be returned during the read process\n */\nexport type ReadErrors = Array<UnknownError | IncorrectTypeError | MissingDataError>;\n\nexport type ReadResult<Data> = Result<Data, ReadErrors>;\nexport type WriteResult<Data> = Result<Data, WriteErrors>;\n\n/**\n * A Type defines a collection of functions associated with a particular\n * data type. Types are not normalized but may contain normalized data.\n *\n * @typeParam Data TypeScript type that corresponds to the data\n * @typeParam NormalizedData TypeScript type that corresponds to the normalized data\n * @typeParam NormalizeInputData TypeScript type that corresponds to the data that the normalize method expects as input. Defaults to the same as Data\n */\nexport type TypeRepository<Data, ReadInput, WriteResponse, WriteInput> = {\n readonly namespace: string;\n readonly typeName: string;\n\n equals(x: Data, y: Data): boolean;\n\n /**\n * Normalizes data for storage.\n */\n write(cache: Cache, input: WriteInput): WriteResult<WriteResponse>;\n\n /**\n * De-normalizes a Type's data. A type could normalize into a Link, or return Data to the caller.\n * A caller must not be relying on the referenced type being normalized or not.\n * If denormalization fails, it will return undefined.\n */\n read(cache: ReadonlyCache, input: ReadInput): ReadResult<Data>;\n};\n\n/**\n * An InvalidatableType defines an invalidate function that takes a generic input,\n * and returns a promise that resolves when the invalidation is complete\n *\n * @typeParam T is another type to extend\n * @typeParam InvalidateInput is the input to run an invalidation\n */\nexport type Invalidatable<T extends TypeRepository<any, any, any, any>, InvalidateInput> = T & {\n /**\n *\n * @param configs Partial KeyConfigs for this Type (partial KeyConfig treats missing parts as wildcard)\n */\n invalidate(configs: InvalidateInput[]): Promise<void>;\n};\n\n/**\n * A QueryableType defines a collection of functions associated with a particular\n * data type that gets its own cache entry.\n *\n * @typeParam Data TypeScript type that corresponds to the data\n * @typeParam KeyConfig TypeScript type for the inputs needed to construct\n * a Key for the data\n * @typeParam NormalizeInputData TypeScript type that corresponds to the data that the normalize method expects as input. Defaults to the same as Data\n */\nexport type Queryable<T extends TypeRepository<any, any, any, any>, QueryInput> = T & {\n /**\n *\n * @param cache The cache to run the query against\n * @param query The query to run\n */\n query(cache: Cache, query: QueryInput): ReadResult<DataOf<T>>;\n};\n\nexport type DataOf<T extends TypeRepository<any, any, any, any>> =\n T extends TypeRepository<infer Data, any, any, any> ? Data : never;\nexport type ReadInputOf<T extends TypeRepository<any, any, any, any>> =\n T extends TypeRepository<any, infer ReadInput, any, any> ? ReadInput : never;\nexport type WriteResponseOf<T extends TypeRepository<any, any, any, any>> =\n T extends TypeRepository<any, any, infer WriteResponse, any> ? WriteResponse : never;\nexport type WriteResultOf<T extends TypeRepository<any, any, any, any>> = WriteResult<\n WriteResponseOf<T>\n>;\nexport type WriteInputOf<T extends TypeRepository<any, any, any, any>> =\n T extends TypeRepository<any, any, any, infer WriteInput> ? WriteInput : never;\nexport type InvalidateInputOf<T extends TypeRepository<any, any, any, any>> =\n T extends Invalidatable<any, infer InvalidateInput> ? InvalidateInput : never;\n\n/**\n * A utility method for ensuring that a given entry in the cache matches a type based on namespace and name information\n * @param cacheEntry the cache entry to validate against\n * @param type the type to validate the cache entry against\n * @returns\n */\nexport function isCacheEntryForType(\n cacheEntry: CacheEntry<unknown>,\n type: TypeRepository<unknown, unknown, unknown, unknown>\n): boolean {\n return (\n cacheEntry.metadata.type.namespace === type.namespace &&\n cacheEntry.metadata.type.name === type.typeName\n );\n}\n\nexport type KeyParamsOf<T> =\n T extends IdentifiableTypeRepository<any, any, infer KeyParams, any, any> ? KeyParams : never;\n\nexport type WriteInput<Data, Extension = Record<string, unknown>> = { data: Data } & Extension;\nexport type NormalizeDataInput<\n Data,\n NormalizedData,\n Extension extends Record<string, any> = Record<string, unknown>,\n> = WriteInput<Data, Extension & { existingNormalizedData?: NormalizedData }>;\n\nexport type ReadInput<Extension extends Record<string, any> = Record<string, unknown>> = {\n normalizedData: NormalizedLink;\n} & Extension;\nexport type ReadByKeyParamsInput<KeyParams> = {\n keyParams: KeyParams;\n};\nexport type DenormalizeInput<Extension extends Record<string, any> = Record<string, unknown>> =\n Extension & {\n key: Key;\n };\nexport type DenormalizeDataInput<\n NormalizedData,\n Extension extends Record<string, any> = Record<string, unknown>,\n> = Extension & { normalizedData: NormalizedData };\n\nexport type KeyParamQuery<KeyParams, ReadInputExtension> = {\n type: 'keyParams';\n keyParams: KeyParams;\n} & ReadInputExtension;\nexport type QueryFor<T> = T extends Queryable<any, infer Query> ? Query : never;\n\n/**\n * An abstract base class that implements Type, defining additional abstract properties and methods\n * for building a key for a given set of parameters, and defining the canonical cache control metadata\n * for any data written\n */\nexport abstract class IdentifiableTypeRepository<\n Data,\n NormalizedData,\n KeyParams,\n ReadInputExtension extends Record<string, any> = Record<string, unknown>,\n WriteInputExtension extends Record<string, any> = Record<string, unknown>,\n Query extends KeyParamQuery<KeyParams, ReadInputExtension> = KeyParamQuery<\n KeyParams,\n ReadInputExtension\n >,\n> implements\n Queryable<\n TypeRepository<\n Data,\n ReadInput<ReadInputExtension>,\n NormalizedLink,\n WriteInput<Data, WriteInputExtension>\n >,\n Query\n >\n{\n constructor(protected services: {}) {}\n abstract readonly cacheControl: CanonicalCacheControlMetadata;\n abstract readonly namespace: string;\n abstract readonly typeName: string;\n protected abstract denormalizeData(\n cache: ReadonlyCache,\n input: DenormalizeDataInput<NormalizedData, ReadInput<ReadInputExtension>>\n ): Result<Data, ReadErrors>;\n protected abstract normalizeData(\n cache: Cache,\n input: NormalizeDataInput<\n Data,\n NormalizedData,\n WriteInputExtension & { existingNormalizedData?: NormalizedData }\n >\n ): Result<NormalizedData, WriteErrors>;\n get cacheMetadata() {\n return {\n cacheControl: { ...this.cacheControl },\n type: { namespace: this.namespace, name: this.typeName },\n };\n }\n\n buildKey(params: KeyParams): Key {\n return `${this.namespace}::${this.typeName}(${stableJSONStringify(params)})`;\n }\n abstract buildKeyParams(input: WriteInput<Data, WriteInputExtension>): KeyParams;\n\n write(cache: Cache, input: WriteInput<Data, WriteInputExtension>): WriteResult<NormalizedLink> {\n // Identifiable types can have a key that is not the same as the input normalized data\n // so we need to build the key from the input, and then get the existing normalized data from the cache\n // and pass it to the normalizeData method. This should ALWAYS be done for identifiable types.\n const key = this.buildKey(this.buildKeyParams(input));\n const existingNormalizedData: NormalizedData | undefined = cache.get<NormalizedData>(key)\n ?.value as any;\n const normalized = this.normalizeData(cache, { ...input, existingNormalizedData });\n if (normalized.isErr()) {\n return err(normalized.error);\n }\n\n cache.set<NormalizedData>(key, {\n value: normalized.value,\n metadata: this.cacheMetadata,\n });\n\n return ok({\n type: 'link',\n linkedKey: key,\n } as const);\n }\n\n protected validateEntry(entry: CacheEntry<unknown>): Result<undefined, ReadErrors> {\n if (!isCacheEntryForType(entry, this)) {\n return err([\n {\n type: 'incorrectType',\n expectedType: this.typeName,\n actualType: entry.metadata.type.name,\n },\n ]);\n }\n return ok(undefined);\n }\n\n query(cache: Cache, query: Query): ReadResult<Data> {\n return this.read(cache, {\n ...query,\n normalizedData: { type: 'link', linkedKey: this.buildKey(query.keyParams) },\n });\n }\n\n read(cache: ReadonlyCache, input: ReadInput<ReadInputExtension>): Result<Data, ReadErrors> {\n return this.denormalize(cache, { ...input, key: input.normalizedData.linkedKey });\n }\n\n denormalize(cache: ReadonlyCache, input: DenormalizeInput<ReadInput<ReadInputExtension>>) {\n const cacheEntry = cache.get<NormalizedData>(input.key, {\n copy: false,\n }) as CacheEntry<NormalizedData>;\n\n if (cacheEntry) {\n const validationResult = this.validateEntry(cacheEntry);\n if (validationResult.isErr()) {\n return err([...validationResult.error]);\n }\n const normalizedData = cacheEntry.value;\n return this.denormalizeData(cache, { ...input, normalizedData });\n }\n\n return err([this.buildMissingDataError()]);\n }\n\n equals(x: Data, y: Data) {\n return deepEquals(x, y);\n }\n\n buildMissingDataError(): MissingDataError {\n return {\n type: 'missingData',\n typeName: this.typeName,\n namespace: this.namespace,\n data: undefined,\n };\n }\n}\n\n/**\n * An abstract base class that extends IdentifiableType, adding the additional constraint that the\n * only data required to write to the cache is an instance of the data itself, i.e. the identity of\n * the data lives inside the data\n */\nexport abstract class SelfIdentifiableTypeRepository<\n Data,\n NormalizedData,\n KeyParams,\n ReadInputExtension extends Record<string, any> = Record<string, unknown>,\n WriteInputExtension extends Record<string, any> = Record<string, unknown>,\n> extends IdentifiableTypeRepository<\n Data,\n NormalizedData,\n KeyParams,\n ReadInputExtension,\n WriteInputExtension\n> {}\n\n/**\n * An abstract base class that extends SelfIdentifiableType. Adds the constraint that the structure of\n * the key parameters must be a flat object, with no objects nested. This class provides invalidation and keybuilding 'for free'.\n * Should be preferred over IdentifiableType and SelfIdentifiableType\n */\nexport abstract class FlattenedKeySelfIdentifiableTypeRepository<\n Data,\n NormalizedData,\n KeyParams extends FlattenedKeyParams,\n ReadInputExtension extends Record<string, any> = Record<string, unknown>,\n WriteInputExtension extends Record<string, any> = Record<string, unknown>,\n >\n extends SelfIdentifiableTypeRepository<\n Data,\n NormalizedData,\n KeyParams,\n ReadInputExtension,\n WriteInputExtension\n >\n implements\n Invalidatable<\n SelfIdentifiableTypeRepository<\n Data,\n NormalizedData,\n KeyParams,\n ReadInputExtension,\n WriteInputExtension\n >,\n Partial<KeyParams>\n >\n{\n instrumentationAttributes: InstrumentationAttributes | undefined;\n constructor(\n protected services: NamedCacheControllerService &\n NamedPubSubService &\n Partial<NamedInstrumentationService>\n ) {\n super(services);\n }\n\n abstract readonly keySchema: string[];\n\n async invalidate(configs: Partial<KeyParams>[]): Promise<void> {\n const startTime = this.services.instrumentation\n ? this.services.instrumentation.currentTimeMs()\n : 0;\n const result = this.runInvalidate(configs).then(() => {\n if (this.services.instrumentation) {\n const duration = this.services.instrumentation.currentTimeMs() - startTime;\n this.collectInstrumentation(duration);\n }\n });\n return result;\n }\n\n protected collectInstrumentation(duration: number) {\n if (this.services.instrumentation) {\n const meter = this.services.instrumentation.metrics.getMeter('onestore');\n const attributes = {\n ...this.instrumentationAttributes,\n namespace: this.namespace,\n typeName: this.typeName,\n };\n meter\n .createCounter<InstrumentationAttributes>(`type.invalidate.count`)\n .add(1, attributes);\n meter\n .createHistogram<InstrumentationAttributes>(`type.invalidate.duration`)\n .record(duration, attributes);\n }\n }\n\n protected async runInvalidate(configs: Partial<KeyParams>[]): Promise<void> {\n const regex = buildRegexForNamespacedKeys(\n this.namespace,\n this.typeName,\n configs,\n this.keySchema\n );\n\n const results: Set<Key> = new Set();\n for await (const entry of this.services.cacheController.findAndModify(\n { key: { $regex: regex } },\n { type: 'invalidate' }\n )) {\n results.add(entry);\n }\n if (results.size > 0) {\n await this.services.pubSub.publish({\n type: 'cacheInvalidation',\n data: results,\n });\n }\n }\n\n buildKey(config: KeyParams): Key {\n return buildNamespacedTypeKey(this.namespace, this.typeName, config, this.keySchema);\n }\n}\ntype ScalarKeyValue = string | number | boolean | null | undefined;\ntype FlattenedKeyParams = Record<string, ScalarKeyValue | ScalarKeyValue[]>;\nexport function buildNamespacedTypeKey(\n namespace: string,\n typeName: string,\n keyConfig: FlattenedKeyParams,\n keySchema: string[]\n): string {\n const keyParts = keySchema\n .map((key) => {\n const value = keyConfig[key];\n return `${ArrayIsArray(value) ? value.join() : value}`;\n })\n .join('|');\n return `${namespace}::${typeName}(${keyParts})`;\n}\n\nexport function buildRegexForNamespacedKeys(\n namespace: string,\n typeName: string,\n partialKeyConfigs: Array<FlattenedKeyParams>,\n keySchema: string[]\n): RegExp {\n const patterns = partialKeyConfigs.map((partial) => {\n // Build a regex pattern for a single KeyConfig\n const requiredParts = keySchema\n .map((key) => {\n if (!(key in partial)) {\n return `[^|]*?`; // Allow any value or absence of value (optional match)\n }\n const value = partial[key];\n return `(?:${(ArrayIsArray(value) ? value.join() : String(value)).replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')})`;\n })\n .join('\\\\|'); // Ensure properties are matched in order with literal separator\n\n return `^${namespace}::${typeName}\\\\((?:${requiredParts})\\\\)$`;\n });\n\n return new RegExp(`^(?:${patterns.join('|')})$`); // Apply OR logic across separate partial configs\n}\n\nexport function extractReadWriteData<Data>(\n result: ReadResult<Data> | WriteResult<Data>,\n errorCollector: any[]\n): Data | undefined {\n if (result.isOk()) {\n return result.value;\n }\n\n errorCollector.push(...result.error);\n return undefined;\n}\n\nexport function buildReadWriteResult<Data, Errors extends WriteErrors | ReadErrors>(\n data: unknown,\n errors: Errors\n): Result<Data, Errors> {\n if (errors.length > 0) {\n return err(errors);\n }\n\n return ok(data as Data);\n}\n\n// This type is the normalized representation of union data (either a pure union, or a discriminated union)\n// This data shape will live in the cache, and contains the discriminator (i.e. an identity of what type of data this entry represents)\n// and the normalized data for that entry\nexport type UnionRepresentation<Discriminator extends string, Data> = {\n discriminator: Discriminator;\n data: Data;\n};\n\n// A utility type that represents all the information required for normalizing and\n// denormalizing types into a cache, including the discriminator value, the\n// canonical representation (Data) and the normalized representation (Normalized)\nexport type DiscriminatorMap<Discriminator extends string, Data, Normalized> = {\n [k in Discriminator]: {\n data: Data;\n normalized: Normalized;\n };\n};\n\n// A utility type for inferring the union of normalized representations\n// possible based on a given DiscriminatorType\nexport type NormalizedRepresentationOf<M extends DiscriminatorMap<any, any, any>> =\n M extends DiscriminatorMap<infer D, any, any>\n ? { [k in D]: UnionRepresentation<k, M[k]['normalized']> }[D]\n : never;\n\n// A utility type for inferring the discriminator -> denormalize function map\n// required by the `denormalizeUnionRepresentation` function\nexport type DenormalizeMapOf<M extends DiscriminatorMap<any, any, any>> =\n M extends DiscriminatorMap<infer D, any, any>\n ? { [k in D]: (normalized: M[k]['normalized']) => M[k]['data'] }\n : never;\n\n// A utility type for inferring the canonical data type union based on\n// a DiscriminatorMap type\nexport type UnionDataOf<M extends DiscriminatorMap<any, any, any>> =\n M extends DiscriminatorMap<any, infer D, any> ? D : never;\n\n// A utility type for inferring the discriminator string union from a\n// given discriminator map type\nexport type DiscriminatorOf<M extends DiscriminatorMap<any, any, any>> =\n M extends DiscriminatorMap<infer D, any, any> ? D : never;\n\n// A utilty type for inferring the test/normalize map type from a\n// discriminator map type to be used in the `normalizeUnionRepresentation` function\nexport type DiscriminatorNormalizeFunctionsMap<M extends DiscriminatorMap<any, any, any>> =\n M extends DiscriminatorMap<infer D, any, any>\n ? {\n [k in D]: {\n test: (data: M[D]['data']) => boolean;\n normalize: (data: M[k]['data']) => M[k]['normalized'];\n };\n }\n : never;\n\n/**\n *\n * @param normalized The normalized data representation, including the discriminator value\n * @param denormalizeMap A map of discriminator -> denormalize functions to be used based on the datas discriminator\n * @returns\n */\nexport function denormalizeUnionRepresentation<\n M extends DiscriminatorMap<string, unknown, unknown>,\n>(normalized: NormalizedRepresentationOf<M>, denormalizeMap: DenormalizeMapOf<M>): UnionDataOf<M> {\n const discriminator = normalized.discriminator as DiscriminatorOf<M>;\n return denormalizeMap[discriminator](normalized.data) as UnionDataOf<M>;\n}\n\n/**\n *\n * @param data The canonical/denormalized data representation\n * @param normalizeTestMap A map of discriminator to test method which return a boolean indicating if the data adheres to this \"discriminator\"\n * and a normalize function to call if the data matches this discriminator\n * @returns\n */\nexport function normalizeUnionRepresentation<M extends DiscriminatorMap<string, unknown, unknown>>(\n data: UnionDataOf<M>,\n normalizeTestMap: DiscriminatorNormalizeFunctionsMap<M>\n): NormalizedRepresentationOf<M> {\n for (const discriminator of Object.keys(normalizeTestMap) as DiscriminatorOf<M>[]) {\n const discriminatorLogic = normalizeTestMap[discriminator];\n if (discriminatorLogic.test(data)) {\n return {\n discriminator,\n data: discriminatorLogic.normalize(data),\n } as unknown as NormalizedRepresentationOf<M>;\n }\n }\n\n throw new Error('No matching union member found');\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduit-client/type-normalization",
3
- "version": "3.12.0",
3
+ "version": "3.13.1",
4
4
  "private": false,
5
5
  "description": "Luvio Normalized Cache Control Command",
6
6
  "type": "module",
@@ -32,9 +32,9 @@
32
32
  "watch": "npm run build --watch"
33
33
  },
34
34
  "dependencies": {
35
- "@conduit-client/service-cache": "3.12.0",
36
- "@conduit-client/service-cache-control": "3.12.0",
37
- "@conduit-client/utils": "3.12.0"
35
+ "@conduit-client/service-cache": "3.13.1",
36
+ "@conduit-client/service-cache-control": "3.13.1",
37
+ "@conduit-client/utils": "3.13.1"
38
38
  },
39
39
  "volta": {
40
40
  "extends": "../../../package.json"