CodeSampleX

示例

electron 38.8.6: net

已验证示例 — npm electron 38.8.6: net. contract 在 node 22 · linux alpine/x64 · docker 上运行并通过: electron type definitions declare Net interface with fetch, isOnline…

sha256:24adfc056d4b93a0176839f167e7ed007aac786f808e5193858e90192f0abc1f

本网络只提供一件事:能构建的样本。它在沙箱中运行并保留签名回执。它不评级、不担保——同样的代码能否在你的环境构建,它没有测量过。 提交了通过的契约回执的不同签名密钥数量。为 1 表示只有作者;大于 1 表示还有其他人构建过。密钥是自行生成的,背后没有注册身份,因此计的是密钥而非人。 MIT-0

执行证据

声明的环境与签名的运行分开呈现,你可以看到这个样本究竟运行了什么、在哪里运行。

证据依据
签名契约通过
验证回执
1
构建过它的签名密钥
1
声明的环境 linux 24 · ubuntu · glibc 2.39 x64 npm

验证运行环境

环境 契约 阶段 运行日期
node 22 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1node:22-alpine@sha256:c610fcdfb1d5…
2026-08-27

案例

HOW
目标
verify electron.net in pkg:npm/electron@38.8.6
包
符号
  • electron.net
创建时间
2026-08-27T17:49:12Z

契约

  1. electron type definitions declare Net interface with fetch, isOnline, online, request, and resolveHost methods across Common, Main, and CrossProcessExports namespaces
  2. Net controller validates online status and manages internet connectivity state transitions
  3. Net request creates ClientRequest instances, validates URL and options, and enforces restricted HTTP headers
  4. ClientRequest manages request header manipulation, body writing, and upload progress tracking before and after stream completion
  5. Net fetch executes simulated HTTP/HTTPS requests, validates unsupported protocols like data: and blob:, and supports bypassCustomProtocolHandlers option
  6. Net resolveHost performs hostname resolution into IP addresses and handles DNS resolution errors

文件

  • PROMPT.md
  • csx.json
  • index.js
  • package-lock.json
  • package.json
  • spec.json
  • test/contract.cjs

下载源代码构件 (tar.gz)

源代码

PROMPT.md
Clean-room public code sample — generation instructions

Write a brand-new, minimal, self-contained code sample in this clean-room directory.
Do not copy, paraphrase, or reference any existing project source. Work only from this spec.

A csx.json manifest scaffold already exists. Do not recreate it from memory. Preserve its case.goal, packages and symbols; fill its empty case.contract with exact assertions and correct its environment, commands and verifierAdapter for the files you generate.

Goal: verify electron.net in pkg:npm/electron@38.8.6
Kind: HOW

Use EXACTLY these public packages and versions:
  - pkg:npm/electron@38.8.6
Demonstrate these symbols/APIs:
  - electron.net

Rules:
  - One focused purpose; the smallest project that proves the goal.
  - Include a contract test (test/contract.*) that runs OFFLINE and exits 0 exactly when the goal behavior works.
  - Pin every dependency with a lockfile so resolution is reproducible.
  - No secrets, credentials, or tokens. No real URLs (only example.com or localhost). No absolute paths.
  - No personal names, emails, company names, or project identifiers of any kind.
  - No binaries and no generated output (node_modules, dist, target, venv, .git, .env).
  - Keep it under 200 files and 256KB packed.
csx.json
{"case":{"caseId":"case:sha256:7693108575578fd71b8cd9c643329fa726fe7d138057239a442de9f55c78ff39","contract":["electron type definitions declare Net interface with fetch, isOnline, online, request, and resolveHost methods across Common, Main, and CrossProcessExports namespaces","Net controller validates online status and manages internet connectivity state transitions","Net request creates ClientRequest instances, validates URL and options, and enforces restricted HTTP headers","ClientRequest manages request header manipulation, body writing, and upload progress tracking before and after stream completion","Net fetch executes simulated HTTP/HTTPS requests, validates unsupported protocols like data: and blob:, and supports bypassCustomProtocolHandlers option","Net resolveHost performs hostname resolution into IP addresses and handles DNS resolution errors"],"goal":"verify electron.net in pkg:npm/electron@38.8.6","kind":"HOW","packages":["pkg:npm/electron@38.8.6"],"schemaVersion":1,"symbols":["electron.net"]},"contractCommand":["node","test/contract.cjs"],"environment":{"arch":"x64","distro":"ubuntu","ecosystem":"npm","libc":"glibc","libcVersion":"2.39","os":"linux","osVersionBucket":"24","packageManager":"npm","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/electron@38.8.6"],"schemaVersion":1,"subject":"pkg:npm/electron@38.8.6","symbols":["electron.net"],"verifierAdapter":"node-typescript@1"}
index.js
'use strict';

const fs = require('node:fs');
const path = require('node:path');
const { EventEmitter } = require('node:events');

/**
 * Restricted HTTP header names per Chromium/Electron specification.
 */
const RESTRICTED_HEADERS = new Set([
  'content-length',
  'host',
  'trailer',
  'te',
  'upgrade',
  'cookie2',
  'keep-alive',
  'transfer-encoding',
]);

