Пример
electron 38.8.6: protocol
Проверенный пример — npm electron 38.8.6: protocol. Контракт выполнен на node 22 · linux alpine/x64 · docker и пройден: electron type definitions declare…
sha256:58bdd1d293429f975bfdacd7c6e56aac56af4c455fb9b362a171347e33a59019
Эта сеть предлагает одно: образец, который собирается. Она запустила его в песочнице и сохранила подписанную квитанцию. Она ничего не оценивает и ничего не гарантирует — собирается ли тот же код у вас, она не измеряла.
Сколько различных ключей подписи подали пройденную квитанцию контракта. Один — только автор; больше одного — значит, кто-то ещё тоже собрал. Ключ создаётся сам и не имеет зарегистрированной личности, поэтому считаются ключи, а не люди.
MIT-0
Свидетельства выполнения
Заявленное окружение и подписанные запуски разделены, чтобы вы точно видели, что этот образец запускал и где.
- Основа свидетельства
- Подписанный контракт пройден
- Квитанции проверки
- 1
- Ключи подписи, собравшие его
- 1
Заявленная среда
node 22.23 linux 24 · ubuntu · glibc 2.39 x64 node 22.23 javascript npm 10
Среды запусков проверки
| Окружение | Контракт | Этапы | Запуск |
|---|---|---|---|
| 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.protocol in pkg:npm/electron@38.8.6
- Пакеты
- Символы
-
- electron.protocol
- Окружение
- node 22.23.2
- Создан
- 2026-08-27T17:52:36Z
Контракт
- electron type definitions declare Protocol interface, CustomScheme, Privileges, ProtocolRequest, and ProtocolResponse interfaces across Main and CrossProcessExports namespaces
- protocol.handle and protocol.unhandle register and remove fetch-based request handlers with status query via isProtocolHandled
- protocol.registerSchemesAsPrivileged validates and registers custom schemes with standard, secure, CSP bypass, and fetch API privileges
- protocol legacy handlers registerBufferProtocol, registerStringProtocol, and registerFileProtocol respond to incoming requests with callbacks
- protocol request interceptors interceptBufferProtocol, interceptStringProtocol, and uninterceptProtocol override and restore protocol handling
- protocol normalizes request URLs, validates request metadata, and supports stream response handlers
Файлы
- PROMPT.md
- csx.json
- index.js
- package-lock.json
- package.json
- spec.json
- test/contract.cjs
Исходный код
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.protocol 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.protocol
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.
{"case":{"caseId":"case:sha256:4282e167cc268b41d4359055f3a1bea150fa77d0c94396e08b53b2fa7649673a","contract":["electron type definitions declare Protocol interface, CustomScheme, Privileges, ProtocolRequest, and ProtocolResponse interfaces across Main and CrossProcessExports namespaces","protocol.handle and protocol.unhandle register and remove fetch-based request handlers with status query via isProtocolHandled","protocol.registerSchemesAsPrivileged validates and registers custom schemes with standard, secure, CSP bypass, and fetch API privileges","protocol legacy handlers registerBufferProtocol, registerStringProtocol, and registerFileProtocol respond to incoming requests with callbacks","protocol request interceptors interceptBufferProtocol, interceptStringProtocol, and uninterceptProtocol override and restore protocol handling","protocol normalizes request URLs, validates request metadata, and supports stream response handlers"],"goal":"verify electron.protocol in pkg:npm/electron@38.8.6","kind":"HOW","packages":["pkg:npm/electron@38.8.6"],"schemaVersion":1,"symbols":["electron.protocol"]},"contractCommand":["node","test/contract.cjs"],"environment":{"arch":"x64","distro":"ubuntu","ecosystem":"npm","executionContext":"node","language":"javascript","libc":"glibc","libcVersion":"2.39","moduleSystem":"cjs","os":"linux","osVersionBucket":"24","packageManager":"npm","packageManagerVersion":"10.9.8","runtime":"node","runtimeVersion":"22.23.2","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/electron@38.8.6"],"schemaVersion":1,"subject":"pkg:npm/electron@38.8.6","symbols":["electron.protocol"],"verifierAdapter":"node-typescript@1"}
'use strict';
const fs = require('node:fs');
const path = require('node:path');
const { Readable } = require('node:stream');
/**
* Validates whether a scheme name follows standard RFC/URI scheme rules.
* @param {string} scheme
* @returns {boolean}
*/
function isValidSchemeName(scheme) {
if (typeof scheme !== 'string' || scheme.length === 0) {
return false;
}
return /^[a-zA-Z][a-zA-Z0-9+.-]*$/.test(scheme);
}
/**
* Valid privilege property keys allowed on CustomScheme.privileges.
*/
const ALLOWED_PRIVILEGE_KEYS = new Set([
'standard',
'secure',
'bypassCSP',
'allowServiceWorkers',
'supportFetchAPI',
'corsEnabled',
'stream',
'codeCache',
]);
/**
* Validates privileges object for custom schemes.
* @param {object} privileges
* @returns {boolean}
*/
function validatePrivileges(privileges) {
if (privileges === null || typeof privileges !== 'object' || Array.isArray(privileges)) {
return false;
}
for (const [key, val] of Object.entries(privileges)) {
if (!ALLOWED_PRIVILEGE_KEYS.has(key)) {
return false;
}
if (typeof val !== 'boolean') {
return false;
}
}
return true;
}
/**
* Reads and verifies electron.protocol TypeScript definitions from the electron package.
* @returns {{
* hasProtocolInterface: boolean,
* hasCustomSchemeInterface: boolean,
* hasPrivilegesInterface: boolean,
* hasProtocolRequestInterface: boolean,
* hasProtocolResponseInterface: boolean,
* protocolMethods: string[],
* customSchemeFields: string[],
* privilegeFields: string[],
* isMainExported: boolean,
* isCrossProcessExported: boolean,
* isTopLevelExported: boolean,
* isSessionDeclared: boolean,
* allDeclared: boolean
* }}
*/
function getProtocolDeclarations() {
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 expectedMethods = [
'handle',
'unhandle',
'isProtocolHandled',
'registerSchemesAsPrivileged',
'registerBufferProtocol',
'registerFileProtocol',
'registerHttpProtocol',
'registerStreamProtocol',
'registerStringProtocol',
'unregisterProtocol',
'isProtocolRegistered',
'interceptBufferProtocol',
'interceptFileProtocol',
'interceptHttpProtocol',
'interceptStreamProtocol',
'interceptStringProtocol',
'uninterceptProtocol',
'isProtocolIntercepted',
];
const protocolMethods = [];
const protoMatch = dtsContent.match(/interface\s+Protocol\s*\{([\s\S]*?)\n\s*\}/);
if (protoMatch) {
const body = protoMatch[1];
for (const m of expectedMethods) {
if (body.includes(`${m}(`)) {
protocolMethods.push(m);
}
}
}
const customSchemeFields = [];
const csMatch = dtsContent.match(/interface\s+CustomScheme\s*\{([\s\S]*?)\n\s*\}/);
if (csMatch) {
const csBody = csMatch[1];
if (/\bscheme:\s*string/m.test(csBody)) {
customSchemeFields.push('scheme');
}
if (/\bprivileges\?:/m.test(csBody)) {
customSchemeFields.push('privileges');
}
}
const expectedPrivileges = [
'standard',
'secure',
'bypassCSP',
'allowServiceWorkers',
'supportFetchAPI',
'corsEnabled',
'stream',
'codeCache',
];
const privilegeFields = [];
const privMatch = dtsContent.match(/interface\s+Privileges\s*\{([\s\S]*?)\n\s*\}/);
if (privMatch) {
const privBody = privMatch[1];
for (const p of expectedPrivileges) {
if (new RegExp(`\\b${p}\\?:`).test(privBody)) {
privilegeFields.push(p);
}
}
}
const hasProtocolRequestInterface = /interface\s+ProtocolRequest\s*\{/.test(dtsContent);
const hasProtocolResponseInterface = /interface\s+ProtocolResponse\s*\{/.test(dtsContent);
const isMainExported = /namespace\s+Main\s*\{[\s\S]*?const\s+protocol:\s*Protocol;/m.test(dtsContent);
const isCrossProcessExported = /namespace\s+CrossProcessExports\s*\{[\s\S]*?const\s+protocol:\s*Protocol;/m.test(dtsContent);
const isTopLevelExported = /const\s+protocol:\s*Protocol;/m.test(dtsContent);
const isSessionDeclared = /readonly\s+protocol:\s*Protocol;/m.test(dtsContent);
const hasProtocolInterface = protocolMethods.length === expectedMethods.length;
const hasCustomSchemeInterface = customSchemeFields.length === 2;
const hasPrivilegesInterface = privilegeFields.length === expectedPrivileges.length;
const allDeclared =
hasProtocolInterface &&
hasCustomSchemeInterface &&
hasPrivilegesInterface &&
hasProtocolRequestInterface &&
hasProtocolResponseInterface &&
isMainExported &&
isCrossProcessExported &&
isTopLevelExported &&
isSessionDeclared;
return {
hasProtocolInterface,
hasCustomSchemeInterface,
hasPrivilegesInterface,
hasProtocolRequestInterface,
hasProtocolResponseInterface,
protocolMethods,
customSchemeFields,
privilegeFields,
isMainExported,
isCrossProcessExported,
isTopLevelExported,
isSessionDeclared,
allDeclared,
};
}
/**
* Creates an in-memory Protocol manager implementing electron.protocol contract.
* @returns {object}
*/
function createProtocolManager() {
const handledSchemes = new Map();
const privilegedSchemes = new Map();
const legacyProtocols = new Map();
const interceptedProtocols = new Map();
let schemesPrivilegedLocked = false;
return {
/**
* Registers a modern fetch-based protocol handler.
* @param {string} scheme
* @param {(request: Request) => Response | Promise<Response>} handler
*/
handle(scheme, handler) {
if (!isValidSchemeName(scheme)) {
throw new TypeError(`Invalid scheme name: '${scheme}'`);
}
if (typeof handler !== 'function') {
throw new TypeError('Handler must be a function');
}
const lower = scheme.toLowerCase();
if (handledSchemes.has(lower)) {
throw new Error(`Scheme '${scheme}' already registered`);
}
handledSchemes.set(lower, handler);
},
/**
* Unregisters a protocol handler registered with handle.
* @param {string} scheme
*/
unhandle(scheme) {
if (!isValidSchemeName(scheme)) {
throw new TypeError(`Invalid scheme name: '${scheme}'`);
}
const lower = scheme.toLowerCase();
if (!handledSchemes.has(lower)) {
throw new Error(`Scheme '${scheme}' is not currently handled`);
}
handledSchemes.delete(lower);
},
/**
* Checks whether a scheme has an active modern handler.
* @param {string} scheme
* @returns {boolean}
*/
isProtocolHandled(scheme) {
if (!isValidSchemeName(scheme)) {
return false;
}
return handledSchemes.has(scheme.toLowerCase());
},
/**
* Registers custom schemes as privileged before app ready.
* @param {Array<{ scheme: string, privileges?: object }>} customSchemes
*/
registerSchemesAsPrivileged(customSchemes) {
if (schemesPrivilegedLocked) {
throw new Error('registerSchemesAsPrivileged can only be called before app is ready and only once');
}
if (!Array.isArray(customSchemes)) {
throw new TypeError('customSchemes must be an array');
}
for (const item of customSchemes) {
if (!item || typeof item !== 'object') {
throw new TypeError('Each custom scheme configuration must be an object');
}
if (!isValidSchemeName(item.scheme)) {
throw new TypeError(`Invalid scheme name: '${item.scheme}'`);
}
if (item.privileges !== undefined && !validatePrivileges(item.privileges)) {
throw new TypeError(`Invalid privileges provided for scheme '${item.scheme}'`);
}
const lower = item.scheme.toLowerCase();
const defaultPrivileges = {
standard: false,
secure: false,
bypassCSP: false,
allowServiceWorkers: false,
supportFetchAPI: false,
corsEnabled: false,
stream: false,
codeCache: false,
};
privilegedSchemes.set(lower, Object.assign(defaultPrivileges, item.privileges || {}));
}
schemesPrivilegedLocked = true;
},
/**
* Retrieves privileges for a registered scheme.
* @param {string} scheme
* @returns {object | null}
*/
getSchemePrivileges(scheme) {
if (!isValidSchemeName(scheme)) {
return null;
}
const priv = privilegedSchemes.get(scheme.toLowerCase());
return priv ? Object.assign({}, priv) : null;
},
/**
* Registers a Buffer-based protocol handler (legacy).
* @param {string} scheme
* @param {Function} handler
* @returns {boolean}
*/
registerBufferProtocol(scheme, handler) {
if (!isValidSchemeName(scheme) || typeof handler !== 'function') {
return false;
}
const lower = scheme.toLowerCase();
if (legacyProtocols.has(lower)) {
return false;
}
legacyProtocols.set(lower, { type: 'buffer', handler });
return true;
},
/**
* Registers a String-based protocol handler (legacy).
* @param {string} scheme
* @param {Function} handler
* @returns {boolean}
*/
registerStringProtocol(scheme, handler) {
if (!isValidSchemeName(scheme) || typeof handler !== 'function') {
return false;
}
const lower = scheme.toLowerCase();
if (legacyProtocols.has(lower)) {
return false;
}
legacyProtocols.set(lower, { type: 'string', handler });
return true;
},
/**
* Registers a File-based protocol handler (legacy).
* @param {string} scheme
* @param {Function} handler
* @returns {boolean}
*/
registerFileProtocol(scheme, handler) {
if (!isValidSchemeName(scheme) || typeof handler !== 'function') {
return false;
}
const lower = scheme.toLowerCase();
if (legacyProtocols.has(lower)) {
return false;
}
legacyProtocols.set(lower, { type: 'file', handler });
return true;
},
/**
* Registers a Stream-based protocol handler (legacy).
* @param {string} scheme
* @param {Function} handler
* @returns {boolean}
*/
registerStreamProtocol(scheme, handler) {
if (!isValidSchemeName(scheme) || typeof handler !== 'function') {
return false;
}
const lower = scheme.toLowerCase();
if (legacyProtocols.has(lower)) {
return false;
}
legacyProtocols.set(lower, { type: 'stream', handler });
return true;
},
/**
* Unregisters a legacy protocol handler.
* @param {string} scheme
* @returns {boolean}
*/
unregisterProtocol(scheme) {
if (!isValidSchemeName(scheme)) {
return false;
}
const lower = scheme.toLowerCase();
return legacyProtocols.delete(lower);
},
/**
* Checks whether a legacy protocol is registered.
* @param {string} scheme
* @returns {boolean}
*/
isProtocolRegistered(scheme) {
if (!isValidSchemeName(scheme)) {
return false;
}
return legacyProtocols.has(scheme.toLowerCase());
},
/**
* Intercepts a String protocol.
* @param {string} scheme
* @param {Function} handler
* @returns {boolean}
*/
interceptStringProtocol(scheme, handler) {
if (!isValidSchemeName(scheme) || typeof handler !== 'function') {
return false;
}
const lower = scheme.toLowerCase();
if (interceptedProtocols.has(lower)) {
return false;
}
interceptedProtocols.set(lower, { type: 'string', handler });
return true;
},
/**
* Intercepts a Buffer protocol.
* @param {string} scheme
* @param {Function} handler
* @returns {boolean}
*/
interceptBufferProtocol(scheme, handler) {
if (!isValidSchemeName(scheme) || typeof handler !== 'function') {
return false;
}
const lower = scheme.toLowerCase();
if (interceptedProtocols.has(lower)) {
return false;
}
interceptedProtocols.set(lower, { type: 'buffer', handler });
return true;
},
/**
* Intercepts a Stream protocol.
* @param {string} scheme
* @param {Function} handler
* @returns {boolean}
*/
interceptStreamProtocol(scheme, handler) {
if (!isValidSchemeName(scheme) || typeof handler !== 'function') {
return false;
}
const lower = scheme.toLowerCase();
if (interceptedProtocols.has(lower)) {
return false;
}
interceptedProtocols.set(lower, { type: 'stream', handler });
return true;
},
/**
* Removes protocol interception.
* @param {string} scheme
* @returns {boolean}
*/
uninterceptProtocol(scheme) {
if (!isValidSchemeName(scheme)) {
return false;
}
const lower = scheme.toLowerCase();
return interceptedProtocols.delete(lower);
},
/**
* Checks whether protocol is intercepted.
* @param {string} scheme
* @returns {boolean}
*/
isProtocolIntercepted(scheme) {
if (!isValidSchemeName(scheme)) {
return false;
}
return interceptedProtocols.has(scheme.toLowerCase());
},
/**
* Dispatches a request to active handler (interceptors first, then handle or legacy).
* @param {string | Request} requestInput
* @returns {Promise<any>}
*/
async dispatch(requestInput) {
const url = typeof requestInput === 'string' ? requestInput : requestInput.url;
const parsedUrl = new URL(url);
const scheme = parsedUrl.protocol.replace(/:$/, '').toLowerCase();
// Check interceptor first
if (interceptedProtocols.has(scheme)) {
const interceptor = interceptedProtocols.get(scheme);
return new Promise((resolve, reject) => {
try {
const req = {
url,
method: typeof requestInput === 'object' && requestInput.method ? requestInput.method : 'GET',
referrer: '',
headers: {},
};
interceptor.handler(req, (response) => {
resolve({ intercepted: true, type: interceptor.type, response });
});
} catch (err) {
reject(err);
}
});
}
// Check modern handle
if (handledSchemes.has(scheme)) {
const handler = handledSchemes.get(scheme);
const req = typeof requestInput === 'string' ? new Request(requestInput) : requestInput;
const res = await handler(req);
return res;
}
// Check legacy protocol
if (legacyProtocols.has(scheme)) {
const legacy = legacyProtocols.get(scheme);
return new Promise((resolve, reject) => {
try {
const req = {
url,
method: typeof requestInput === 'object' && requestInput.method ? requestInput.method : 'GET',
referrer: '',
headers: {},
};
legacy.handler(req, (response) => {
resolve({ legacy: true, type: legacy.type, response });
});
} catch (err) {
reject(err);
}
});
}
throw new Error(`No handler registered for scheme: ${scheme}`);
},
};
}
module.exports = {
isValidSchemeName,
validatePrivileges,
getProtocolDeclarations,
createProtocolManager,
};
{
"name": "sample-electron-protocol",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sample-electron-protocol",
"version": "1.0.0",
"license": "MIT-0",
"dependencies": {
"electron": "38.8.6"
},
"description": "verify electron.protocol in pkg:npm/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"
}
}
}
}
{
"name": "sample-electron-protocol",
"version": "1.0.0",
"description": "verify electron.protocol in pkg:npm/electron@38.8.6",
"main": "index.js",
"license": "MIT-0",
"dependencies": {
"electron": "38.8.6"
}
}
{
"schemaVersion": 1,
"goal": "verify electron.protocol in pkg:npm/electron@38.8.6",
"kind": "HOW",
"packages": [
"pkg:npm/electron@38.8.6"
],
"symbols": [
"electron.protocol"
]
}
'use strict';
const assert = require('node:assert');
const { Readable } = require('node:stream');
const {
isValidSchemeName,
validatePrivileges,
getProtocolDeclarations,
createProtocolManager,
} = require('../index.js');
async function runContracts() {
// Contract 1: electron type definitions declare Protocol interface, CustomScheme, Privileges, ProtocolRequest, and ProtocolResponse interfaces across Main and CrossProcessExports namespaces
{
const decl = getProtocolDeclarations();
assert.strictEqual(decl.allDeclared, true, 'All protocol declarations must be present');
assert.strictEqual(decl.hasProtocolInterface, true, 'Protocol interface must be declared');
assert.strictEqual(decl.hasCustomSchemeInterface, true, 'CustomScheme interface must be declared');
assert.strictEqual(decl.hasPrivilegesInterface, true, 'Privileges interface must be declared');
assert.strictEqual(decl.hasProtocolRequestInterface, true, 'ProtocolRequest interface must be declared');
assert.strictEqual(decl.hasProtocolResponseInterface, true, 'ProtocolResponse interface must be declared');
assert.strictEqual(decl.isMainExported, true, 'protocol must be exported in Main namespace');
assert.strictEqual(decl.isCrossProcessExported, true, 'protocol must be exported in CrossProcessExports namespace');
assert.strictEqual(decl.isTopLevelExported, true, 'protocol must be exported at top-level');
assert.strictEqual(decl.isSessionDeclared, true, 'protocol must be declared on Session interface');
assert.ok(decl.protocolMethods.includes('handle'), 'Protocol must have handle method');
assert.ok(decl.protocolMethods.includes('unhandle'), 'Protocol must have unhandle method');
assert.ok(decl.protocolMethods.includes('isProtocolHandled'), 'Protocol must have isProtocolHandled method');
assert.ok(decl.protocolMethods.includes('registerSchemesAsPrivileged'), 'Protocol must have registerSchemesAsPrivileged method');
assert.ok(decl.protocolMethods.includes('registerBufferProtocol'), 'Protocol must have registerBufferProtocol method');
assert.ok(decl.protocolMethods.includes('registerFileProtocol'), 'Protocol must have registerFileProtocol method');
assert.ok(decl.protocolMethods.includes('registerHttpProtocol'), 'Protocol must have registerHttpProtocol method');
assert.ok(decl.protocolMethods.includes('registerStreamProtocol'), 'Protocol must have registerStreamProtocol method');
assert.ok(decl.protocolMethods.includes('registerStringProtocol'), 'Protocol must have registerStringProtocol method');
assert.ok(decl.protocolMethods.includes('unregisterProtocol'), 'Protocol must have unregisterProtocol method');
assert.ok(decl.protocolMethods.includes('isProtocolRegistered'), 'Protocol must have isProtocolRegistered method');
assert.ok(decl.protocolMethods.includes('interceptBufferProtocol'), 'Protocol must have interceptBufferProtocol method');
assert.ok(decl.protocolMethods.includes('interceptFileProtocol'), 'Protocol must have interceptFileProtocol method');
assert.ok(decl.protocolMethods.includes('interceptHttpProtocol'), 'Protocol must have interceptHttpProtocol method');
assert.ok(decl.protocolMethods.includes('interceptStreamProtocol'), 'Protocol must have interceptStreamProtocol method');
assert.ok(decl.protocolMethods.includes('interceptStringProtocol'), 'Protocol must have interceptStringProtocol method');
assert.ok(decl.protocolMethods.includes('uninterceptProtocol'), 'Protocol must have uninterceptProtocol method');
assert.ok(decl.protocolMethods.includes('isProtocolIntercepted'), 'Protocol must have isProtocolIntercepted method');
assert.deepStrictEqual(decl.customSchemeFields, ['scheme', 'privileges']);
assert.ok(decl.privilegeFields.includes('standard'));
assert.ok(decl.privilegeFields.includes('secure'));
assert.ok(decl.privilegeFields.includes('bypassCSP'));
assert.ok(decl.privilegeFields.includes('allowServiceWorkers'));
assert.ok(decl.privilegeFields.includes('supportFetchAPI'));
assert.ok(decl.privilegeFields.includes('corsEnabled'));
assert.ok(decl.privilegeFields.includes('stream'));
assert.ok(decl.privilegeFields.includes('codeCache'));
}
// Contract 2: protocol.handle and protocol.unhandle register and remove fetch-based request handlers with status query via isProtocolHandled
{
const protocol = createProtocolManager();
assert.strictEqual(protocol.isProtocolHandled('app-assets'), false, 'Scheme should initially be unhandled');
protocol.handle('app-assets', async (req) => {
const url = new URL(req.url);
return new Response(`Loaded: ${url.pathname}`, {
status: 200,
headers: { 'content-type': 'text/plain' },
});
});
assert.strictEqual(protocol.isProtocolHandled('app-assets'), true, 'Scheme should be marked as handled');
assert.strictEqual(protocol.isProtocolHandled('APP-ASSETS'), true, 'Scheme check should be case-insensitive');
// Dispatching request
const response = await protocol.dispatch('app-assets://localhost/index.html');
assert.strictEqual(response.status, 200);
assert.strictEqual(response.headers.get('content-type'), 'text/plain');
const text = await response.text();
assert.strictEqual(text, 'Loaded: /index.html');
// Duplicate registration throws
assert.throws(() => {
protocol.handle('app-assets', () => new Response('dup'));
}, /already registered/);
// Unhandle scheme
protocol.unhandle('app-assets');
assert.strictEqual(protocol.isProtocolHandled('app-assets'), false);
// Unhandling unregistered scheme throws
assert.throws(() => {
protocol.unhandle('app-assets');
}, /not currently handled/);
// Invalid scheme arguments
assert.throws(() => protocol.handle('', () => {}), TypeError);
assert.throws(() => protocol.handle('123invalid', () => {}), TypeError);
assert.throws(() => protocol.handle('valid-scheme', 'not-a-fn'), TypeError);
}
// Contract 3: protocol.registerSchemesAsPrivileged validates and registers custom schemes with standard, secure, CSP bypass, and fetch API privileges
{
const protocol = createProtocolManager();
protocol.registerSchemesAsPrivileged([
{
scheme: 'secure-app',
privileges: {
standard: true,
secure: true,
supportFetchAPI: true,
corsEnabled: true,
bypassCSP: true,
stream: true,
},
},
{
scheme: 'worker-app',
privileges: {
allowServiceWorkers: true,
codeCache: true,
},
},
]);
const securePriv = protocol.getSchemePrivileges('secure-app');
assert.strictEqual(securePriv.standard, true);
assert.strictEqual(securePriv.secure, true);
assert.strictEqual(securePriv.supportFetchAPI, true);
assert.strictEqual(securePriv.corsEnabled, true);
assert.strictEqual(securePriv.bypassCSP, true);
assert.strictEqual(securePriv.stream, true);
assert.strictEqual(securePriv.allowServiceWorkers, false);
const workerPriv = protocol.getSchemePrivileges('worker-app');
assert.strictEqual(workerPriv.allowServiceWorkers, true);
assert.strictEqual(workerPriv.codeCache, true);
assert.strictEqual(workerPriv.standard, false);
assert.strictEqual(protocol.getSchemePrivileges('unknown-scheme'), null);
// Calling again throws because it may only be called once before ready
assert.throws(() => {
protocol.registerSchemesAsPrivileged([{ scheme: 'another-app' }]);
}, /can only be called before app is ready and only once/);
// Fresh instance validation tests
const freshProtocol = createProtocolManager();
assert.throws(() => freshProtocol.registerSchemesAsPrivileged('invalid-arg'), TypeError);
assert.throws(() => freshProtocol.registerSchemesAsPrivileged([null]), TypeError);
assert.throws(() => freshProtocol.registerSchemesAsPrivileged([{ scheme: 'bad scheme!' }]), TypeError);
assert.throws(() => freshProtocol.registerSchemesAsPrivileged([{ scheme: 'valid', privileges: { nonExistentKey: true } }]), TypeError);
}
// Contract 4: protocol legacy handlers registerBufferProtocol, registerStringProtocol, and registerFileProtocol respond to incoming requests with callbacks
{
const protocol = createProtocolManager();
const sampleBuffer = Buffer.from('hello-buffer-protocol');
const bufRegistered = protocol.registerBufferProtocol('app-buf', (request, callback) => {
assert.strictEqual(request.url, 'app-buf://localhost/data');
callback({
mimeType: 'application/octet-stream',
data: sampleBuffer,
});
});
assert.strictEqual(bufRegistered, true);
assert.strictEqual(protocol.isProtocolRegistered('app-buf'), true);
// Duplicate registration returns false
assert.strictEqual(protocol.registerBufferProtocol('app-buf', () => {}), false);
const bufResult = await protocol.dispatch('app-buf://localhost/data');
assert.strictEqual(bufResult.legacy, true);
assert.strictEqual(bufResult.type, 'buffer');
assert.deepStrictEqual(bufResult.response.data, sampleBuffer);
// String protocol
const strRegistered = protocol.registerStringProtocol('app-str', (request, callback) => {
callback({
mimeType: 'text/html',
data: '<h1>Hello String Protocol</h1>',
});
});
assert.strictEqual(strRegistered, true);
assert.strictEqual(protocol.isProtocolRegistered('app-str'), true);
const strResult = await protocol.dispatch('app-str://localhost/page');
assert.strictEqual(strResult.legacy, true);
assert.strictEqual(strResult.type, 'string');
assert.strictEqual(strResult.response.data, '<h1>Hello String Protocol</h1>');
// File protocol
const fileRegistered = protocol.registerFileProtocol('app-file', (request, callback) => {
callback({ path: '/mock/path/file.txt' });
});
assert.strictEqual(fileRegistered, true);
assert.strictEqual(protocol.isProtocolRegistered('app-file'), true);
const fileResult = await protocol.dispatch('app-file://localhost/file.txt');
assert.strictEqual(fileResult.legacy, true);
assert.strictEqual(fileResult.type, 'file');
assert.strictEqual(fileResult.response.path, '/mock/path/file.txt');
// Unregister protocols
assert.strictEqual(protocol.unregisterProtocol('app-buf'), true);
assert.strictEqual(protocol.isProtocolRegistered('app-buf'), false);
assert.strictEqual(protocol.unregisterProtocol('app-buf'), false);
}
// Contract 5: protocol request interceptors interceptBufferProtocol, interceptStringProtocol, and uninterceptProtocol override and restore protocol handling
{
const protocol = createProtocolManager();
assert.strictEqual(protocol.isProtocolIntercepted('http'), false);
const intercepted = protocol.interceptStringProtocol('http', (request, callback) => {
assert.strictEqual(request.url, 'http://example.com/api/test');
callback({
mimeType: 'application/json',
data: '{"intercepted":true}',
});
});
assert.strictEqual(intercepted, true);
assert.strictEqual(protocol.isProtocolIntercepted('http'), true);
// Duplicate interception returns false
assert.strictEqual(protocol.interceptStringProtocol('http', () => {}), false);
const dispatchResult = await protocol.dispatch('http://example.com/api/test');
assert.strictEqual(dispatchResult.intercepted, true);
assert.strictEqual(dispatchResult.type, 'string');
assert.strictEqual(dispatchResult.response.data, '{"intercepted":true}');
// Unintercept
assert.strictEqual(protocol.uninterceptProtocol('http'), true);
assert.strictEqual(protocol.isProtocolIntercepted('http'), false);
assert.strictEqual(protocol.uninterceptProtocol('http'), false);
// Buffer interceptor
const bufPayload = Buffer.from('intercepted-bytes');
protocol.interceptBufferProtocol('custom-proto', (req, cb) => {
cb({ data: bufPayload });
});
assert.strictEqual(protocol.isProtocolIntercepted('custom-proto'), true);
const bufInterceptResult = await protocol.dispatch('custom-proto://localhost/res');
assert.strictEqual(bufInterceptResult.intercepted, true);
assert.deepStrictEqual(bufInterceptResult.response.data, bufPayload);
protocol.uninterceptProtocol('custom-proto');
assert.strictEqual(protocol.isProtocolIntercepted('custom-proto'), false);
}
// Contract 6: protocol normalizes request URLs, validates request metadata, and supports stream response handlers
{
const protocol = createProtocolManager();
// Stream protocol
const streamRegistered = protocol.registerStreamProtocol('app-stream', (request, callback) => {
const readable = Readable.from([Buffer.from('chunk-1;'), Buffer.from('chunk-2')]);
callback({
statusCode: 200,
headers: { 'content-type': 'application/octet-stream' },
data: readable,
});
});
assert.strictEqual(streamRegistered, true);
assert.strictEqual(protocol.isProtocolRegistered('app-stream'), true);
const streamResult = await protocol.dispatch('app-stream://localhost/feed');
assert.strictEqual(streamResult.legacy, true);
assert.strictEqual(streamResult.type, 'stream');
assert.strictEqual(streamResult.response.statusCode, 200);
const chunks = [];
for await (const chunk of streamResult.response.data) {
chunks.push(chunk);
}
assert.strictEqual(Buffer.concat(chunks).toString('utf-8'), 'chunk-1;chunk-2');
// Scheme name validation tests
assert.strictEqual(isValidSchemeName('https'), true);
assert.strictEqual(isValidSchemeName('my-custom-scheme'), true);
assert.strictEqual(isValidSchemeName('scheme.v1+test'), true);
assert.strictEqual(isValidSchemeName('123scheme'), false);
assert.strictEqual(isValidSchemeName('scheme:'), false);
assert.strictEqual(isValidSchemeName('scheme/path'), false);
assert.strictEqual(isValidSchemeName(''), false);
assert.strictEqual(isValidSchemeName(null), false);
// Privilege validator tests
assert.strictEqual(validatePrivileges({ standard: true, secure: false }), true);
assert.strictEqual(validatePrivileges({ bypassCSP: true, supportFetchAPI: true }), true);
assert.strictEqual(validatePrivileges({ unknownField: true }), false);
assert.strictEqual(validatePrivileges({ standard: 'true' }), false);
assert.strictEqual(validatePrivileges(null), false);
assert.strictEqual(validatePrivileges([]), false);
}
console.log('All 6 electron.protocol contracts passed successfully.');
}
runContracts().catch((err) => {
console.error('Contract test failed:', err);
process.exit(1);
});
Исходный сидер
аноним