| #!/usr/bin/env node | |
| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| const fs = require('node:fs'); | |
| const safeCodePattern = /^[A-Z0-9_]{1,64}$/; | |
| function findErrorCode(error) { | |
| const seen = new Set(); | |
| let current = error; | |
| while (current && typeof current === 'object' && !seen.has(current)) { | |
| seen.add(current); | |
| for (const value of [current.code, current.errno]) { | |
| if (typeof value === 'string' && safeCodePattern.test(value)) { | |
| return value; | |
| } | |
| } | |
| current = current.cause; | |
| } | |
| return undefined; | |
| } | |
| function contextAwareFetch(fetchImplementation, emit) { | |
| return async (...args) => { | |
| try { | |
| const response = await Reflect.apply(fetchImplementation, globalThis, args); | |
| if (!response.ok) { | |
| emit({ kind: 'http', status: response.status }); | |
| } | |
| return response; | |
| } catch (error) { | |
| const code = findErrorCode(error); | |
| emit(code ? { kind: 'fetch', code } : { kind: 'fetch' }); | |
| throw error; | |
| } | |
| }; | |
| } | |
| function writeContext(context) { | |
| try { | |
| fs.writeSync(3, `${JSON.stringify(context)}\n`); | |
| } catch { | |
| // Context is best-effort. The installer must retain its original failure when fd 3 is unavailable. | |
| } | |
| } | |
| if (require.main === module) { | |
| const installer = process.argv[2]; | |
| if (!installer) { | |
| throw new Error('Electron installer path is required'); | |
| } | |
| if (typeof globalThis.fetch !== 'function') { | |
| throw new Error('This Electron installer requires the built-in Fetch API'); | |
| } | |
| globalThis.fetch = contextAwareFetch(globalThis.fetch, writeContext); | |
| require(installer); | |
| } | |
| module.exports = { contextAwareFetch, findErrorCode }; |