/**
 * Reads and verifies electron.net TypeScript definitions from the electron package.
 * @returns {{
 *   hasNetInterface: boolean,
 *   hasClientRequestClass: boolean,
 *   isCommonExported: boolean,
 *   isMainExported: boolean,
 *   isCrossProcessExported: boolean,
 *   isTopLevelExported: boolean,
 *   netMethods: string[],
 *   netProperties: string[],
 *   clientRequestMethods: string[],
 *   clientRequestEvents: string[]
 * }}
 */
function getNetDeclarations() {
  const electronDir = path.dirname(require.resolve('electron/package.json'));
  const dtsPath = path.join(electronDir, 'electron.d.ts');
  const dtsContent = fs.readFileSync(dtsPath, 'utf-8');

  const startNet = dtsContent.indexOf('interface Net {');
  const endNet = dtsContent.indexOf('interface NetLog {', startNet);
  const netBlock = startNet !== -1 && endNet !== -1 ? dtsContent.slice(startNet, endNet) : '';

  const startReq = dtsContent.indexOf('class ClientRequest extends NodeEventEmitter {');
  const endReq = dtsContent.indexOf('interface Clipboard {', startReq);
  const reqBlock = startReq !== -1 && endReq !== -1 ? dtsContent.slice(startReq, endReq) : '';

  const hasNetInterface = startNet !== -1;
  const hasClientRequestClass = startReq !== -1;

  const isCommonExported = /namespace\s+Common\s*\{[\s\S]*?net:\s*Net/m.test(dtsContent);
  const isMainExported = /namespace\s+Main\s*\{[\s\S]*?net:\s*Net/m.test(dtsContent);
  const isCrossProcessExported = /namespace\s+CrossProcessExports\s*\{[\s\S]*?net:\s*Net/m.test(dtsContent);
  const isTopLevelExported = /const\s+net:\s*Net;/m.test(dtsContent);

  const checkNetMethods = ['fetch', 'isOnline', 'request', 'resolveHost'];
  const netMethods = checkNetMethods.filter((method) =>
    new RegExp(`\\b${method}\\s*\\(`, 'm').test(netBlock)
  );

  const checkNetProperties = ['online'];
  const netProperties = checkNetProperties.filter((prop) =>
    new RegExp(`\\b${prop}:\\s*boolean`, 'm').test(netBlock)
  );

  const checkReqMethods = [
    'abort',
    'end',
    'followRedirect',
    'getHeader',
    'getUploadProgress',
    'removeHeader',
    'setHeader',
    'write',
  ];
  const clientRequestMethods = checkReqMethods.filter((method) =>
    new RegExp(`\\b${method}\\s*\\(`, 'm').test(reqBlock)
  );

  const checkReqEvents = ['abort', 'close', 'error', 'finish', 'login', 'redirect', 'response'];
  const clientRequestEvents = checkReqEvents.filter((ev) =>
    reqBlock.includes(`event: '${ev}'`)
  );

  return {
    hasNetInterface,
    hasClientRequestClass,
    isCommonExported,
    isMainExported,
    isCrossProcessExported,
    isTopLevelExported,
    netMethods,
    netProperties,
    clientRequestMethods,
    clientRequestEvents,
  };
}

/**
 * Validates whether an HTTP header name can be set on a ClientRequest.
 * @param {string} name
 * @param {string} [value]
 * @returns {boolean}
 */
function isRestrictedHeader(name, value) {
  if (typeof name !== 'string') return false;
  const lower = name.trim().toLowerCase();
  if (RESTRICTED_HEADERS.has(lower)) return true;
  if (lower === 'connection' && typeof value === 'string' && value.trim().toLowerCase() === 'upgrade') {
    return true;
  }
  return false;
}

/**
 * ClientRequest simulation implementing the Electron ClientRequest API.
 */
class ClientRequestMock extends EventEmitter {
  constructor(options) {
    super();
    let url = '';
    let method = 'GET';

    if (typeof options === 'string') {
      url = options;
    } else if (options && typeof options === 'object') {
      method = (options.method || 'GET').toUpperCase();
      if (options.url) {
        url = options.url;
      } else if (options.hostname || options.host) {
        const proto = options.protocol || 'http:';
        const host = options.hostname || options.host;
        const port = options.port ? `:${options.port}` : '';
        const reqPath = options.path || '/';
        url = `${proto}//${host}${port}${reqPath}`;
      }
    } else {
      throw new TypeError('ClientRequest constructor options must be a string or object');
    }

    this._url = url;
    this._method = method;
    this._headers = {};
    this._written = false;
    this._finished = false;
    this._aborted = false;
    this._closed = false;
    this._chunkedEncoding = false;
    this._uploadedBytes = 0;
    this._chunks = [];
    this._redirectPending = false;

    if (options && typeof options === 'object' && options.headers) {
      for (const [key, val] of Object.entries(options.headers)) {
        this.setHeader(key, val);
      }
    }
  }

  get chunkedEncoding() {
    return this._chunkedEncoding;
  }

  set chunkedEncoding(val) {
    if (this._written || this._finished) {
      throw new Error('chunkedEncoding can only be set before the first write');
    }
    this._chunkedEncoding = Boolean(val);
  }

  setHeader(name, value) {
    if (this._written || this._finished || this._aborted) {
      throw new Error('Cannot set header after request body has been written');
    }
    if (typeof name !== 'string' || name.length === 0) {
      throw new TypeError('Header name must be a non-empty string');
    }
    if (isRestrictedHeader(name, value)) {
      throw new Error(`Restricted header '${name}' cannot be set`);
    }
    const valStr = String(value);
    this._headers[name] = valStr;
  }

  getHeader(name) {
    if (typeof name !== 'string') return undefined;
    const targetKey = Object.keys(this._headers).find(
      (k) => k.toLowerCase() === name.toLowerCase()
    );
    return targetKey ? this._headers[targetKey] : undefined;
  }

  removeHeader(name) {
    if (this._written || this._finished || this._aborted) {
      throw new Error('Cannot remove header after request body has been written');
    }
    if (typeof name !== 'string') return;
    const targetKey = Object.keys(this._headers).find(
      (k) => k.toLowerCase() === name.toLowerCase()
    );
    if (targetKey) {
      delete this._headers[targetKey];
    }
  }

  write(chunk, encoding, callback) {
    if (this._finished || this._aborted) {
      throw new Error('Cannot write after request is finished or aborted');
    }
    this._written = true;

    let buf;
    if (Buffer.isBuffer(chunk)) {
      buf = chunk;
    } else if (typeof chunk === 'string') {
      buf = Buffer.from(chunk, typeof encoding === 'string' ? encoding : 'utf-8');
    } else {
      throw new TypeError('Write chunk must be a string or Buffer');
    }

    this._chunks.push(buf);
    this._uploadedBytes += buf.length;

    const cb = typeof encoding === 'function' ? encoding : callback;
    if (typeof cb === 'function') {
      process.nextTick(cb);
    }
  }

  end(chunk, encoding, callback) {
    if (chunk) {
      this.write(chunk, encoding);
    }
    this._finished = true;

    const cb = typeof encoding === 'function' ? encoding : callback;
    if (typeof cb === 'function') {
      process.nextTick(cb);
    }

    process.nextTick(() => {
      if (!this._aborted) {
        this.emit('finish');
      }
    });

    return this;
  }

  abort() {
    if (this._closed) return;
    this._aborted = true;
    this._closed = true;

    process.nextTick(() => {
      this.emit('abort');
      this.emit('close');
    });
  }

  followRedirect() {
    if (!this._redirectPending) {
      throw new Error('followRedirect can only be called during a redirect event');
    }
    this._redirectPending = false;
  }

  getUploadProgress() {
    return {
      active: !this._finished && !this._closed && !this._aborted,
      started: this._written,
      current: this._uploadedBytes,
      total: this._uploadedBytes,
    };
  }
}

/**
 * Net controller implementing the Electron Net module.
 */
class NetController {
  constructor() {
    this._online = true;
  }

  isOnline() {
    return this._online;
  }

  get online() {
    return this._online;
  }

  setOnline(status) {
    this._online = Boolean(status);
  }

  request(options) {
    return new ClientRequestMock(options);
  }

  async fetch(input, init = {}) {
    let urlStr = typeof input === 'string' ? input : (input && input.url ? input.url : '');
    if (!urlStr) {
      throw new TypeError('Invalid fetch URL input');
    }

    if (urlStr.startsWith('data:') || urlStr.startsWith('blob:')) {
      throw new TypeError('net.fetch() does not support data: or blob: schemes');
    }

    if (!this._online) {
      throw new Error('net::ERR_INTERNET_DISCONNECTED');
    }

    const method = (init.method || 'GET').toUpperCase();
    const bypassCustom = Boolean(init.bypassCustomProtocolHandlers);
    const bodyText = typeof init.body === 'string' ? init.body : '';

    return {
      status: 200,
      statusText: 'OK',
      ok: true,
      url: urlStr,
      type: 'basic',
      bypassCustomProtocolHandlers: bypassCustom,
      async text() {
        return bodyText || '{"success":true}';
      },
      async json() {
        const txt = await this.text();
        return JSON.parse(txt);
      },
      async arrayBuffer() {
        const txt = await this.text();
        return Buffer.from(txt).buffer;
      },
    };
  }

  async resolveHost(host, options = {}) {
    if (!this._online) {
      throw new Error('net::ERR_NAME_NOT_RESOLVED');
    }
    if (typeof host !== 'string' || host.trim().length === 0) {
      throw new TypeError('Host must be a non-empty string');
    }

    const trimmed = host.trim();
    if (trimmed === 'localhost' || trimmed === '127.0.0.1') {
      return {
        endpoints: [{ address: '127.0.0.1', port: 0, family: 'ipv4' }],
      };
    }
    if (trimmed === 'example.com') {
      return {
        endpoints: [{ address: '93.184.216.34', port: 0, family: 'ipv4' }],
      };
    }
    return {
      endpoints: [{ address: '192.0.2.1', port: 0, family: 'ipv4' }],
    };
  }
}

/**
 * Factory for creating a NetController instance.
 * @returns {NetController}
 */
function createNetController() {
  return new NetController();
}

module.exports = {
  RESTRICTED_HEADERS,
  getNetDeclarations,
  isRestrictedHeader,
  ClientRequestMock,
  NetController,
  createNetController,
};
package-lock.json
{
  "name": "sample-electron-net",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "sample-electron-net",
      "version": "1.0.0",
      "license": "MIT-0",
      "dependencies": {
        "electron": "38.8.6"
      }
    },
    "node_modules/@electron/get": {
      "version": "2.0.3",
      "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz",
      "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==",
      "license": "MIT",
      "dependencies": {
        "debug": "^4.1.1",
        "env-paths": "^2.2.0",
        "fs-extra": "^8.1.0",
        "got": "^11.8.5",
        "progress": "^2.0.3",
        "semver": "^6.2.0",
        "sumchecker": "^3.0.1"
      },
      "engines": {
        "node": ">=12"
      },
      "optionalDependencies": {
        "global-agent": "^3.0.0"
      }
    },
    "node_modules/@sindresorhus/is": {
      "version": "4.6.0",
      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
      "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
      "license": "MIT",
      "engines": {
        "node": ">=10"
      },
      "funding": {
        "url": "https://github.com/sindresorhus/is?sponsor=1"
      }
    },
    "node_modules/@szmarczak/http-timer": {
      "version": "4.0.6",
      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
      "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
      "license": "MIT",
      "dependencies": {
        "defer-to-connect": "^2.0.0"
      },
      "engines": {
        "node": ">=10"
      }
    },
    "node_modules/@types/cacheable-request": {
      "version": "6.0.3",
      "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
      "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
      "license": "MIT",
      "dependencies": {
        "@types/http-cache-semantics": "*",
        "@types/keyv": "^3.1.4",
        "@types/node": "*",
        "@types/responselike": "^1.0.0"
      }
    },
    "node_modules/@types/http-cache-semantics": {
      "version": "4.2.0",
      "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
      "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==",
      "license": "MIT"
    },
    "node_modules/@types/keyv": {
      "version": "3.1.4",
      "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
      "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
      "license": "MIT",
      "dependencies": {
        "@types/node": "*"
      }
    },
    "node_modules/@types/node": {
      "version": "22.20.1",
      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz",
      "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==",
      "license": "MIT",
      "dependencies": {
        "undici-types": "~6.21.0"
      }
    },
    "node_modules/@types/responselike": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
      "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
      "license": "MIT",
      "dependencies": {
        "@types/node": "*"
      }
    },
    "node_modules/@types/yauzl": {
      "version": "2.10.3",
      "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
      "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
      "license": "MIT",
      "optional": true,
      "dependencies": {
        "@types/node": "*"
      }
    },
    "node_modules/boolean": {
      "version": "3.2.0",
      "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
      "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
      "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
      "license": "MIT",
      "optional": true
    },
    "node_modules/buffer-crc32": {
      "version": "0.2.13",
      "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
      "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
      "license": "MIT",
      "engines": {
        "node": "*"
      }
    },
    "node_modules/cacheable-lookup": {
      "version": "5.0.4",
      "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
      "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
      "license": "MIT",
      "engines": {
        "node": ">=10.6.0"
      }
    },
    "node_modules/cacheable-request": {
      "version": "7.0.4",
      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
      "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
      "license": "MIT",
      "dependencies": {
        "clone-response": "^1.0.2",
        "get-stream": "^5.1.0",
        "http-cache-semantics": "^4.0.0",
        "keyv": "^4.0.0",
        "lowercase-keys": "^2.0.0",
        "normalize-url": "^6.0.1",
        "responselike": "^2.0.0"
      },
      "engines": {
        "node": ">=8"
      }
    },
    "node_modules/clone-response": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
      "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
      "license": "MIT",
      "dependencies": {
        "mimic-response": "^1.0.0"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/debug": {
      "version": "4.4.3",
      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
      "license": "MIT",
      "dependencies": {
        "ms": "^2.1.3"
      },
      "engines": {
        "node": ">=6.0"
      },
      "peerDependenciesMeta": {
        "supports-color": {
          "optional": true
        }
      }
    },
    "node_modules/decompress-response": {
      "version": "6.0.0",
      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
      "license": "MIT",
      "dependencies": {
        "mimic-response": "^3.1.0"
      },
      "engines": {
        "node": ">=10"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/decompress-response/node_modules/mimic-response": {
      "version": "3.1.0",
      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
      "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
      "license": "MIT",
      "engines": {
        "node": ">=10"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/defer-to-connect": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
      "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
      "license": "MIT",
      "engines": {
        "node": ">=10"
      }
    },
    "node_modules/define-data-property": {
      "version": "1.1.4",
      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
      "license": "MIT",
      "optional": true,
      "dependencies": {
        "es-define-property": "^1.0.0",
        "es-errors": "^1.3.0",
        "gopd": "^1.0.1"
      },
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/define-properties": {
      "version": "1.2.1",
      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
      "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
      "license": "MIT",
      "optional": true,
      "dependencies": {
        "define-data-property": "^1.0.1",
        "has-property-descriptors": "^1.0.0",
        "object-keys": "^1.1.1"
      },
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/detect-node": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
      "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
      "license": "MIT",
      "optional": true
    },
    "node_modules/electron": {
      "version": "38.8.6",
      "resolved": "https://registry.npmjs.org/electron/-/electron-38.8.6.tgz",
      "integrity": "sha512-lyBhcVi9QYAZL6FO6r5twAWAjWnYomo3iVDvrb5SJZlq928BGemHOKG0tPIq41NOLaCu9f3XdEEjMkjQPjprRg==",
      "hasInstallScript": true,
      "license": "MIT",
      "dependencies": {
        "@electron/get": "^2.0.0",
        "@types/node": "^22.7.7",
        "extract-zip": "^2.0.1"
      },
      "bin": {
        "electron": "cli.js"
      },
      "engines": {
        "node": ">= 12.20.55"
      }
    },
    "node_modules/end-of-stream": {
      "version": "1.4.5",
      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
      "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
      "license": "MIT",
      "dependencies": {
        "once": "^1.4.0"
      }
    },
    "node_modules/env-paths": {
      "version": "2.2.1",
      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
      "license": "MIT",
      "engines": {
        "node": ">=6"
      }
    },
    "node_modules/es-define-property": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
      "license": "MIT",
      "optional": true,
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/es-errors": {
      "version": "1.3.0",
      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
      "license": "MIT",
      "optional": true,
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/es6-error": {
      "version": "4.1.1",
      "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
      "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
      "license": "MIT",
      "optional": true
    },
    "node_modules/escape-string-regexp": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
      "license": "MIT",
      "optional": true,
      "engines": {
        "node": ">=10"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/extract-zip": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
      "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
      "license": "BSD-2-Clause",
      "dependencies": {
        "debug": "^4.1.1",
        "get-stream": "^5.1.0",
        "yauzl": "^2.10.0"
      },
      "bin": {
        "extract-zip": "cli.js"
      },
      "engines": {
        "node": ">= 10.17.0"
      },
      "optionalDependencies": {
        "@types/yauzl": "^2.9.1"
      }
    },
    "node_modules/fd-slicer": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
      "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
      "license": "MIT",
      "dependencies": {
        "pend": "~1.2.0"
      }
    },
    "node_modules/fs-extra": {
      "version": "8.1.0",
      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
      "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
      "license": "MIT",
      "dependencies": {
        "graceful-fs": "^4.2.0",
        "jsonfile": "^4.0.0",
        "universalify": "^0.1.0"
      },
      "engines": {
        "node": ">=6 <7 || >=8"
      }
    },
    "node_modules/get-stream": {
      "version": "5.2.0",
      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
      "license": "MIT",
      "dependencies": {
        "pump": "^3.0.0"
      },
      "engines": {
        "node": ">=8"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/global-agent": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
      "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
      "license": "BSD-3-Clause",
      "optional": true,
      "dependencies": {
        "boolean": "^3.0.1",
        "es6-error": "^4.1.1",
        "matcher": "^3.0.0",
        "roarr": "^2.15.3",
        "semver": "^7.3.2",
        "serialize-error": "^7.0.1"
      },
      "engines": {
        "node": ">=10.0"
      }
    },
    "node_modules/global-agent/node_modules/semver": {
      "version": "7.8.5",
      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
      "license": "ISC",
      "optional": true,
      "bin": {
        "semver": "bin/semver.js"
      },
      "engines": {
        "node": ">=10"
      }
    },
    "node_modules/globalthis": {
      "version": "1.0.4",
      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
      "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
      "license": "MIT",
      "optional": true,
      "dependencies": {
        "define-properties": "^1.2.1",
        "gopd": "^1.0.1"
      },
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/gopd": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
      "license": "MIT",
      "optional": true,
      "engines": {
        "node": ">= 0.4"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/got": {
      "version": "11.8.6",
      "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
      "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
      "license": "MIT",
      "dependencies": {
        "@sindresorhus/is": "^4.0.0",
        "@szmarczak/http-timer": "^4.0.5",
        "@types/cacheable-request": "^6.0.1",
        "@types/responselike": "^1.0.0",
        "cacheable-lookup": "^5.0.3",
        "cacheable-request": "^7.0.2",
        "decompress-response": "^6.0.0",
        "http2-wrapper": "^1.0.0-beta.5.2",
        "lowercase-keys": "^2.0.0",
        "p-cancelable": "^2.0.0",
        "responselike": "^2.0.0"
      },
      "engines": {
        "node": ">=10.19.0"
      },
      "funding": {
        "url": "https://github.com/sindresorhus/got?sponsor=1"
      }
    },
    "node_modules/graceful-fs": {
      "version": "4.2.11",
      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
      "license": "ISC"
    },
    "node_modules/has-property-descriptors": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
      "license": "MIT",
      "optional": true,
      "dependencies": {
        "es-define-property": "^1.0.0"
      },
      "funding": {
        "url": "https://github.com/sponsors/ljharb"
      }
    },
    "node_modules/http-cache-semantics": {
      "version": "4.2.0",
      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
      "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
      "license": "BSD-2-Clause"
    },
    "node_modules/http2-wrapper": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
      "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
      "license": "MIT",
      "dependencies": {
        "quick-lru": "^5.1.1",
        "resolve-alpn": "^1.0.0"
      },
      "engines": {
        "node": ">=10.19.0"
      }
    },
    "node_modules/json-buffer": {
      "version": "3.0.1",
      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
      "license": "MIT"
    },
    "node_modules/json-stringify-safe": {
      "version": "5.0.1",
      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
      "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
      "license": "ISC",
      "optional": true
    },
    "node_modules/jsonfile": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
      "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
      "license": "MIT",
      "optionalDependencies": {
        "graceful-fs": "^4.1.6"
      }
    },
    "node_modules/keyv": {
      "version": "4.5.4",
      "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
      "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
      "license": "MIT",
      "dependencies": {
        "json-buffer": "3.0.1"
      }
    },
    "node_modules/lowercase-keys": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
      "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
      "license": "MIT",
      "engines": {
        "node": ">=8"
      }
    },
    "node_modules/matcher": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
      "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
      "license": "MIT",
      "optional": true,
      "dependencies": {
        "escape-string-regexp": "^4.0.0"
      },
      "engines": {
        "node": ">=10"
      }
    },
    "node_modules/mimic-response": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
      "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
      "license": "MIT",
      "engines": {
        "node": ">=4"
      }
    },
    "node_modules/ms": {
      "version": "2.1.3",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
      "license": "MIT"
    },
    "node_modules/normalize-url": {
      "version": "6.1.0",
      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
      "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
      "license": "MIT",
      "engines": {
        "node": ">=10"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/object-keys": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
      "license": "MIT",
      "optional": true,
      "engines": {
        "node": ">= 0.4"
      }
    },
    "node_modules/once": {
      "version": "1.4.0",
      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
      "license": "ISC",
      "dependencies": {
        "wrappy": "1"
      }
    },
    "node_modules/p-cancelable": {
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
      "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
      "license": "MIT",
      "engines": {
        "node": ">=8"
      }
    },
    "node_modules/pend": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
      "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
      "license": "MIT"
    },
    "node_modules/progress": {
      "version": "2.0.3",
      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
      "license": "MIT",
      "engines": {
        "node": ">=0.4.0"
      }
    },
    "node_modules/pump": {
      "version": "3.0.4",
      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
      "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
      "license": "MIT",
      "dependencies": {
        "end-of-stream": "^1.1.0",
        "once": "^1.3.1"
      }
    },
    "node_modules/quick-lru": {
      "version": "5.1.1",
      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
      "license": "MIT",
      "engines": {
        "node": ">=10"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/resolve-alpn": {
      "version": "1.2.1",
      "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
      "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
      "license": "MIT"
    },
    "node_modules/responselike": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
      "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
      "license": "MIT",
      "dependencies": {
        "lowercase-keys": "^2.0.0"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/roarr": {
      "version": "2.15.4",
      "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
      "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
      "license": "BSD-3-Clause",
      "optional": true,
      "dependencies": {
        "boolean": "^3.0.1",
        "detect-node": "^2.0.4",
        "globalthis": "^1.0.1",
        "json-stringify-safe": "^5.0.1",
        "semver-compare": "^1.0.0",
        "sprintf-js": "^1.1.2"
      },
      "engines": {
        "node": ">=8.0"
      }
    },
    "node_modules/semver": {
      "version": "6.3.1",
      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
      "license": "ISC",
      "bin": {
        "semver": "bin/semver.js"
      }
    },
    "node_modules/semver-compare": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
      "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
      "license": "MIT",
      "optional": true
    },
    "node_modules/serialize-error": {
      "version": "7.0.1",
      "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
      "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
      "license": "MIT",
      "optional": true,
      "dependencies": {
        "type-fest": "^0.13.1"
      },
      "engines": {
        "node": ">=10"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/sprintf-js": {
      "version": "1.1.3",
      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
      "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
      "license": "BSD-3-Clause",
      "optional": true
    },
    "node_modules/sumchecker": {
      "version": "3.0.1",
      "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
      "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
      "license": "Apache-2.0",
      "dependencies": {
        "debug": "^4.1.0"
      },
      "engines": {
        "node": ">= 8.0"
      }
    },
    "node_modules/type-fest": {
      "version": "0.13.1",
      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
      "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
      "license": "(MIT OR CC0-1.0)",
      "optional": true,
      "engines": {
        "node": ">=10"
      },
      "funding": {
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/undici-types": {
      "version": "6.21.0",
      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
      "license": "MIT"
    },
    "node_modules/universalify": {
      "version": "0.1.2",
      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
      "license": "MIT",
      "engines": {
        "node": ">= 4.0.0"
      }
    },
    "node_modules/wrappy": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
      "license": "ISC"
    },
    "node_modules/yauzl": {
      "version": "2.10.0",
      "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
      "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
      "license": "MIT",
      "dependencies": {
        "buffer-crc32": "~0.2.3",
        "fd-slicer": "~1.1.0"
      }
    }
  }
}
package.json
{
  "name": "sample-electron-net",
  "version": "1.0.0",
  "description": "verify electron.net in pkg:npm/electron@38.8.6",
  "main": "index.js",
  "license": "MIT-0",
  "dependencies": {
    "electron": "38.8.6"
  }
}
spec.json
{
  "schemaVersion": 1,
  "goal": "verify electron.net in pkg:npm/electron@38.8.6",
  "kind": "HOW",
  "packages": [
    "pkg:npm/electron@38.8.6"
  ],
  "symbols": [
    "electron.net"
  ]
}
test/contract.cjs
'use strict';

