8000 fix: polyfills for Node.js compatibility by jcbsfilho · Pull Request #170 · aziontech/lib · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

fix: polyfills for Node.js compatibility #170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/bundler/src/polyfills/promises/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
*/
import promisesContext from './context/index.js';

export default { promisesContext };
export default promisesContext;
2 changes: 2 additions & 0 deletions packages/unenv-preset/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export default {
module: `${polyfillsPath}/node/module.js`,
string_decoder: 'string_decoder/lib/string_decoder.js',
timers: 'timers-browserify/',
util: `${polyfillsPath}/node/util.js`,
'util/types': `${polyfillsPath}/node/internal/util/types.js`,
},
external: ['node:async_hooks', 'node:fs/promises', 'node:stream', 'node:crypto'],
polyfill: [
Expand Down
122 changes: 87 additions & 35 deletions packages/unenv-preset/src/polyfills/node/https.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable */
// https://nodejs.org/api/https.html
import { EventEmitter } from 'node:events';

Expand Down Expand Up @@ -41,47 +42,54 @@ export const get = notImplemented('https.get');

export const createServer = notImplemented('https.createServer');

export const request = (options, callback) => {
let fullUrl;
let headers = {};
let method;
export const request = (urlOrOptions, optionsOrCallback, maybeCallback) => {
let url, options, callback;
if (typeof urlOrOptions === 'string' || urlOrOptions instanceof URL) {
url = urlOrOptions;
if (typeof optionsOrCallback === 'object' && optionsOrCallback !== null) {
options = optionsOrCallback;
callback = maybeCallback;
} else {
options = {};
callback = optionsOrCallback;
}
} else {
url = undefined;
options = urlOrOptions || {};
callback = optionsOrCallback;
}

if (typeof options === 'object') {
let fullUrl, headers, method, body;
if (url) {
fullUrl = url.toString();
headers = { ...(options.headers || {}) };
method = (options.method || 'GET').toUpperCase();
body = options.body;
} else {
const protocol = options.protocol || 'https:';
const hostname = options.hostname || 'localhost';
const port = options.port ? `:${options.port}` : '';
const path = options.path || '/';
method = options.method || 'GET';

// Adiciona os query params, se existirem
body = options.body;
const queryParams = options.query ? new URLSearchParams(options.query).toString() : '';
const queryString = queryParams ? `?${queryParams}` : '';

fullUrl = `${protocol}//${hostname}${port}${path}${queryString}`;
headers = { ...options.headers };
} else {
fullUrl = options;
headers = options.headers || {};
headers = { ...(options.headers || {}) };
method = (options.method || 'GET').toUpperCase();
}

const { body } = options;

// Verifica o tipo de conteúdo e formata o corpo adequadamente
let formattedBody;
if (headers['Content-Type'] === 'application/json') {
formattedBody = body ? JSON.stringify(body) : undefined;
if (!options.method && body) method = 'POST';
} else if (headers['Content-Type'] === 'application/x-www-form-urlencoded') {
formattedBody = body ? new URLSearchParams(body).toString() : undefined;
if (!options.method && body) method = 'POST';
} else {
formattedBody = body; // Envia o corpo como está para outros tipos de conteúdo
formattedBody = body;
if (!options.method && body) method = 'POST';
}

const fetchOptions = {
method,
headers,
body: JSON.stringify(formattedBody) || JSON.stringify({}),
};

const controller = new AbortController();
const signal = controller.signal;

Expand All @@ -90,7 +98,7 @@ export const request = (options, callback) => {
timeoutId = setTimeout(() => {
controller.abort();
reqEvents.emit('error', new Error('Request timed out'));
}, 5000);
}, options.timeout);
}

const reqEvents = new EventEmitter();
Expand All @@ -99,9 +107,21 @@ export const request = (options, callback) => {

const req = {
_bodyBuffer: [],
removeHeader: (name) => {
if (headers && typeof name === 'string') {
delete headers[name];
delete headers[name.toLowerCase()];
}
},
setHeader: (name, value) => {
if (headers && typeof name === 'string') {
headers[name] = value;
headers[name.toLowerCase()] = value;
}
},
write: (chunk) => {
if (typeof chunk === 'string' || chunk instanceof Buffer) {
req._bodyBuffer.push(chunk); // Adiciona o chunk ao buffer
req._bodyBuffer.push(chunk);
} else {
throw new Error('Invalid chunk type. Expected string or Buffer.');
}
Expand All @@ -125,37 +145,69 @@ export const request = (options, callback) => {
}
endCalled = true;

// Concatena os chunks do buffer para formar o corpo completo
const body = req._bodyBuffer.length > 0 ? req._bodyBuffer.join('') : undefined;
let bodyFinal;
if (req._bodyBuffer.length > 0) {
if (Buffer.isBuffer(req._bodyBuffer[0])) {
bodyFinal = Buffer.concat(req._bodyBuffer).toString();
} else {
bodyFinal = req._bodyBuffer.join('');
}
} else {
bodyFinal = formattedBody;
}

const fetchOptions = {
method,
headers,
signal,
};

if (method !== 'GET' && method !== 'HEAD' && bodyFinal !== undefined) {
fetchOptions.body = bodyFinal;
}

fetch(fullUrl, { ...fetchOptions, body, signal })
fetch(fullUrl, fetchOptions)
.then(async (response) => {
clearTimeout(timeoutId);

const res = new EventEmitter();
res.statusCode = response.status;
res.headers = Object.fromEntries(response.headers.entries());

const chunks = [];
res[Symbol.asyncIterator] = async function* () {
let ended = false;
this.on('end', () => {
ended = true;
});
let idx = 0;
while (!ended || idx < chunks.length) {
if (idx < chunks.length) {
yield chunks[idx++];
} else {
await new Promise((resolve) => this.once('data', resolve));
}
}
};

const reader = response.body?.getReader();

const processStream = async () => {
try {
if (!reader) {
console.log('No reader available');
res.emit('end'); // Emite 'end' se não houver corpo na resposta
res.emit('end');
return;
}
// eslint-disable-next-line no-constant-condition
while (true) {
const { done, value } = await reader.read();
if (done) {
res.emit('end');
break;
}
res.emit('data', Buffer.from(value));
const buf = Buffer.from(value);
chunks.push(buf);
res.emit('data', buf);
}
} catch (err) {
console.error('Error processing stream:', err);
res.emit('error', err);
}
};
Expand All @@ -172,7 +224,7 @@ export const request = (options, callback) => {

reqEvents.emit('response', res);

if (callback) {
if (typeof callback === 'function') {
callback(res);
}
})
Expand Down
11 changes: 11 additions & 0 deletions packages/unenv-preset/src/polyfills/node/internal/_internal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable */
export function notImplemented(name) {
const fn = () => {
throw createNotImplementedError(name);
};
return Object.assign(fn, { __unenv__: true });
}

export function createNotImplementedError(name) {
return new Error(`[unenv] ${name} is not implemented yet!`);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-disable */
export function inherits(ctor, superCtor) {
if (!superCtor) {
return;
}
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true,
},
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* eslint-disable */
export const isRegExp = (val) => val instanceof RegExp;

export const isDate = (val) => val instanceof Date;

export const isArray = (val) => Array.isArray(val);

export const isBoolean = (val) => typeof val === 'boolean';

export const isNull = (val) => val === null;

export const isNullOrUndefined = (val) => val === null || val === undefined;

export const isNumber = (val) => typeof val === 'number';

export const isString = (val) => typeof val === 'string';

export const isSymbol = (val) => typeof val === 'symbol';

export const isUndefined = (val) => val === undefined;

// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
export const isFunction = (val) => typeof val === 'function';

export const isBuffer = (val) => {
return (
val &&
typeof val === 'object' &&
typeof val.copy === 'function' &&
typeof val.fill === 'function' &&
typeof val.readUInt8 === 'function'
);
};

export const isDeepStrictEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);

export const isObject = (val) =>
val !== null &&
typeof val === 'object' &&
// eslint-disable-next-line no-prototype-builtins
Object.getPrototypeOf(val).isPrototypeOf(Object);

export const isError = (val) => val instanceof Error;

// Source https://github.com/jonschlinkert/is-primitive/blob/b22c524da5cbac075f14145780ec4b3637afd7dc/index.js
export const isPrimitive = (val) => {
if (typeof val === 'object') {
return val === null;
}
return typeof val !== 'function';
};
64 changes: 64 additions & 0 deletions packages/unenv-preset/src/polyfills/node/internal/util/log.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* eslint-disable */
export const log = (...args) => {
console.log(...args);
};

export const debuglog = (section, _cb) => {
const fn = (msg, ...params) => {
if (fn.enabled) {
console.debug(`[${section}] ${msg}`, ...params);
}
};
fn.enabled = true;
return fn;
};

export const debug = debuglog;

export const inspect = (object) => JSON.stringify(object, null, 2);

export const format = (...args) => _format(...args);

export const formatWithOptions = (_options, ...args) => _format(...args);

// Source: https://github.com/tmpfs/format-util/blob/0c989942c959b179eec294a4e725afd63e743f18/format.js
function _format(fmt, ...args) {
const re = /(%?)(%([djos]))/g;
if (args.length > 0) {
fmt = fmt.replace(re, (match, escaped, ptn, flag) => {
let arg = args.shift();
switch (flag) {
case 'o':
if (Array.isArray(arg)) {
arg = JSON.stringify(arg);
break;
}
break;
case 's':
arg = '' + arg;
break;
case 'd':
arg = Number(arg);
break;
case 'j':
arg = JSON.stringify(arg);
break;
}
if (!escaped) {
return arg;
}
args.unshift(arg);
return match;
});
}

// arguments remain after formatting
if (args.length > 0) {
fmt += ' ' + args.join(' ');
}

// update escaped %% values
fmt = fmt.replace(/%{2}/g, '%');

return '' + fmt;
}
Loading
0