@continuedev/fetch 1.0.13 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Extracts content from either a file path or data URI
3
+ */
4
+ export declare function getCertificateContent(input: string): string;
5
+ export declare class CertsCache {
6
+ private static instance;
7
+ private _fixedCa;
8
+ private _initialized;
9
+ private _customCerts;
10
+ private constructor();
11
+ static getInstance(): CertsCache;
12
+ get fixedCa(): string[];
13
+ getCachedCustomCert(path: string): Promise<string | undefined>;
14
+ getAllCachedCustomCerts(caBundlePath: string[] | string): Promise<string[]>;
15
+ getCa(caBundlePath: undefined | string | string[]): Promise<string[]>;
16
+ clear(): Promise<void>;
17
+ }
package/dist/certs.js ADDED
@@ -0,0 +1,105 @@
1
+ import { globalAgent } from "https";
2
+ import * as fs from "node:fs";
3
+ import tls from "node:tls";
4
+ /**
5
+ * Extracts content from either a file path or data URI
6
+ */
7
+ export function getCertificateContent(input) {
8
+ if (input.startsWith("data:")) {
9
+ // Parse data URI: data:[<mediatype>][;base64],<data>
10
+ const [header, data] = input.split(",");
11
+ if (header.includes("base64")) {
12
+ return Buffer.from(data, "base64").toString("utf8");
13
+ }
14
+ else {
15
+ return decodeURIComponent(data);
16
+ }
17
+ }
18
+ else {
19
+ // Assume it's a file path
20
+ return fs.readFileSync(input, "utf8");
21
+ }
22
+ }
23
+ export class CertsCache {
24
+ static instance;
25
+ _fixedCa = [];
26
+ _initialized = false;
27
+ _customCerts = new Map();
28
+ constructor() { }
29
+ static getInstance() {
30
+ if (!CertsCache.instance) {
31
+ CertsCache.instance = new CertsCache();
32
+ }
33
+ return CertsCache.instance;
34
+ }
35
+ get fixedCa() {
36
+ if (this._initialized) {
37
+ return this._fixedCa;
38
+ }
39
+ const globalCerts = [];
40
+ if (Boolean(process.env.IS_BINARY)) {
41
+ if (Array.isArray(globalAgent.options.ca)) {
42
+ globalCerts.push(...globalAgent.options.ca.map((cert) => cert.toString()));
43
+ }
44
+ else if (typeof globalAgent.options.ca !== "undefined") {
45
+ globalCerts.push(globalAgent.options.ca.toString());
46
+ }
47
+ }
48
+ const extraCerts = [];
49
+ if (process.env.NODE_EXTRA_CA_CERTS) {
50
+ try {
51
+ const content = fs.readFileSync(process.env.NODE_EXTRA_CA_CERTS, "utf8");
52
+ extraCerts.push(content);
53
+ }
54
+ catch (error) {
55
+ if (process.env.VERBOSE_FETCH) {
56
+ console.error(`Error reading NODE_EXTRA_CA_CERTS file: ${process.env.NODE_EXTRA_CA_CERTS}`, error);
57
+ }
58
+ }
59
+ }
60
+ this._fixedCa = Array.from(new Set([...tls.rootCertificates, ...globalCerts, ...extraCerts]));
61
+ this._initialized = true;
62
+ return this._fixedCa;
63
+ }
64
+ async getCachedCustomCert(path) {
65
+ if (this._customCerts.has(path)) {
66
+ return this._customCerts.get(path);
67
+ }
68
+ const certContent = getCertificateContent(path);
69
+ this._customCerts.set(path, certContent);
70
+ return certContent;
71
+ }
72
+ async getAllCachedCustomCerts(caBundlePath) {
73
+ const paths = Array.isArray(caBundlePath) ? caBundlePath : [caBundlePath];
74
+ const certs = [];
75
+ await Promise.all(paths.map(async (path) => {
76
+ try {
77
+ const certContent = await this.getCachedCustomCert(path);
78
+ if (certContent) {
79
+ certs.push(certContent);
80
+ }
81
+ else if (process.env.VERBOSE_FETCH) {
82
+ console.warn(`Empty certificate found at ${path}`);
83
+ }
84
+ }
85
+ catch (error) {
86
+ if (process.env.VERBOSE_FETCH) {
87
+ console.error(`Error loading custom certificate from ${path}:`, error);
88
+ }
89
+ }
90
+ }));
91
+ return certs;
92
+ }
93
+ async getCa(caBundlePath) {
94
+ if (!caBundlePath) {
95
+ return this.fixedCa;
96
+ }
97
+ const customCerts = await this.getAllCachedCustomCerts(caBundlePath);
98
+ return [...this.fixedCa, ...customCerts];
99
+ }
100
+ async clear() {
101
+ this._customCerts.clear();
102
+ this._initialized = false;
103
+ this._fixedCa = [];
104
+ }
105
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,139 @@
1
+ import * as fs from "node:fs";
2
+ import { beforeEach, expect, test, vi } from "vitest";
3
+ import { CertsCache, getCertificateContent } from "./certs.js";
4
+ // Mock fs module
5
+ vi.mock("node:fs", () => ({
6
+ readFileSync: vi.fn(),
7
+ }));
8
+ const mockReadFileSync = vi.mocked(fs.readFileSync);
9
+ beforeEach(() => {
10
+ vi.clearAllMocks();
11
+ });
12
+ test("getCertificateContent should decode base64 data URI correctly", () => {
13
+ const testCert = "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----";
14
+ const base64Data = Buffer.from(testCert, "utf8").toString("base64");
15
+ const dataUri = `data:application/x-pem-file;base64,${base64Data}`;
16
+ const result = getCertificateContent(dataUri);
17
+ expect(result).toBe(testCert);
18
+ });
19
+ test("getCertificateContent should decode URL-encoded data URI correctly", () => {
20
+ const testCert = "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----";
21
+ const encodedData = encodeURIComponent(testCert);
22
+ const dataUri = `data:text/plain,${encodedData}`;
23
+ const result = getCertificateContent(dataUri);
24
+ expect(result).toBe(testCert);
25
+ });
26
+ test("getCertificateContent should handle plain text data URI correctly", () => {
27
+ const testCert = "simple-cert-content";
28
+ const dataUri = `data:text/plain,${testCert}`;
29
+ const result = getCertificateContent(dataUri);
30
+ expect(result).toBe(testCert);
31
+ });
32
+ test("getCertificateContent should read file when input is a file path", () => {
33
+ const filePath = "/path/to/cert.pem";
34
+ const expectedContent = "-----BEGIN CERTIFICATE-----\nfile content\n-----END CERTIFICATE-----";
35
+ mockReadFileSync.mockReturnValue(expectedContent);
36
+ const result = getCertificateContent(filePath);
37
+ expect(mockReadFileSync).toHaveBeenCalledWith(filePath, "utf8");
38
+ expect(result).toBe(expectedContent);
39
+ });
40
+ test("getCertificateContent should handle data URI with different media types", () => {
41
+ const testData = "certificate-data";
42
+ const base64Data = Buffer.from(testData, "utf8").toString("base64");
43
+ const dataUri = `data:application/x-x509-ca-cert;base64,${base64Data}`;
44
+ const result = getCertificateContent(dataUri);
45
+ expect(result).toBe(testData);
46
+ });
47
+ test("getCertificateContent should handle data URI without media type", () => {
48
+ const testData = "simple-data";
49
+ const base64Data = Buffer.from(testData, "utf8").toString("base64");
50
+ const dataUri = `data:;base64,${base64Data}`;
51
+ const result = getCertificateContent(dataUri);
52
+ expect(result).toBe(testData);
53
+ });
54
+ test("getCertificateContent should handle data URI without media type or encoding", () => {
55
+ const testData = "simple-data";
56
+ const base64Data = Buffer.from(testData, "utf8").toString("base64");
57
+ const dataUri = `data:;base64,${base64Data}`;
58
+ const result = getCertificateContent(dataUri);
59
+ expect(result).toBe(testData);
60
+ });
61
+ test("getCertificateContent should handle relative file paths", () => {
62
+ const filePath = "./certs/ca.pem";
63
+ const expectedContent = "certificate from relative path";
64
+ mockReadFileSync.mockReturnValue(expectedContent);
65
+ const result = getCertificateContent(filePath);
66
+ expect(mockReadFileSync).toHaveBeenCalledWith(filePath, "utf8");
67
+ expect(result).toBe(expectedContent);
68
+ });
69
+ test("getCertificateContent should handle data URI with special characters in URL encoding", () => {
70
+ const testCert = "cert with spaces and special chars: !@#$%";
71
+ const encodedData = encodeURIComponent(testCert);
72
+ const dataUri = `data:text/plain,${encodedData}`;
73
+ const result = getCertificateContent(dataUri);
74
+ expect(result).toBe(testCert);
75
+ });
76
+ test("CertsCache.getCachedCustomCert should cache and return certificate content", async () => {
77
+ const certsCache = CertsCache.getInstance();
78
+ const filePath = "/path/to/custom/cert.pem";
79
+ const expectedContent = "custom cert content";
80
+ mockReadFileSync.mockReturnValue(expectedContent);
81
+ const cert1 = await certsCache.getCachedCustomCert(filePath);
82
+ expect(cert1).toBe(expectedContent);
83
+ expect(mockReadFileSync).toHaveBeenCalledTimes(1);
84
+ expect(mockReadFileSync).toHaveBeenCalledWith(filePath, "utf8");
85
+ // Call again to check if it's cached
86
+ const cert2 = await certsCache.getCachedCustomCert(filePath);
87
+ expect(cert2).toBe(expectedContent);
88
+ // readFileSync should not be called again
89
+ expect(mockReadFileSync).toHaveBeenCalledTimes(1);
90
+ });
91
+ test("CertsCache.getAllCachedCustomCerts should return all cached custom certs", async () => {
92
+ const certsCache = CertsCache.getInstance();
93
+ const filePaths = ["/path/to/cert1.pem", "/path/to/cert2.pem"];
94
+ const expectedContent1 = "content of cert1";
95
+ const expectedContent2 = "content of cert2";
96
+ mockReadFileSync.mockReturnValueOnce(expectedContent1);
97
+ mockReadFileSync.mockReturnValueOnce(expectedContent2);
98
+ const certs = await certsCache.getAllCachedCustomCerts(filePaths);
99
+ expect(certs).toEqual([expectedContent1, expectedContent2]);
100
+ expect(mockReadFileSync).toHaveBeenCalledTimes(2);
101
+ });
102
+ test("CertsCache.getCa should return combined CA when caBundlePath is provided", async () => {
103
+ const certsCache = CertsCache.getInstance();
104
+ const fixedCa = ["fixed CA cert"];
105
+ const customCertPath = "/path/to/custom/cert.pem";
106
+ const customCertContent = "custom cert content";
107
+ // Directly set _fixedCa to avoid initializing it with real data
108
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
109
+ // @ts-ignore
110
+ certsCache._fixedCa = fixedCa;
111
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
112
+ // @ts-ignore
113
+ certsCache._initialized = true;
114
+ mockReadFileSync.mockReturnValue(customCertContent);
115
+ const ca = await certsCache.getCa(customCertPath);
116
+ expect(ca).toEqual([...fixedCa, customCertContent]);
117
+ });
118
+ test("CertsCache.clear should clear custom certs and reset initialized flag", () => {
119
+ const certsCache = CertsCache.getInstance();
120
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
121
+ // @ts-ignore
122
+ certsCache._customCerts.set("key", "value");
123
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
124
+ // @ts-ignore
125
+ certsCache._initialized = true;
126
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
127
+ // @ts-ignore
128
+ certsCache._fixedCa = ["test"];
129
+ certsCache.clear();
130
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
131
+ // @ts-ignore
132
+ expect(certsCache._customCerts.size).toBe(0);
133
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
134
+ // @ts-ignore
135
+ expect(certsCache._initialized).toBe(false);
136
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
137
+ // @ts-ignore
138
+ expect(certsCache._fixedCa).toEqual([]);
139
+ });
package/dist/fetch.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import * as followRedirects from "follow-redirects";
2
2
  import { HttpProxyAgent } from "http-proxy-agent";
3
3
  import { HttpsProxyAgent } from "https-proxy-agent";
4
- import fetch, { Response } from "node-fetch";
4
+ import { Response } from "node-fetch";
5
5
  import { getAgentOptions } from "./getAgentOptions.js";
6
- import { getProxyFromEnv, shouldBypassProxy } from "./util.js";
6
+ import fetch from "./node-fetch-patch.js";
7
+ import { getProxy, shouldBypassProxy } from "./util.js";
7
8
  const { http, https } = followRedirects.default;
8
9
  function logRequest(method, url, headers, body, proxy, shouldBypass) {
9
10
  console.log("=== FETCH REQUEST ===");
@@ -65,15 +66,11 @@ export async function fetchwithRequestOptions(url_, init, requestOptions) {
65
66
  if (url.host === "localhost") {
66
67
  url.host = "127.0.0.1";
67
68
  }
68
- const agentOptions = getAgentOptions(requestOptions);
69
+ const agentOptions = await getAgentOptions(requestOptions);
69
70
  // Get proxy from options or environment variables
70
- let proxy = requestOptions?.proxy;
71
- if (!proxy) {
72
- proxy = getProxyFromEnv(url.protocol);
73
- }
71
+ const proxy = getProxy(url.protocol, requestOptions);
74
72
  // Check if should bypass proxy based on requestOptions or NO_PROXY env var
75
- const shouldBypass = requestOptions?.noProxy?.includes(url.hostname) ||
76
- shouldBypassProxy(url.hostname);
73
+ const shouldBypass = shouldBypassProxy(url.hostname, requestOptions);
77
74
  // Create agent
78
75
  const protocol = url.protocol === "https:" ? https : http;
79
76
  const agent = proxy && !shouldBypass
@@ -2,7 +2,6 @@ import { globalAgent } from "https";
2
2
  import * as fs from "node:fs";
3
3
  import * as os from "node:os";
4
4
  import * as path from "node:path";
5
- import { afterEach, beforeEach, expect, test } from "vitest";
6
5
  import { getAgentOptions } from "./getAgentOptions.js";
7
6
  // Store original env
8
7
  const originalEnv = process.env;
@@ -2,6 +2,6 @@ import { RequestOptions } from "@continuedev/config-types";
2
2
  /**
3
3
  * Prepares agent options based on request options and certificates
4
4
  */
5
- export declare function getAgentOptions(requestOptions?: RequestOptions): {
5
+ export declare function getAgentOptions(requestOptions?: RequestOptions): Promise<{
6
6
  [key: string]: any;
7
- };
7
+ }>;
@@ -1,48 +1,12 @@
1
- import { globalAgent } from "https";
2
- import * as fs from "node:fs";
3
- import tls from "node:tls";
4
- /**
5
- * Extracts content from either a file path or data URI
6
- */
7
- function getCertificateContent(input) {
8
- if (input.startsWith("data:")) {
9
- // Parse data URI: data:[<mediatype>][;base64],<data>
10
- const [header, data] = input.split(",");
11
- if (header.includes("base64")) {
12
- return Buffer.from(data, "base64").toString("utf8");
13
- }
14
- else {
15
- return decodeURIComponent(data);
16
- }
17
- }
18
- else {
19
- // Assume it's a file path
20
- return fs.readFileSync(input, "utf8");
21
- }
22
- }
1
+ import { CertsCache, getCertificateContent } from "./certs.js";
23
2
  /**
24
3
  * Prepares agent options based on request options and certificates
25
4
  */
26
- export function getAgentOptions(requestOptions) {
5
+ export async function getAgentOptions(requestOptions) {
27
6
  const TIMEOUT = 7200; // 7200 seconds = 2 hours
28
7
  const timeout = (requestOptions?.timeout ?? TIMEOUT) * 1000; // measured in ms
29
- // Get root certificates
30
- let globalCerts = [];
31
- if (process.env.IS_BINARY) {
32
- if (Array.isArray(globalAgent.options.ca)) {
33
- globalCerts = [...globalAgent.options.ca.map((cert) => cert.toString())];
34
- }
35
- else if (typeof globalAgent.options.ca !== "undefined") {
36
- globalCerts.push(globalAgent.options.ca.toString());
37
- }
38
- }
39
- const ca = Array.from(new Set([...tls.rootCertificates, ...globalCerts]));
40
- const customCerts = typeof requestOptions?.caBundlePath === "string"
41
- ? [requestOptions?.caBundlePath]
42
- : requestOptions?.caBundlePath;
43
- if (customCerts) {
44
- ca.push(...customCerts.map((customCert) => getCertificateContent(customCert)));
45
- }
8
+ const certsCache = CertsCache.getInstance();
9
+ const ca = await certsCache.getCa(requestOptions?.caBundlePath);
46
10
  const agentOptions = {
47
11
  ca,
48
12
  rejectUnauthorized: requestOptions?.verifySsl,
@@ -60,5 +24,14 @@ export function getAgentOptions(requestOptions) {
60
24
  agentOptions.passphrase = passphrase;
61
25
  }
62
26
  }
27
+ if (process.env.VERBOSE_FETCH) {
28
+ console.log(`Fetch agent options:`);
29
+ console.log(`\tTimeout (sessionTimeout/keepAliveMsecs): ${agentOptions.timeout}`);
30
+ console.log(`\tTotal CA certs: ${ca.length}`);
31
+ console.log(`\tGlobal/Root CA certs: ${certsCache.fixedCa.length}`);
32
+ console.log(`\tCustom CA certs: ${ca.length - certsCache.fixedCa.length}`);
33
+ console.log(`\tClient certificate: ${requestOptions?.clientCertificate ? "Yes" : "No"}`);
34
+ console.log(`\trejectUnauthorized/verifySsl: ${agentOptions.rejectUnauthorized ?? "not set (defaults to true)"}`);
35
+ }
63
36
  return agentOptions;
64
37
  }
@@ -1,89 +1,141 @@
1
+ import { globalAgent } from "https";
1
2
  import * as fs from "node:fs";
2
- import { expect, test, vi } from "vitest";
3
- // Mock fs module
4
- vi.mock("node:fs", () => ({
5
- readFileSync: vi.fn(),
6
- }));
7
- const mockReadFileSync = vi.mocked(fs.readFileSync);
8
- // We need to access the private getCertificateContent function for testing
9
- // Since it's not exported, we'll test it indirectly through scenarios or mock the module
10
- const getCertificateContent = (input) => {
11
- if (input.startsWith("data:")) {
12
- // Parse data URI: data:[<mediatype>][;base64],<data>
13
- const [header, data] = input.split(",");
14
- if (header.includes("base64")) {
15
- return Buffer.from(data, "base64").toString("utf8");
16
- }
17
- else {
18
- return decodeURIComponent(data);
19
- }
3
+ import * as os from "node:os";
4
+ import * as path from "node:path";
5
+ import { afterEach, beforeEach, expect, test } from "vitest";
6
+ import { CertsCache } from "./certs.js";
7
+ import { getAgentOptions } from "./getAgentOptions.js";
8
+ // Store original env
9
+ const originalEnv = process.env;
10
+ const originalGlobalAgentOptions = { ...globalAgent.options };
11
+ // Temporary directory for test certificate files
12
+ let tempDir;
13
+ beforeEach(() => {
14
+ // Create a temporary directory for test files
15
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "fetch-test-"));
16
+ process.env = { ...originalEnv };
17
+ // Reset globalAgent for each test
18
+ globalAgent.options = { ...originalGlobalAgentOptions };
19
+ });
20
+ afterEach(() => {
21
+ process.env = originalEnv;
22
+ globalAgent.options = originalGlobalAgentOptions;
23
+ CertsCache.getInstance().clear();
24
+ // Clean up temporary directory
25
+ try {
26
+ fs.rmSync(tempDir, { recursive: true, force: true });
20
27
  }
21
- else {
22
- // Assume it's a file path
23
- return fs.readFileSync(input, "utf8");
28
+ catch (error) {
29
+ console.error(`Failed to remove temp directory: ${error}`);
24
30
  }
25
- };
26
- test("getCertificateContent should decode base64 data URI correctly", () => {
27
- const testCert = "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----";
28
- const base64Data = Buffer.from(testCert, "utf8").toString("base64");
29
- const dataUri = `data:application/x-pem-file;base64,${base64Data}`;
30
- const result = getCertificateContent(dataUri);
31
- expect(result).toBe(testCert);
32
31
  });
33
- test("getCertificateContent should decode URL-encoded data URI correctly", () => {
34
- const testCert = "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----";
35
- const encodedData = encodeURIComponent(testCert);
36
- const dataUri = `data:text/plain,${encodedData}`;
37
- const result = getCertificateContent(dataUri);
38
- expect(result).toBe(testCert);
32
+ // Helper function to create test certificate files
33
+ function createTestCertFile(filename, content) {
34
+ const filePath = path.join(tempDir, filename);
35
+ fs.writeFileSync(filePath, content, "utf8");
36
+ return filePath;
37
+ }
38
+ test("getAgentOptions returns basic configuration with default values", async () => {
39
+ const options = await getAgentOptions();
40
+ // Check default timeout (7200 seconds = 2 hours = 7,200,000 ms)
41
+ expect(options.timeout).toBe(7200000);
42
+ expect(options.sessionTimeout).toBe(7200000);
43
+ expect(options.keepAliveMsecs).toBe(7200000);
44
+ expect(options.keepAlive).toBe(true);
45
+ // Verify certificates array exists and contains items
46
+ expect(options.ca).toBeInstanceOf(Array);
47
+ expect(options.ca.length).toBeGreaterThan(0);
48
+ // Verify at least one of the real TLS root certificates is included
49
+ // This assumes there's at least one certificate with "CERTIFICATE" in it
50
+ expect(options.ca.some((cert) => cert.includes("CERTIFICATE"))).toBe(true);
39
51
  });
40
- test("getCertificateContent should handle plain text data URI correctly", () => {
41
- const testCert = "simple-cert-content";
42
- const dataUri = `data:text/plain,${testCert}`;
43
- const result = getCertificateContent(dataUri);
44
- expect(result).toBe(testCert);
52
+ test("getAgentOptions respects custom timeout", async () => {
53
+ const customTimeout = 300; // 5 minutes
54
+ const options = await getAgentOptions({ timeout: customTimeout });
55
+ // Check timeout values (300 seconds = 300,000 ms)
56
+ expect(options.timeout).toBe(300000);
57
+ expect(options.sessionTimeout).toBe(300000);
58
+ expect(options.keepAliveMsecs).toBe(300000);
45
59
  });
46
- test("getCertificateContent should read file when input is a file path", () => {
47
- const filePath = "/path/to/cert.pem";
48
- const expectedContent = "-----BEGIN CERTIFICATE-----\nfile content\n-----END CERTIFICATE-----";
49
- mockReadFileSync.mockReturnValue(expectedContent);
50
- const result = getCertificateContent(filePath);
51
- expect(mockReadFileSync).toHaveBeenCalledWith(filePath, "utf8");
52
- expect(result).toBe(expectedContent);
60
+ test("getAgentOptions uses verifySsl setting", async () => {
61
+ // With verifySsl true
62
+ let options = await getAgentOptions({ verifySsl: true });
63
+ expect(options.rejectUnauthorized).toBe(true);
64
+ // With verifySsl false
65
+ options = await getAgentOptions({ verifySsl: false });
66
+ expect(options.rejectUnauthorized).toBe(false);
53
67
  });
54
- test("getCertificateContent should handle data URI with different media types", () => {
55
- const testData = "certificate-data";
56
- const base64Data = Buffer.from(testData, "utf8").toString("base64");
57
- const dataUri = `data:application/x-x509-ca-cert;base64,${base64Data}`;
58
- const result = getCertificateContent(dataUri);
59
- expect(result).toBe(testData);
68
+ test("getAgentOptions incorporates custom CA bundle paths", async () => {
69
+ // Create a test CA bundle file
70
+ const caBundleContent = "-----BEGIN CERTIFICATE-----\nMIIDtTCCAp2gAwIBAgIJAMcuSp7chAYdMA==\n-----END CERTIFICATE-----";
71
+ const caBundlePath = createTestCertFile("ca-bundle.pem", caBundleContent);
72
+ // Single string path
73
+ let options = await getAgentOptions({ caBundlePath });
74
+ // Verify that our test certificate is included in the CA list
75
+ expect(options.ca).toContain(caBundleContent);
76
+ // Create multiple test CA bundle files
77
+ const caContent1 = "-----BEGIN CERTIFICATE-----\nABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\n-----END CERTIFICATE-----";
78
+ const caContent2 = "-----BEGIN CERTIFICATE-----\n0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\n-----END CERTIFICATE-----";
79
+ const caPath1 = createTestCertFile("ca1.pem", caContent1);
80
+ const caPath2 = createTestCertFile("ca2.pem", caContent2);
81
+ // Array of paths
82
+ options = await getAgentOptions({
83
+ caBundlePath: [caPath1, caPath2],
84
+ });
85
+ // Verify that both test certificates are included in the CA list
86
+ expect(options.ca).toContain(caContent1);
87
+ expect(options.ca).toContain(caContent2);
60
88
  });
61
- test("getCertificateContent should handle data URI without media type", () => {
62
- const testData = "simple-data";
63
- const base64Data = Buffer.from(testData, "utf8").toString("base64");
64
- const dataUri = `data:;base64,${base64Data}`;
65
- const result = getCertificateContent(dataUri);
66
- expect(result).toBe(testData);
89
+ test("getAgentOptions includes global certs when running as binary", async () => {
90
+ // Set up test certs in globalAgent
91
+ globalAgent.options.ca = ["global-cert-1", "global-cert-2"];
92
+ // Set IS_BINARY environment variable
93
+ process.env.IS_BINARY = "true";
94
+ const options = await getAgentOptions();
95
+ // Test for global certs
96
+ expect(options.ca).toContain("global-cert-1");
97
+ expect(options.ca).toContain("global-cert-2");
67
98
  });
68
- test("getCertificateContent should handle data URI without media type or encoding", () => {
69
- const testData = "simple-data";
70
- const base64Data = Buffer.from(testData, "utf8").toString("base64");
71
- const dataUri = `data:;base64,${base64Data}`;
72
- const result = getCertificateContent(dataUri);
73
- expect(result).toBe(testData);
99
+ test("getAgentOptions handles client certificate configuration", async () => {
100
+ // Create test certificate files
101
+ const clientCertContent = "-----BEGIN CERTIFICATE-----\nCLIENTCERT\n-----END CERTIFICATE-----";
102
+ const clientKeyContent = "-----BEGIN PRIVATE KEY-----\nCLIENTKEY\n-----END PRIVATE KEY-----";
103
+ const certPath = createTestCertFile("client.cert", clientCertContent);
104
+ const keyPath = createTestCertFile("client.key", clientKeyContent);
105
+ const clientCertOptions = {
106
+ clientCertificate: {
107
+ cert: certPath,
108
+ key: keyPath,
109
+ passphrase: "secret-passphrase",
110
+ },
111
+ };
112
+ const options = await getAgentOptions(clientCertOptions);
113
+ expect(options.cert).toBe(clientCertContent);
114
+ expect(options.key).toBe(clientKeyContent);
115
+ expect(options.passphrase).toBe("secret-passphrase");
74
116
  });
75
- test("getCertificateContent should handle relative file paths", () => {
76
- const filePath = "./certs/ca.pem";
77
- const expectedContent = "certificate from relative path";
78
- mockReadFileSync.mockReturnValue(expectedContent);
79
- const result = getCertificateContent(filePath);
80
- expect(mockReadFileSync).toHaveBeenCalledWith(filePath, "utf8");
81
- expect(result).toBe(expectedContent);
117
+ test("getAgentOptions handles client certificate without passphrase", async () => {
118
+ // Create test certificate files
119
+ const clientCertContent = "-----BEGIN CERTIFICATE-----\nCLIENTCERT2\n-----END CERTIFICATE-----";
120
+ const clientKeyContent = "-----BEGIN PRIVATE KEY-----\nCLIENTKEY2\n-----END PRIVATE KEY-----";
121
+ const certPath = createTestCertFile("client2.cert", clientCertContent);
122
+ const keyPath = createTestCertFile("client2.key", clientKeyContent);
123
+ const clientCertOptions = {
124
+ clientCertificate: {
125
+ cert: certPath,
126
+ key: keyPath,
127
+ },
128
+ };
129
+ const options = await getAgentOptions(clientCertOptions);
130
+ expect(options.cert).toBe(clientCertContent);
131
+ expect(options.key).toBe(clientKeyContent);
132
+ expect(options.passphrase).toBeUndefined();
82
133
  });
83
- test("getCertificateContent should handle data URI with special characters in URL encoding", () => {
84
- const testCert = "cert with spaces and special chars: !@#$%";
85
- const encodedData = encodeURIComponent(testCert);
86
- const dataUri = `data:text/plain,${encodedData}`;
87
- const result = getCertificateContent(dataUri);
88
- expect(result).toBe(testCert);
134
+ test("getAgentOptions reads NODE_EXTRA_CA_CERTS", async () => {
135
+ const extraCertContent = "-----BEGIN CERTIFICATE-----\nEXTRA_CERT\n-----END CERTIFICATE-----";
136
+ const certPath = createTestCertFile("extra-cert.cert", extraCertContent);
137
+ expect(CertsCache.getInstance().fixedCa).not.toContain(extraCertContent);
138
+ CertsCache.getInstance().clear();
139
+ process.env.NODE_EXTRA_CA_CERTS = certPath;
140
+ expect(CertsCache.getInstance().fixedCa).toContain(extraCertContent);
89
141
  });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Fetch function
3
+ *
4
+ * @param {string | URL | import('./request').default} url - Absolute url or Request instance
5
+ * @param {*} [options_] - Fetch options
6
+ * @return {Promise<import('./response').default>}
7
+ */
8
+ export default function fetch(url: string | URL | any, options_?: any): Promise<any>;
9
+ import { Blob } from "fetch-blob/from.js";
10
+ import { blobFrom } from "fetch-blob/from.js";
11
+ import { blobFromSync } from "fetch-blob/from.js";
12
+ import { File } from "fetch-blob/from.js";
13
+ import { fileFrom } from "fetch-blob/from.js";
14
+ import { fileFromSync } from "fetch-blob/from.js";
15
+ import { FormData } from "formdata-polyfill/esm.min.js";
16
+ export { AbortError, Blob, blobFrom, blobFromSync, FetchError, File, fileFrom, fileFromSync, FormData, Headers, isRedirect, Request, Response };