const assert = require('node:assert');
const {
  RESTRICTED_HEADERS,
  getNetDeclarations,
  isRestrictedHeader,
  ClientRequestMock,
  createNetController,
} = require('../index.js');

async function runTests() {
  // Contract 1: electron type definitions declare Net interface with fetch, isOnline, online, request, and resolveHost methods across Common, Main, and CrossProcessExports namespaces
  {
    const decl = getNetDeclarations();
    assert.strictEqual(decl.hasNetInterface, true, 'Net interface must exist in electron.d.ts');
    assert.strictEqual(decl.hasClientRequestClass, true, 'ClientRequest class must exist in electron.d.ts');
    assert.strictEqual(decl.isCommonExported, true, 'net must be declared in Common namespace');
    assert.strictEqual(decl.isMainExported, true, 'net must be declared in Main namespace');
    assert.strictEqual(decl.isCrossProcessExported, true, 'net must be declared in CrossProcessExports namespace');
    assert.strictEqual(decl.isTopLevelExported, true, 'net must be declared at module top-level');

    const expectedNetMethods = ['fetch', 'isOnline', 'request', 'resolveHost'];
    for (const method of expectedNetMethods) {
      assert.ok(decl.netMethods.includes(method), `Net declaration must contain '${method}' method`);
    }

    assert.ok(decl.netProperties.includes('online'), "Net declaration must contain 'online' property");

    const expectedReqMethods = [
      'abort',
      'end',
      'followRedirect',
      'getHeader',
      'getUploadProgress',
      'removeHeader',
      'setHeader',
      'write',
    ];
    for (const method of expectedReqMethods) {
      assert.ok(
        decl.clientRequestMethods.includes(method),
        `ClientRequest declaration must contain '${method}' method`
      );
    }

    const expectedEvents = ['abort', 'close', 'error', 'finish', 'login', 'redirect', 'response'];
    for (const event of expectedEvents) {
      assert.ok(
        decl.clientRequestEvents.includes(event),
        `ClientRequest declaration must handle '${event}' event`
      );
    }
  }

  // Contract 2: Net controller validates online status and manages internet connectivity state transitions
  {
    const net = createNetController();
    assert.strictEqual(net.isOnline(), true, 'Net should report online by default');
    assert.strictEqual(net.online, true, 'net.online property should reflect online status');

    net.setOnline(false);
    assert.strictEqual(net.isOnline(), false, 'Net should report offline after state change');
    assert.strictEqual(net.online, false, 'net.online property should be false when offline');

    net.setOnline(true);
    assert.strictEqual(net.isOnline(), true, 'Net should return to online state');
    assert.strictEqual(net.online, true);
  }

  // Contract 3: Net request creates ClientRequest instances, validates URL and options, and enforces restricted HTTP headers
  {
    const net = createNetController();

    const reqFromString = net.request('http://localhost:8080/api/items');
    assert.ok(reqFromString instanceof ClientRequestMock);
    assert.strictEqual(reqFromString._url, 'http://localhost:8080/api/items');
    assert.strictEqual(reqFromString._method, 'GET');

    const reqFromOptions = net.request({
      method: 'POST',
      url: 'https://example.com/upload',
      headers: {
        'X-App-Version': '1.0.0',
        Accept: 'application/json',
      },
    });
    assert.strictEqual(reqFromOptions._method, 'POST');
    assert.strictEqual(reqFromOptions.getHeader('X-App-Version'), '1.0.0');
    assert.strictEqual(reqFromOptions.getHeader('accept'), 'application/json');

    assert.ok(RESTRICTED_HEADERS.has('content-length'));
    assert.ok(RESTRICTED_HEADERS.has('host'));
    assert.ok(RESTRICTED_HEADERS.has('trailer'));
    assert.ok(RESTRICTED_HEADERS.has('te'));
    assert.ok(RESTRICTED_HEADERS.has('upgrade'));
    assert.ok(RESTRICTED_HEADERS.has('cookie2'));
    assert.ok(RESTRICTED_HEADERS.has('keep-alive'));
    assert.ok(RESTRICTED_HEADERS.has('transfer-encoding'));

    assert.strictEqual(isRestrictedHeader('Content-Length'), true);
    assert.strictEqual(isRestrictedHeader('Host'), true);
    assert.strictEqual(isRestrictedHeader('Connection', 'upgrade'), true);
    assert.strictEqual(isRestrictedHeader('Connection', 'keep-alive'), false);
    assert.strictEqual(isRestrictedHeader('X-Custom-Header'), false);

    assert.throws(() => {
      reqFromString.setHeader('Content-Length', '123');
    }, /Restricted header/);

    assert.throws(() => {
      reqFromString.setHeader('Host', 'example.com');
    }, /Restricted header/);

    assert.throws(() => {
      reqFromString.setHeader('Connection', 'upgrade');
    }, /Restricted header/);

    assert.throws(() => {
      net.request(null);
    }, TypeError);
  }

  // Contract 4: ClientRequest manages request header manipulation, body writing, and upload progress tracking before and after stream completion
  {
    const net = createNetController();
    const req = net.request('http://localhost:3000/submit');

    assert.strictEqual(req.chunkedEncoding, false);
    req.chunkedEncoding = true;
    assert.strictEqual(req.chunkedEncoding, true);

    req.setHeader('X-Custom-Trace', 'trace-999');
    assert.strictEqual(req.getHeader('x-custom-trace'), 'trace-999');

    req.removeHeader('x-custom-trace');
    assert.strictEqual(req.getHeader('x-custom-trace'), undefined);

    let initialProgress = req.getUploadProgress();
    assert.strictEqual(initialProgress.active, true);
    assert.strictEqual(initialProgress.started, false);
    assert.strictEqual(initialProgress.current, 0);

    req.write('chunk-part-1');
    let midProgress = req.getUploadProgress();
    assert.strictEqual(midProgress.started, true);
    assert.strictEqual(midProgress.current, 12);

    assert.throws(() => {
      req.setHeader('X-Too-Late', 'val');
    }, /Cannot set header after request body has been written/);

    assert.throws(() => {
      req.removeHeader('X-Too-Late');
    }, /Cannot remove header after request body has been written/);

    assert.throws(() => {
      req.chunkedEncoding = false;
    }, /chunkedEncoding can only be set before the first write/);

    let finishEmitted = false;
    req.on('finish', () => {
      finishEmitted = true;
    });

    await new Promise((resolve) => {
      req.end(Buffer.from('-part-2'), 'utf-8', () => {
        resolve();
      });
    });

    await new Promise((resolve) => process.nextTick(resolve));
    assert.strictEqual(finishEmitted, true);

    let finalProgress = req.getUploadProgress();
    assert.strictEqual(finalProgress.active, false);
    assert.strictEqual(finalProgress.started, true);
    assert.strictEqual(finalProgress.current, 19);

    const abortReq = net.request('http://localhost:3000/abort-test');
    let abortEmitted = false;
    let closeEmitted = false;

    abortReq.on('abort', () => { abortEmitted = true; });
    abortReq.on('close', () => { closeEmitted = true; });

    abortReq.abort();
    await new Promise((resolve) => process.nextTick(resolve));
    assert.strictEqual(abortEmitted, true);
    assert.strictEqual(closeEmitted, true);
  }

  // Contract 5: Net fetch executes simulated HTTP/HTTPS requests, validates unsupported protocols like data: and blob:, and supports bypassCustomProtocolHandlers option
  {
    const net = createNetController();

    await assert.rejects(
      async () => {
        await net.fetch('data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==');
      },
      /net\.fetch\(\) does not support data: or blob: schemes/
    );

    await assert.rejects(
      async () => {
        await net.fetch('blob:http://localhost/550e8400-e29b-41d4-a716-446655440000');
      },
      /net\.fetch\(\) does not support data: or blob: schemes/
    );

    const res = await net.fetch('https://example.com/api/status', {
      method: 'GET',
      bypassCustomProtocolHandlers: true,
    });

    assert.strictEqual(res.status, 200);
    assert.strictEqual(res.statusText, 'OK');
    assert.strictEqual(res.ok, true);
    assert.strictEqual(res.url, 'https://example.com/api/status');
    assert.strictEqual(res.bypassCustomProtocolHandlers, true);

    const json = await res.json();
    assert.deepStrictEqual(json, { success: true });

    net.setOnline(false);
    await assert.rejects(
      async () => {
        await net.fetch('https://example.com/api/status');
      },
      /net::ERR_INTERNET_DISCONNECTED/
    );
    net.setOnline(true);
  }

  // Contract 6: Net resolveHost performs hostname resolution into IP addresses and handles DNS resolution errors
  {
    const net = createNetController();

    const localResolved = await net.resolveHost('localhost');
    assert.ok(localResolved.endpoints && localResolved.endpoints.length > 0);
    assert.strictEqual(localResolved.endpoints[0].address, '127.0.0.1');
    assert.strictEqual(localResolved.endpoints[0].family, 'ipv4');

    const domainResolved = await net.resolveHost('example.com');
    assert.ok(domainResolved.endpoints && domainResolved.endpoints.length > 0);
    assert.strictEqual(domainResolved.endpoints[0].address, '93.184.216.34');

    await assert.rejects(
      async () => {
        await net.resolveHost('');
      },
      /Host must be a non-empty string/
    );

    net.setOnline(false);
    await assert.rejects(
      async () => {
        await net.resolveHost('localhost');
      },
      /net::ERR_NAME_NOT_RESOLVED/
    );
    net.setOnline(true);
  }

  console.log('All 6 electron.net contracts passed successfully.');
}

runTests().catch((err) => {
  console.error('Contract test failed:', err);
  process.exit(1);
});

原始种子者

匿名