diskFileSystemProvider.ts ×20

Frontier kind: Code frontier

unlabeled · c_56163f53fac6

7 tests · 26351 LOC · 101 files · introduces 0 tests · 260 LOC · 7 files

Introduces — evidence that enters the hierarchy at this concept

Code
56 ranges260 lines · 7 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
2912 ranges26351 lines · 101 files · Browse complete extent
All tests (intent)
7 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

7 files ranked by introduced lines: 260 introduced LOC across 56 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/agentHost/node/agentSdkDownloader.ts 146 introduced LOC · 19 ranges

Open complete file

219 * `undefined` when the header is absent, an array, or not a clean integer.
220 */
221 > function parseContentLength(header: string | string[] | undefined): number | undefined { agentSdkDownloader.ts
222 > if (typeof header !== 'string' || !/^\d+$/.test(header)) {
223 return undefined;
224 }
225 > const parsed = parseInt(header, 10); agentSdkDownloader.ts
226 > return parsed > 0 ? parsed : undefined;
227 > }
228
229 export class AgentSdkDownloader extends Disposable implements IAgentSdkDownloader {
360 return cacheDir;
361 }
363 > // Download (deduped across concurrent callers in the same process).
364 > // cacheDir is already unique per (pkg, version, sdkTarget) — within
365 > // a single downloader instance userDataPath is fixed, so it serves
366 > // as the dedup key without an extra string allocation.
367 > let pending = this._pendingDownloads.get(cacheDir);
368 > if (!pending) {
369 > pending = this._download(pkg, url, cacheDir, sentinel, token).finally(() => {
370 > this._pendingDownloads.delete(cacheDir);
371 > });
372 > this._pendingDownloads.set(cacheDir, pending);
373 > }
374 > return pending;
375 }
376
390
391 private async _download(
392 > pkg: IAgentSdkPackage, agentSdkDownloader.ts
393 > url: string,
394 > cacheDir: string,
395 > sentinel: URI,
396 > token: CancellationToken,
397 > ): Promise<string> {
398 > this._logService.info(`[AgentSdkDownloader] ${pkg.id}: downloading from ${url}`);
399 > const start = Date.now();
400 > const parent = path.dirname(cacheDir);
401 > await this._fileService.createFolder(URI.file(parent));
402 >
403 > // Extract to a per-pid scratch dir alongside the final cache dir, then
404 > // rename into place. If two windows of the same install race, the loser
405 > // catches the `move`'s `FILE_MOVE_CONFLICT`, checks the existing
406 > // .complete sentinel, and uses that instead — see the rename-loser
407 > // path below.
408 > const tmpDir = `${cacheDir}.tmp.${process.pid}`;
409 > const tmpDirUri = URI.file(tmpDir);
410 > await this._delIgnoringMissing(tmpDirUri);
411 > await this._fileService.createFolder(tmpDirUri);
412 >
413 > // Fire the download lifecycle on the process-global event so a single
414 > // subscriber (the protocol server) can forward it to clients. One
415 > // `started`, throttled `progress` from `_fetch`, then a terminal frame.
416 > const downloadId = generateUuid();
417 > let lastReceived = 0;
418 > let lastTotal: number | undefined;
419 > this._fireProgress(pkg, downloadId, 'started', 0, undefined);
420 >
421 > try {
422 > const tarballPath = path.join(tmpDir, 'sdk.tgz');
423 > await this._fetch(url, tarballPath, token, (receivedBytes, totalBytes) => {
424 > lastReceived = receivedBytes;
425 > lastTotal = totalBytes;
426 > this._fireProgress(pkg, downloadId, 'progress', receivedBytes, totalBytes);
427 > });
428 await this._extractTarGz(tarballPath, tmpDir);
429 await this._fileService.del(URI.file(tarballPath));
455 const elapsed = Math.round((Date.now() - start) / 1000);
456 this._logService.info(`[AgentSdkDownloader] ${pkg.id}: downloaded in ${elapsed}s`);
457 > this._fireProgress(pkg, downloadId, 'completed', lastTotal ?? lastReceived, lastTotal); agentSdkDownloader.ts
458 > return cacheDir;
459 > } catch (err) {
460 await this._delIgnoringMissing(tmpDirUri);
461 if (token.isCancellationRequested) {
472 );
473 }
475
476 private _fireProgress(
477 > pkg: IAgentSdkPackage, agentSdkDownloader.ts
478 > downloadId: string,
479 > phase: AgentSdkDownloadPhase,
480 > receivedBytes: number,
481 > totalBytes: number | undefined,
482 > error?: string,
483 > ): void {
484 > this._onDidDownloadProgress.fire({
485 > downloadId,
486 > packageId: pkg.id,
487 > displayName: pkg.displayName,
488 > phase,
489 > receivedBytes,
490 > totalBytes,
491 > ...(error !== undefined ? { error } : {}),
492 > });
493 > }
494
495 private async _handleRenameLoser(
513
514 private async _fetch(
515 > url: string, agentSdkDownloader.ts
516 > dest: string,
517 > token: CancellationToken,
518 > onBytes?: (receivedBytes: number, totalBytes: number | undefined) => void,
519 > ): Promise<void> {
520 > // Delegate to IRequestService (corporate proxy, strictSSL, kerberos,
521 > // retries, redirect follow). `fs.createWriteStream` (not
522 > // `IFileService.writeFile`) so that cancelling a multi-MB download
523 > // aborts promptly via destroy(). Manual pipe (not `stream.pipeline`)
524 > // because the source is a VSBufferReadableStream — not a Node
525 > // Readable — so node-stream utilities can't introspect it.
526 > if (token.isCancellationRequested) {
527 throw new CancellationError();
528 }
529 > const context: IRequestContext = await this._requestService.request({ agentSdkDownloader.ts
530 > url,
531 > type: 'GET',
532 > callSite: 'agentSdkDownloader',
533 > }, token);
534 > if (token.isCancellationRequested) {
535 context.stream.destroy();
536 throw new CancellationError();
537 }
539 > const statusCode = context.res.statusCode ?? 0;
540 > if (statusCode < 200 || statusCode >= 300) {
541 context.stream.destroy();
542 throw new Error(`HTTP ${statusCode} fetching ${url}`);
543 }
545 > // The CDN sends `Content-Length` for these static tarballs, which lets
546 > // us report determinate percentage progress. A missing/garbled header
547 > // degrades gracefully to an indeterminate (byte-count only) report.
548 > const totalBytes = parseContentLength(context.res.headers['content-length']);
549 >
550 > await new Promise<void>((resolve, reject) => {
551 > const out = fs.createWriteStream(dest);
552 > let settled = false;
553 > // Throttle progress so a fast link doesn't fire thousands of
554 > // samples. The first chunk always passes (lastEmit starts at 0)
555 > // and 'end' forces a final sample, so consumers see a start and a
556 > // 100% finish regardless of chunk timing.
557 > let receivedBytes = 0;
558 > let lastEmitTime = 0;
559 > const emitBytes = (force: boolean) => {
560 > if (!onBytes) {
561 return;
562 }
563 > const now = Date.now(); agentSdkDownloader.ts
564 > if (!force && now - lastEmitTime < PROGRESS_EMIT_THROTTLE_MS) {
565 return;
566 }
567 > lastEmitTime = now; agentSdkDownloader.ts
568 > onBytes(receivedBytes, totalBytes);
569 > };
570 > const settleResolve = () => {
571 if (settled) { return; }
572 settled = true;
574 resolve();
575 };
576 > const settleReject = (err: unknown) => { agentSdkDownloader.ts
577 if (settled) { return; }
578 settled = true;
582 reject(err);
583 };
584 > const cancelSub = token.onCancellationRequested(() => settleReject(new CancellationError())); agentSdkDownloader.ts
585 > out.on('error', settleReject);
586 > out.on('finish', settleResolve);
587 > // Backpressure: tarballs are 70-95MB; if the disk is slower
588 > // than the network (Windows AV scan, network home dir, …) an
589 > // unthrottled pipe buffers the whole thing in memory. Pause the
590 > // source when the sink's internal buffer hits highWaterMark and
591 > // resume on 'drain'.
592 > out.on('drain', () => context.stream.resume());
593 > context.stream.on('data', chunk => {
594 > receivedBytes += chunk.byteLength;
595 > emitBytes(false);
596 > if (!out.write(chunk.buffer)) {
597 context.stream.pause();
598 }
600 > context.stream.on('end', () => {
601 emitBytes(true);
602 out.end();
604 > context.stream.on('error', settleReject);
605 > });
606 > }
607
608 private async _extractTarGz(tarball: string, dest: string): Promise<void> {
613
614 private async _delIgnoringMissing(uri: URI): Promise<void> {
615 > try { agentSdkDownloader.ts
616 > await this._fileService.del(uri, { recursive: true });
617 > } catch (err) {
618 > // `force: true` behaviour: missing path is a no-op.
619 > if (toFileOperationResult(err as Error) !== FileOperationResult.FILE_NOT_FOUND) {
620 throw err;
621 }
623 > }
624 }
625
src/vs/platform/files/node/diskFileSystemProvider.ts 45 introduced LOC · 20 ranges

Open complete file

84 stat.mode & constants.S_IXOTH
85 ) {
86 > permissions = (permissions ?? 0) | FilePermission.Executable; diskFileSystemProvider.ts
87 > }
88
89 return {
114
115 async readdir(resource: URI): Promise<[string, FileType][]> {
117 > const children = await Promises.readdir(this.toFilePath(resource), { withFileTypes: true });
118 >
119 > const result: [string, FileType][] = [];
120 > await Promise.all(children.map(async child => {
121 try {
122 let type: FileType;
131 this.logService.trace(error); // ignore errors for individual entries that can arise from permission denied
132 }
134 >
135 > return result;
136 > } catch (error) {
137 throw this.toFileSystemProviderError(error);
138 }
140
141 private toType(entry: Stats | IDirent, symbolicLink?: { dangling: boolean }): FileType {
150 type = FileType.File;
151 } else if (entry.isDirectory()) {
152 > type = FileType.Directory; diskFileSystemProvider.ts
153 > } else {
154 type = FileType.Unknown;
155 }
617
618 async mkdir(resource: URI): Promise<void> {
620 > await promises.mkdir(this.toFilePath(resource));
621 > } catch (error) {
622 throw this.toFileSystemProviderError(error);
623 }
625
626 async delete(resource: URI, opts: IFileDeleteOptions): Promise<void> {
628 > const filePath = this.toFilePath(resource);
629 > if (opts.recursive) {
630 let rmMoveToPath: string | undefined = undefined;
631 if (opts?.atomic !== false && opts.atomic.postfix) {
634
635 await Promises.rm(filePath, RimRafMode.MOVE, rmMoveToPath);
636 > } else { diskFileSystemProvider.ts
637 try {
638 await promises.unlink(filePath);
841
842 private toFileSystemProviderError(error: NodeJS.ErrnoException): FileSystemProviderError {
843 > if (error instanceof FileSystemProviderError) { diskFileSystemProvider.ts
844 return error; // avoid double conversion
845 }
847 > let resultError: Error | string = error;
848 > let code: FileSystemProviderErrorCode;
849 > switch (error.code) {
850 > case 'ENOENT':
851 > code = FileSystemProviderErrorCode.FileNotFound;
852 > break;
853 > case 'EISDIR':
854 code = FileSystemProviderErrorCode.FileIsADirectory;
855 break;
856 > case 'ENOTDIR': diskFileSystemProvider.ts
857 code = FileSystemProviderErrorCode.FileNotADirectory;
858 break;
859 > case 'EEXIST': diskFileSystemProvider.ts
860 code = FileSystemProviderErrorCode.FileExists;
861 break;
862 > case 'EPERM': diskFileSystemProvider.ts
863 > case 'EACCES':
864 code = FileSystemProviderErrorCode.NoPermissions;
865 break;
866 > case 'ERR_UNC_HOST_NOT_ALLOWED': diskFileSystemProvider.ts
867 resultError = `${error.message}. Please update the 'security.allowedUNCHosts' setting if you want to allow this host.`;
868 code = FileSystemProviderErrorCode.Unknown;
869 break;
870 > default: diskFileSystemProvider.ts
871 code = FileSystemProviderErrorCode.Unknown;
873 >
874 > return createFileSystemProviderError(resultError, code);
875 > }
876
877 private async toFileSystemProviderWriteError(resource: URI | undefined, error: NodeJS.ErrnoException): Promise<FileSystemProviderError> {
src/vs/platform/request/node/requestService.ts 30 introduced LOC · 5 ranges

Open complete file

88
89 async request(options: NodeRequestOptions, token: CancellationToken): Promise<IRequestContext> {
90 > const { proxyUrl, strictSSL } = this; requestService.ts
91 >
92 > let shellEnv: typeof process.env | undefined = undefined;
93 > try {
94 > shellEnv = await getResolvedShellEnv(this.configurationService, this.logService, this.environmentService.args, process.env);
95 > } catch (error) {
96 if (!this.shellEnvErrorLogged) {
97 this.shellEnvErrorLogged = true;
99 }
100 }
102 > const env = {
103 > ...process.env,
104 > ...shellEnv
105 > };
106 > const agent = options.agent ? options.agent : await getProxyAgent(options.url || '', env, { proxyUrl, strictSSL });
107 >
108 > options.agent = agent;
109 > options.strictSSL = strictSSL;
110 >
111 > if (this.authorization) {
112 options.headers = {
113 ...(options.headers || {}),
115 };
116 }
118 > return this.logAndRequest(options, () => nodeRequest(options, token));
119 > }
120
121 async resolveProxy(url: string): Promise<string | undefined> {
166 }
167
168 > async function getNodeRequest(options: IRequestOptions): Promise<IRawRequestFunction> { requestService.ts
169 > const endpoint = parseUrl(options.url!);
170 > const module = endpoint.protocol === 'https:' ? await import('https') : await import('http');
171 >
172 > return module.request;
173 > }
174
175 export async function nodeRequest(options: NodeRequestOptions, token: CancellationToken): Promise<IRequestContext> {
203 const rawRequest = options.getRawRequest
204 ? options.getRawRequest(options)
205 > : await getNodeRequest(options); requestService.ts
206 >
207 > const opts: https.RequestOptions & { cache?: 'default' | 'no-store' | 'reload' | 'no-cache' | 'force-cache' | 'only-if-cached' } = {
208 > hostname: endpoint.hostname,
209 port: endpoint.port ? parseInt(endpoint.port) : (endpoint.protocol === 'https:' ? 443 : 80),
210 protocol: endpoint.protocol,
src/vs/platform/request/node/proxy.ts 19 introduced LOC · 5 ranges

Open complete file

9 export type Agent = any;
10
11 > function getSystemProxyURI(requestURL: Url, env: typeof process.env): string | null { proxy.ts
12 > if (requestURL.protocol === 'http:') {
13 > return env.HTTP_PROXY || env.http_proxy || null;
14 > } else if (requestURL.protocol === 'https:') {
15 return env.HTTPS_PROXY || env.https_proxy || env.HTTP_PROXY || env.http_proxy || null;
16 }
24 }
25
26 > export async function getProxyAgent(rawRequestURL: string, env: typeof process.env, options: IOptions = {}): Promise<Agent> { proxy.ts
27 > const requestURL = parseUrl(rawRequestURL);
28 > const proxyURL = options.proxyUrl || getSystemProxyURI(requestURL, env);
29 >
30 > if (!proxyURL) {
31 > return null;
32 > }
33
34 const proxyEndpoint = parseUrl(proxyURL);
35
36 > if (!/^https?:$/.test(proxyEndpoint.protocol || '')) { proxy.ts
37 return null;
38 }
40 const opts = {
41 host: proxyEndpoint.hostname || '',
42 > port: (proxyEndpoint.port ? +proxyEndpoint.port : 0) || (proxyEndpoint.protocol === 'https' ? 443 : 80), proxy.ts
43 > auth: proxyEndpoint.auth,
44 > rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true,
45 > };
46 >
47 > if (requestURL.protocol === 'http:') {
48 const { default: mod } = await import('http-proxy-agent');
49 return new mod.HttpProxyAgent(proxyURL, opts);
52 return new mod.HttpsProxyAgent(proxyURL, opts);
53 }
54 > } proxy.ts
src/vs/platform/shell/node/shellEnv.ts 9 introduced LOC · 2 ranges

Open complete file

30 * - any other error from spawning a shell to figure out the environment
31 */
32 > export async function getResolvedShellEnv(configurationService: IConfigurationService, logService: ILogService, args: NativeParsedArgs, env: IProcessEnvironment): Promise<typeof process.env> { shellEnv.ts
33 >
34 > // Skip if --force-disable-user-env
35 > if (args['force-disable-user-env']) {
36 > logService.trace('resolveShellEnv(): skipped (--force-disable-user-env)');
37 >
38 > return {};
39 > }
40
41 // Skip on windows
98 return unixShellEnvPromise;
99 }
100 > } shellEnv.ts
101
102 async function doResolveUnixShellEnv(logService: ILogService, token: CancellationToken): Promise<typeof process.env> {
src/vs/base/node/pfs.ts 8 introduced LOC · 3 ranges

Open complete file

274 }
275 } catch {
276 > /* ignore - use stat() instead */ pfs.ts
277 > }
278
279 // If the stat is a symbolic link or failed to stat, use fs.stat()
290 return { stat: lstats, symbolicLink: { dangling: true } };
291 }
292 > pfs.ts
293 > // Windows: workaround a node.js bug where reparse points
294 > // are not supported (https://github.com/nodejs/node/issues/36790)
295 if (isWindows && error.code === 'EACCES') {
296 try {
309 }
310 }
311 > pfs.ts
312 > throw error;
313 > }
314 }
315
src/vs/platform/files/common/fileService.ts 3 introduced LOC · 2 ranges

Open complete file

1091 let deleteFileOptions = options;
1092 if (hasFileAtomicDeleteCapability(provider) && !deleteFileOptions?.atomic) {
1093 > const enforcedAtomicDelete = provider.enforceAtomicDelete?.(resource); fileService.ts
1094 > if (enforcedAtomicDelete) {
1095 deleteFileOptions = { ...options, atomic: enforcedAtomicDelete };
1096 }
1097 > } fileService.ts
1098
1099 const useTrash = !!deleteFileOptions?.useTrash;