fork of hey-api/openapi-ts because I need some additional things
fork

Configure Feed

Select the types of activity you want to include in your feed.

at feat/use-mutation-hooks 181 lines 6.1 kB view raw
1import type { ISymbolMeta } from '../extensions'; 2import { Symbol } from './symbol'; 3import type { ISymbolIdentifier, ISymbolIn, ISymbolRegistry } from './types'; 4 5type IndexEntry = [string, unknown]; 6type IndexKeySpace = ReadonlyArray<IndexEntry>; 7type QueryCacheKey = string; 8type SymbolId = number; 9 10export class SymbolRegistry implements ISymbolRegistry { 11 private _id: SymbolId = 0; 12 private _indices: Map<IndexEntry[0], Map<IndexEntry[1], Set<SymbolId>>> = new Map(); 13 private _queryCache: Map<QueryCacheKey, ReadonlyArray<SymbolId>> = new Map(); 14 private _queryCacheDependencies: Map<QueryCacheKey, Set<QueryCacheKey>> = new Map(); 15 private _registered: Set<SymbolId> = new Set(); 16 private _stubs: Set<SymbolId> = new Set(); 17 private _stubCache: Map<QueryCacheKey, SymbolId> = new Map(); 18 private _values: Map<SymbolId, Symbol> = new Map(); 19 20 get(identifier: ISymbolIdentifier): Symbol | undefined { 21 return typeof identifier === 'number' 22 ? this._values.get(identifier) 23 : this.query(identifier)[0]; 24 } 25 26 isRegistered(identifier: ISymbolIdentifier): boolean { 27 const symbol = this.get(identifier); 28 return symbol ? this._registered.has(symbol.id) : false; 29 } 30 31 get nextId(): SymbolId { 32 return this._id++; 33 } 34 35 query(filter: ISymbolMeta): ReadonlyArray<Symbol> { 36 const cacheKey = this.buildCacheKey(filter); 37 const cachedIds = this._queryCache.get(cacheKey); 38 if (cachedIds) { 39 return cachedIds.map((symbolId) => this._values.get(symbolId)!); 40 } 41 const sets: Array<Set<SymbolId>> = []; 42 const indexKeySpace = this.buildIndexKeySpace(filter); 43 const cacheDependencies = new Set<QueryCacheKey>(); 44 let missed = false; 45 for (const indexEntry of indexKeySpace) { 46 cacheDependencies.add(this.serializeIndexEntry(indexEntry)); 47 const values = this._indices.get(indexEntry[0]); 48 if (!values) { 49 missed = true; 50 break; 51 } 52 const set = values.get(indexEntry[1]); 53 if (!set) { 54 missed = true; 55 break; 56 } 57 sets.push(set); 58 } 59 if (missed || !sets.length) { 60 this._queryCacheDependencies.set(cacheKey, cacheDependencies); 61 this._queryCache.set(cacheKey, []); 62 return []; 63 } 64 let result = new Set(sets[0]); 65 for (const set of sets.slice(1)) { 66 result = new Set([...result].filter((symbolId) => set.has(symbolId))); 67 } 68 const resultIds = [...result]; 69 this._queryCacheDependencies.set(cacheKey, cacheDependencies); 70 this._queryCache.set(cacheKey, resultIds); 71 return resultIds.map((symbolId) => this._values.get(symbolId)!); 72 } 73 74 reference(meta: ISymbolMeta): Symbol { 75 const [registered] = this.query(meta); 76 if (registered) return registered; 77 78 const cacheKey = this.buildCacheKey(meta); 79 const cachedId = this._stubCache.get(cacheKey); 80 if (cachedId !== undefined) return this._values.get(cachedId)!; 81 82 const stub = new Symbol({ meta, name: '' }, this.nextId); 83 84 this._values.set(stub.id, stub); 85 this._stubs.add(stub.id); 86 this._stubCache.set(cacheKey, stub.id); 87 return stub; 88 } 89 90 register(symbol: ISymbolIn): Symbol { 91 const result = new Symbol(symbol, this.nextId); 92 93 this._values.set(result.id, result); 94 this._registered.add(result.id); 95 96 if (result.meta) { 97 const indexKeySpace = this.buildIndexKeySpace(result.meta); 98 this.indexSymbol(result.id, indexKeySpace); 99 this.invalidateCache(indexKeySpace); 100 this.replaceStubs(result, indexKeySpace); 101 } 102 103 return result; 104 } 105 106 *registered(): IterableIterator<Symbol> { 107 for (const id of this._registered.values()) { 108 yield this._values.get(id)!; 109 } 110 } 111 112 private buildCacheKey(filter: ISymbolMeta): QueryCacheKey { 113 const indexKeySpace = this.buildIndexKeySpace(filter); 114 return indexKeySpace 115 .map((indexEntry) => this.serializeIndexEntry(indexEntry)) 116 .sort() // ensure order-insensitivity 117 .join('|'); 118 } 119 120 private buildIndexKeySpace(meta: ISymbolMeta, prefix = ''): IndexKeySpace { 121 const entries: Array<IndexEntry> = []; 122 for (const [key, value] of Object.entries(meta)) { 123 const path = prefix ? `${prefix}.${key}` : key; 124 if (value && typeof value === 'object' && !Array.isArray(value)) { 125 entries.push(...this.buildIndexKeySpace(value as ISymbolMeta, path)); 126 } else { 127 entries.push([path, value]); 128 } 129 } 130 return entries; 131 } 132 133 private indexSymbol(symbolId: SymbolId, indexKeySpace: IndexKeySpace): void { 134 for (const [key, value] of indexKeySpace) { 135 if (!this._indices.has(key)) this._indices.set(key, new Map()); 136 const values = this._indices.get(key)!; 137 const set = values.get(value) ?? new Set(); 138 set.add(symbolId); 139 values.set(value, set); 140 } 141 } 142 143 private invalidateCache(indexKeySpace: IndexKeySpace): void { 144 const changed = indexKeySpace.map((indexEntry) => this.serializeIndexEntry(indexEntry)); 145 for (const [cacheKey, cacheDependencies] of this._queryCacheDependencies.entries()) { 146 for (const key of changed) { 147 if (cacheDependencies.has(key)) { 148 this._queryCacheDependencies.delete(cacheKey); 149 this._queryCache.delete(cacheKey); 150 break; 151 } 152 } 153 } 154 } 155 156 private isSubset(sub: IndexKeySpace, sup: IndexKeySpace): boolean { 157 const supMap = new Map(sup); 158 for (const [key, value] of sub) { 159 if (!supMap.has(key) || supMap.get(key) !== value) { 160 return false; 161 } 162 } 163 return true; 164 } 165 166 private replaceStubs(symbol: Symbol, indexKeySpace: IndexKeySpace): void { 167 for (const stubId of this._stubs.values()) { 168 const stub = this._values.get(stubId); 169 if (stub?.meta && this.isSubset(this.buildIndexKeySpace(stub.meta), indexKeySpace)) { 170 const cacheKey = this.buildCacheKey(stub.meta); 171 this._stubCache.delete(cacheKey); 172 this._stubs.delete(stubId); 173 stub.setCanonical(symbol); 174 } 175 } 176 } 177 178 private serializeIndexEntry(indexEntry: IndexEntry): string { 179 return `${indexEntry[0]}:${JSON.stringify(indexEntry[1])}`; 180 } 181}