Files
CherryHQ-cherry-studio/packages/shared/data/cache/templateKey.ts
fullex ad2b402c04 feat(cache-service): add main-process subscribe API with template key support
Enables main-process services to react to cache changes without writer-side
wiring — unblocks the web-search/OCR provider rotation use case where new
providers would otherwise require manual hook-ups at every write site.

Also unifies equality across main and renderer on lodash.isEqual (fixing
redundant cross-window broadcasts when Record/Array values are rebuilt on
every write) and extracts template utilities to the shared package so both
processes use one implementation.
2026-04-22 22:04:06 -07:00

71 lines
2.2 KiB
TypeScript

import type { SharedCacheSchema } from './cacheSchemas'
import { DefaultSharedCache } from './cacheSchemas'
/**
* Checks if a schema key is a template key (contains `${...}` placeholder).
*
* @example
* ```ts
* isTemplateKey('scroll.position.${id}') // true
* isTemplateKey('app.user.avatar') // false
* ```
*/
export function isTemplateKey(key: string): boolean {
return key.includes('${') && key.includes('}')
}
/**
* Converts a template key pattern into a RegExp for matching concrete keys.
*
* Each `${variable}` placeholder expands to `([\w\-]+)` — matches the same
* character set permitted by the cache key naming convention (ASCII word
* chars plus hyphens). Non-ASCII characters, dots, and colons are rejected
* by design: this keeps the subscription layer aligned with the `data-schema-key/valid-key`
* ESLint rule. The placeholder variable name itself is ignored at runtime.
*
* @example
* ```ts
* const regex = templateToRegex('scroll.position.${id}')
* regex.test('scroll.position.topic123') // true
* regex.test('scroll.position.topic-123') // true
* regex.test('scroll.position.') // false
* regex.test('other.key.123') // false
* ```
*/
export function templateToRegex(template: string): RegExp {
const escaped = template.replace(/[.*+?^${}()|[\]\\]/g, (match) => {
if (match === '$' || match === '{' || match === '}') {
return match
}
return '\\' + match
})
const pattern = escaped.replace(/\$\{[^}]+\}/g, '([\\w\\-]+)')
return new RegExp(`^${pattern}$`)
}
/**
* Finds the shared schema key that matches a given concrete key.
*
* Returns the exact schema key (fixed or template pattern), not the concrete
* instance — callers use it to look up the template's default value.
*/
export function findMatchingSharedCacheSchemaKey(key: string): keyof SharedCacheSchema | undefined {
if (key in DefaultSharedCache) {
return key as keyof SharedCacheSchema
}
const schemaKeys = Object.keys(DefaultSharedCache) as Array<keyof SharedCacheSchema>
for (const schemaKey of schemaKeys) {
if (isTemplateKey(schemaKey as string)) {
const regex = templateToRegex(schemaKey as string)
if (regex.test(key)) {
return schemaKey
}
}
}
return undefined
}