Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/json-language-features/client/src/node/schemaCache.ts
3322 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { promises as fs } from 'fs';
7
import * as path from 'path';
8
import { createHash } from 'crypto';
9
import { Memento } from 'vscode';
10
11
interface CacheEntry {
12
etag: string;
13
fileName: string;
14
updateTime: number;
15
}
16
17
interface CacheInfo {
18
[schemaUri: string]: CacheEntry;
19
}
20
21
const MEMENTO_KEY = 'json-schema-cache';
22
23
export class JSONSchemaCache {
24
private cacheInfo: CacheInfo;
25
26
constructor(private readonly schemaCacheLocation: string, private readonly globalState: Memento) {
27
const infos = globalState.get<CacheInfo>(MEMENTO_KEY, {}) as CacheInfo;
28
const validated: CacheInfo = {};
29
for (const schemaUri in infos) {
30
const { etag, fileName, updateTime } = infos[schemaUri];
31
if (typeof etag === 'string' && typeof fileName === 'string' && typeof updateTime === 'number') {
32
validated[schemaUri] = { etag, fileName, updateTime };
33
}
34
}
35
this.cacheInfo = validated;
36
}
37
38
getETag(schemaUri: string): string | undefined {
39
return this.cacheInfo[schemaUri]?.etag;
40
}
41
42
getLastUpdatedInHours(schemaUri: string): number | undefined {
43
const updateTime = this.cacheInfo[schemaUri]?.updateTime;
44
if (updateTime !== undefined) {
45
return (new Date().getTime() - updateTime) / 1000 / 60 / 60;
46
}
47
return undefined;
48
}
49
50
async putSchema(schemaUri: string, etag: string, schemaContent: string): Promise<void> {
51
try {
52
const fileName = getCacheFileName(schemaUri);
53
await fs.writeFile(path.join(this.schemaCacheLocation, fileName), schemaContent);
54
const entry: CacheEntry = { etag, fileName, updateTime: new Date().getTime() };
55
this.cacheInfo[schemaUri] = entry;
56
} catch (e) {
57
delete this.cacheInfo[schemaUri];
58
} finally {
59
await this.updateMemento();
60
}
61
}
62
63
async getSchemaIfUpdatedSince(schemaUri: string, expirationDurationInHours: number): Promise<string | undefined> {
64
const lastUpdatedInHours = this.getLastUpdatedInHours(schemaUri);
65
if (lastUpdatedInHours !== undefined && (lastUpdatedInHours < expirationDurationInHours)) {
66
return this.loadSchemaFile(schemaUri, this.cacheInfo[schemaUri], false);
67
}
68
return undefined;
69
}
70
71
async getSchema(schemaUri: string, etag: string, etagValid: boolean): Promise<string | undefined> {
72
const cacheEntry = this.cacheInfo[schemaUri];
73
if (cacheEntry) {
74
if (cacheEntry.etag === etag) {
75
return this.loadSchemaFile(schemaUri, cacheEntry, etagValid);
76
} else {
77
this.deleteSchemaFile(schemaUri, cacheEntry);
78
}
79
}
80
return undefined;
81
}
82
83
private async loadSchemaFile(schemaUri: string, cacheEntry: CacheEntry, isUpdated: boolean): Promise<string | undefined> {
84
const cacheLocation = path.join(this.schemaCacheLocation, cacheEntry.fileName);
85
try {
86
const content = (await fs.readFile(cacheLocation)).toString();
87
if (isUpdated) {
88
cacheEntry.updateTime = new Date().getTime();
89
}
90
return content;
91
} catch (e) {
92
delete this.cacheInfo[schemaUri];
93
return undefined;
94
} finally {
95
await this.updateMemento();
96
}
97
}
98
99
private async deleteSchemaFile(schemaUri: string, cacheEntry: CacheEntry): Promise<void> {
100
const cacheLocation = path.join(this.schemaCacheLocation, cacheEntry.fileName);
101
delete this.cacheInfo[schemaUri];
102
await this.updateMemento();
103
try {
104
await fs.rm(cacheLocation);
105
} catch (e) {
106
// ignore
107
}
108
}
109
110
111
// for debugging
112
public getCacheInfo() {
113
return this.cacheInfo;
114
}
115
116
private async updateMemento() {
117
try {
118
await this.globalState.update(MEMENTO_KEY, this.cacheInfo);
119
} catch (e) {
120
// ignore
121
}
122
}
123
124
public async clearCache(): Promise<string[]> {
125
const uris = Object.keys(this.cacheInfo);
126
try {
127
const files = await fs.readdir(this.schemaCacheLocation);
128
for (const file of files) {
129
try {
130
await fs.unlink(path.join(this.schemaCacheLocation, file));
131
} catch (_e) {
132
// ignore
133
}
134
}
135
} catch (e) {
136
// ignore
137
} finally {
138
139
this.cacheInfo = {};
140
await this.updateMemento();
141
}
142
return uris;
143
}
144
}
145
function getCacheFileName(uri: string): string {
146
return `${createHash('sha256').update(uri).digest('hex')}.schema.json`;
147
}
148
149