diff --git a/package-lock.json b/package-lock.json index 37e784d1..69b79c2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,12 +15,10 @@ "@vscode/extension-telemetry": "^1.5.1", "@xmldom/xmldom": "^0.9.9", "axios": "^1.15.0", - "core-js": "^3.41.0", "iconv-lite": "^0.7.2", "istextorbinary": "^7.0.0", "minimatch": "^10.2.5", "node-cmd": "^5.0.0", - "vscode-cache": "^0.3.0", "ws": "^8.20.0" }, "devDependencies": { @@ -1958,16 +1956,6 @@ "dev": true, "license": "MIT" }, - "node_modules/core-js": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.41.0.tgz", - "integrity": "sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -6339,11 +6327,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "node_modules/vscode-cache": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/vscode-cache/-/vscode-cache-0.3.0.tgz", - "integrity": "sha1-fMOWZOvZnTcDAwaLibxMlWFGZ8A=" - }, "node_modules/watchpack": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", diff --git a/package.json b/package.json index 3bdf2252..46ef9e71 100644 --- a/package.json +++ b/package.json @@ -1764,12 +1764,10 @@ "@vscode/extension-telemetry": "^1.5.1", "@xmldom/xmldom": "^0.9.9", "axios": "^1.15.0", - "core-js": "^3.41.0", "iconv-lite": "^0.7.2", "istextorbinary": "^7.0.0", "minimatch": "^10.2.5", "node-cmd": "^5.0.0", - "vscode-cache": "^0.3.0", "ws": "^8.20.0" }, "extensionDependencies": [ diff --git a/src/api/index.ts b/src/api/index.ts index 48cd7175..4e1d5dd2 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,7 +1,6 @@ import axios from "axios"; import * as httpsModule from "https"; import * as vscode from "vscode"; -import * as Cache from "vscode-cache"; import * as semver from "semver"; import { getResolvedConnectionSpec, @@ -21,9 +20,12 @@ const DEFAULT_SERVER_VERSION = "2016.2.0"; import * as Atelier from "./atelier"; import { isfsConfig } from "../utils/FileProviderUtil"; -// Map of the authRequest promises for each username@host:port target to avoid concurrency issues +// Map of the authRequest promises for each username@host:port/pathPrefix target to avoid concurrency issues const authRequestMap = new Map>(); +/** Map of `username@host:port/pathPrefix` to cookies */ +const cookiesMap = new Map(); + interface ConnectionSettings { serverName: string; active: boolean; @@ -170,12 +172,11 @@ export class AtelierAPI { } public get cookies(): string[] { - const cookies = this.cache.get("cookies", []); - return cookies; + return cookiesMap.get(this.mapKey()) ?? []; } - public async clearCookies(): Promise { - await this.cache.put("cookies", []); + public clearCookies(): void { + cookiesMap.delete(this.mapKey()); } public xdebugUrl(): string { @@ -191,8 +192,9 @@ export class AtelierAPI { : ""; } - public async updateCookies(newCookies: string[]): Promise { - const cookies = this.cache.get("cookies", []); + public updateCookies(newCookies: string[]): void { + const mapKey = this.mapKey(); + const cookies = cookiesMap.get(mapKey) ?? []; newCookies.forEach((cookie) => { const [cookieName] = cookie.split("="); const index = cookies.findIndex((el) => el.startsWith(cookieName)); @@ -202,7 +204,17 @@ export class AtelierAPI { cookies.push(cookie); } }); - await this.cache.put("cookies", cookies); + cookiesMap.set(mapKey, cookies); + } + + /** Return the key for getting values from connection-specific Maps for this connection */ + private mapKey(): string { + const { host, port, username } = this.config; + let pathPrefix = this._config.pathPrefix || ""; + if (pathPrefix.length && !pathPrefix.startsWith("/")) { + pathPrefix = "/" + pathPrefix; + } + return `${username}@${host}:${port}${pathPrefix}`; } private setConnection(workspaceFolderName: string, namespace?: string): void { @@ -284,11 +296,6 @@ export class AtelierAPI { } } - private get cache(): Cache { - const { host, port } = this.config; - return new Cache(extensionContext, `API:${host}:${port}`); - } - public get connInfo(): string { const { port, docker, dockerService } = this.config; return (docker ? "docker" + (dockerService ? `:${dockerService}:${port}` : "") : this.serverId) + `[${this.ns}]`; @@ -358,9 +365,9 @@ export class AtelierAPI { path = encodeURI(`${pathPrefix}/api/atelier/${path || ""}`) + buildParams(); const cookies = this.cookies; - const target = `${username}@${host}:${port}`; + const mapKey = this.mapKey(); let auth: Promise; - let authRequest = authRequestMap.get(target); + let authRequest = authRequestMap.get(mapKey); if (cookies.length || (method === "HEAD" && !originalPath)) { auth = Promise.resolve(cookies); @@ -372,7 +379,7 @@ export class AtelierAPI { if (!authRequest) { // Recursion point authRequest = this.request(0, "HEAD", undefined, undefined, undefined, undefined, options); - authRequestMap.set(target, authRequest); + authRequestMap.set(mapKey, authRequest); } auth = authRequest; } @@ -438,7 +445,7 @@ export class AtelierAPI { }; } if (response.status === 401) { - authRequestMap.delete(target); + authRequestMap.delete(mapKey); if (this.wsOrFile && !checkingConnection) { setTimeout(() => { checkConnection( @@ -453,7 +460,7 @@ export class AtelierAPI { await this.updateCookies(response.headers["set-cookie"] || []); if (method === "HEAD") { if (!originalPath) { - authRequestMap.delete(target); + authRequestMap.delete(mapKey); return this.cookies; } else if (response.status >= 400) { // The HEAD /doc request errored out @@ -552,7 +559,7 @@ export class AtelierAPI { outputChannel.appendLine(`+- END ----------------------------------------------`); } // always discard the cached authentication promise - authRequestMap.delete(target); + authRequestMap.delete(mapKey); // In some cases schedule an automatic retry. // ENOTFOUND occurs if, say, the VPN to the server's network goes down. diff --git a/src/utils/classDefinition.ts b/src/utils/classDefinition.ts index 42c835ed..d4e841ca 100644 --- a/src/utils/classDefinition.ts +++ b/src/utils/classDefinition.ts @@ -1,8 +1,6 @@ import * as vscode from "vscode"; -import * as Cache from "vscode-cache"; import { onlyUnique } from "."; import { AtelierAPI } from "../api"; -import { extensionContext } from "../extension"; import { DocumentContentProvider } from "../providers/DocumentContentProvider"; export class ClassDefinition { @@ -15,7 +13,6 @@ export class ClassDefinition { } private _className: string; private _classFileName: string; - private _cache; private _workspaceFolder: string; private _namespace: string; @@ -27,26 +24,14 @@ export class ClassDefinition { } this._className = ClassDefinition.normalizeClassName(className, false); this._classFileName = ClassDefinition.normalizeClassName(className, true); - this._cache = new Cache(extensionContext, this._classFileName); } public async getDocument(): Promise { return vscode.workspace.openTextDocument(this.uri); } - public store(kind: string, data: any): any { - return this._cache.put(kind, data, 36000).then(() => data); - } - - public load(kind: string): any { - return this._cache.get(kind); - } - public async methods(scope: "any" | "class" | "instance" = "any"): Promise { - const methods = this.load("methods-" + scope) || []; - if (methods.length) { - return Promise.resolve(methods); - } + const methods = []; const filterScope = (method) => scope === "any" || method.scope === scope; const api = new AtelierAPI(this.uri); const getMethods = (content) => { @@ -58,16 +43,13 @@ export class ClassDefinition { if (extend.length) { return api.actionIndex(extend).then((data) => getMethods(data.result.content)); } - return this.store("methods-" + scope, methods.filter(filterScope).filter(onlyUnique).sort()); + return methods.filter(filterScope).filter(onlyUnique).sort(); }; return api.actionIndex([this._classFileName]).then((data) => getMethods(data.result.content)); } public async properties(): Promise { - const properties = this.load("properties") || []; - if (properties.length) { - return Promise.resolve(properties); - } + const properties = []; const api = new AtelierAPI(this.uri); const getProperties = (content) => { const extend = []; @@ -78,16 +60,13 @@ export class ClassDefinition { if (extend.length) { return api.actionIndex(extend).then((data) => getProperties(data.result.content)); } - return this.store("properties", properties.filter(onlyUnique).sort()); + return properties.filter(onlyUnique).sort(); }; return api.actionIndex([this._classFileName]).then((data) => getProperties(data.result.content)); } public async parameters(): Promise { - const parameters = this.load("parameters") || []; - if (parameters.length) { - return Promise.resolve(parameters); - } + const parameters = []; const api = new AtelierAPI(this.uri); const getParameters = (content) => { const extend = []; @@ -98,40 +77,30 @@ export class ClassDefinition { if (extend.length) { return api.actionIndex(extend).then((data) => getParameters(data.result.content)); } - return this.store("parameters", parameters.filter(onlyUnique).sort()); + return parameters.filter(onlyUnique).sort(); }; return api.actionIndex([this._classFileName]).then((data) => getParameters(data.result.content)); } public async super(): Promise { - const superList = this.load("super"); - if (superList) { - return Promise.resolve(superList); - } const api = new AtelierAPI(this.uri); const sql = `SELECT PrimarySuper FROM %Dictionary.CompiledClass WHERE Name %inlist (SELECT $LISTFROMSTRING(Super, ',') FROM %Dictionary.CompiledClass WHERE Name = ?)`; return api .actionQuery(sql, [this._className]) - .then( - (data) => - data.result.content - .reduce( - (list: string[], el: { PrimarySuper: string }) => - list.concat(el.PrimarySuper.split("~").filter((item) => item.length)), - [] - ) - .filter((name: string) => name !== this._className) - // .filter(name => !['%Library.Base', '%Library.SystemBase'].includes(name)) + .then((data) => + data.result.content + .reduce( + (list: string[], el: { PrimarySuper: string }) => + list.concat(el.PrimarySuper.split("~").filter((item) => item.length)), + [] + ) + .filter((name: string) => name !== this._className) ) - .then((data) => this.store("super", data)); + .then((data) => data); } public async includeCode(): Promise { - const includeCode = this.load("includeCode"); - if (includeCode) { - return Promise.resolve(includeCode); - } const api = new AtelierAPI(this.uri); const sql = `SELECT LIST(IncludeCode) List FROM %Dictionary.CompiledClass WHERE Name %INLIST ( SELECT $LISTFROMSTRING(PrimarySuper, '~') FROM %Dictionary.CompiledClass WHERE Name = ?)`; @@ -144,7 +113,7 @@ export class ClassDefinition { defaultIncludes ) ) - .then((data) => this.store("includeCode", data)); + .then((data) => data); } public async getMemberLocation(name: string): Promise { diff --git a/webpack.config.js b/webpack.config.js index 18922c52..ffd3215f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,7 +4,7 @@ const path = require("path"); const config = { target: "node", // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ - entry: ["core-js/features/array/flat-map", "./src/extension.ts"], // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ + entry: "./src/extension.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ output: { // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ path: path.resolve(__dirname, "dist"),