sistema creado v0.5.0
Some checks failed
build-and-push / deploy (push) Has been skipped
build-and-push / build (push) Failing after 6s

This commit is contained in:
2025-05-14 16:10:41 -06:00
parent b5e40cf4ac
commit 745168cf51
193 changed files with 7267 additions and 8789 deletions

View File

@@ -0,0 +1,73 @@
name: build-and-push
on:
push:
branches: [ main ]
jobs:
build:
runs-on: docker
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to registry
uses: docker/login-action@v2
with:
registry: gitea.interno.com
username: nucleo000
password: 7bc7b2fcd283bd6a251bef3ede368b7f897c919d
- name: Build root image
run: |
docker build -f ./Dockerfile \
-t gitea.interno.com/nucleo000/planilla:${{ github.sha }} \
-t gitea.interno.com/nucleo000/planilla:latest .
- name: Build API image
run: |
docker build -f ./api/Dockerfile \
-t gitea.interno.com/nucleo000/planilla-api:${{ github.sha }} \
-t gitea.interno.com/nucleo000/planilla-api:latest \
./api
- name: Build UI image
run: |
docker build -f ./ui/Dockerfile \
-t gitea.interno.com/nucleo000/planilla-ui:${{ github.sha }} \
-t gitea.interno.com/nucleo000/planilla-ui:latest \
./ui
- name: Push images
run: |
for img in planilla planilla-api planilla-ui; do
docker push gitea.interno.com/nucleo000/$img:${{ github.sha }}
docker push gitea.interno.com/nucleo000/$img:latest
done
deploy:
runs-on: docker
needs: build
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Login to registry
run: |
echo "Logging into registry…"
docker login gitea.interno.com -u nucleo000 -p 7bc7b2fcd283bd6a251bef3ede368b7f897c919d
- name: Pull latest images
run: |
docker pull gitea.interno.com/nucleo000/planilla:latest
docker pull gitea.interno.com/nucleo000/planilla-api:latest
docker pull gitea.interno.com/nucleo000/planilla-ui:latest
- name: Teardown previous stack
run: docker compose down --remove-orphans || true
- name: Start all services fresh
run: docker compose up -d --build --force-recreate --remove-orphans

View File

@@ -8,25 +8,31 @@ jobs:
build:
runs-on: docker
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- uses: docker/setup-buildx-action@v2
- name: Login to registry
uses: docker/login-action@v2
with:
registry: gitea.interno.com
username: nucleo000
password: 7bc7b2fcd283bd6a251bef3ede368b7f897c919d
username: nucleo000 # ⬅ ponelo en secrets
password: 7bc7b2fcd283bd6a251bef3ede368b7f897c919d # ⬅ idem
# ──────────────── builds ────────────────
- name: Build root image
run: |
docker build -f ./Dockerfile \
-t gitea.interno.com/nucleo000/planilla:${{ github.sha }} \
-t gitea.interno.com/nucleo000/planilla:latest .
- name: Build AGENT image
run: |
docker build -f ./agent/Dockerfile \
-t gitea.interno.com/nucleo000/planilla-agent:${{ github.sha }} \
-t gitea.interno.com/nucleo000/planilla-agent:latest \
./agent
- name: Build API image
run: |
docker build -f ./api/Dockerfile \
@@ -34,6 +40,13 @@ jobs:
-t gitea.interno.com/nucleo000/planilla-api:latest \
./api
- name: Build MCP image
run: |
docker build -f ./mcp/Dockerfile \
-t gitea.interno.com/nucleo000/planilla-mcp:${{ github.sha }} \
-t gitea.interno.com/nucleo000/planilla-mcp:latest \
./mcp
- name: Build UI image
run: |
docker build -f ./ui/Dockerfile \
@@ -41,9 +54,22 @@ jobs:
-t gitea.interno.com/nucleo000/planilla-ui:latest \
./ui
- name: Build WORKER image
run: |
docker build -f ./worker/Dockerfile \
-t gitea.interno.com/nucleo000/planilla-worker:${{ github.sha }} \
-t gitea.interno.com/nucleo000/planilla-worker:latest \
./worker
# ──────────────── push loop ────────────────
- name: Push images
run: |
for img in planilla planilla-api planilla-ui; do
for img in planilla \
planilla-agent \
planilla-api \
planilla-mcp \
planilla-ui \
planilla-worker; do
docker push gitea.interno.com/nucleo000/$img:${{ github.sha }}
docker push gitea.interno.com/nucleo000/$img:latest
done
@@ -52,19 +78,21 @@ jobs:
runs-on: docker
needs: build
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: actions/checkout@v3
- name: Login to registry
run: |
echo "Logging into registry…"
docker login gitea.interno.com -u nucleo000 -p 7bc7b2fcd283bd6a251bef3ede368b7f897c919d
run: docker login gitea.interno.com -u nucleo000 -p 7bc7b2fcd283bd6a251bef3ede368b7f897c919d
- name: Pull latest images
run: |
docker pull gitea.interno.com/nucleo000/planilla:latest
docker pull gitea.interno.com/nucleo000/planilla-api:latest
docker pull gitea.interno.com/nucleo000/planilla-ui:latest
for img in planilla \
planilla-agent \
planilla-api \
planilla-mcp \
planilla-ui \
planilla-worker; do
docker pull gitea.interno.com/nucleo000/$img:latest
done
- name: Teardown previous stack
run: docker compose down --remove-orphans || true

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules

View File

@@ -1,19 +0,0 @@
# Dockerfile actualizado
FROM node:23-slim
WORKDIR /app
# 1) Copiás sólo package.json (y package-lock.json si existe) para aprovechar cache
COPY package.json package-lock.json* ./
RUN npm install --omit=dev
# 2) Copiás el resto del código (todos los .js y módulos separados)
COPY . .
# 3) Variables y puerto
ENV PORT=3000
EXPOSE 3000
# 4) Arranque
CMD ["node", "index.js"]

13
Makefile Normal file
View File

@@ -0,0 +1,13 @@
estructura:
powershell -ExecutionPolicy Bypass -File ./dev/scripts/estructura.ps1
build:
docker compose build
up:
docker compose up -d
down:
docker compose down
todo: estructura build

28
agent/Dockerfile Normal file
View File

@@ -0,0 +1,28 @@
# Dockerfile con acceso a docker CLI
FROM node:23-slim
WORKDIR /app
# 1) Instalar dependencias normales
COPY package.json package-lock.json* ./
RUN npm install --omit=dev
# 2) Instalar Docker CLI
RUN apt-get update && \
apt-get install -y ca-certificates curl gnupg && \
install -m 0755 -d /etc/apt/keyrings && \
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \
chmod a+r /etc/apt/keyrings/docker.gpg && \
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable" \
> /etc/apt/sources.list.d/docker.list && \
apt-get update && \
apt-get install -y docker-ce-cli && \
rm -rf /var/lib/apt/lists/*
# 3) Copiar el código fuente
COPY . .
# 4) Puerto y arranque
ENV PORT=4000
EXPOSE 4000
CMD ["node", "index.js"]

16
agent/config.js Normal file
View File

@@ -0,0 +1,16 @@
/*───────────────────────────────────────────────────────────────*/
/* ⚙️ Variables de entorno */
/*───────────────────────────────────────────────────────────────*/
export const config = {
VERSION : '0.6.12',
API_URL : process.env.BOT_API_URL ?? 'http://whatsapp-bot:8002',
GROUP_ID : process.env.GROUP_ID ?? '120363203056794284@g.us',
REPLY_MSG : process.env.REPLY_MSG ?? 'que pedos',
PORT : +(process.env.PORT ?? 4000),
LOG_LEVEL : process.env.LOG_LEVEL ?? 'debug',
RETRY_MS : +(process.env.RETRY_MS ?? 5_000),
MAX_ATTEMPTS : +(process.env.MAX_ATTEMPTS ?? 60),
GEMINI_KEY : process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY ?? '',
GEMINI_MODEL_ID: process.env.GEMINI_MODEL_ID ?? 'gemini-2.5-flash-preview-04-17',
};

View File

@@ -0,0 +1,195 @@
{
"id": "true_50496210031@c.us_3F4F5E90B644956AC938",
"viewed": false,
"body": "amor",
"type": "chat",
"t": 1745718602,
"notifyName": "📀🧮📡🌐",
"from": "50498554225@c.us",
"to": "50496210031@c.us",
"author": "50498554225:77@c.us",
"invis": false,
"isNewMsg": true,
"star": false,
"kicNotified": false,
"recvFresh": true,
"isFromTemplate": false,
"thumbnail": "",
"pollInvalidated": false,
"isSentCagPollCreation": false,
"latestEditMsgKey": null,
"latestEditSenderTimestampMs": null,
"mentionedJidList": [],
"groupMentions": [],
"isEventCanceled": false,
"eventInvalidated": false,
"isVcardOverMmsDocument": false,
"labels": [],
"hasReaction": false,
"ephemeralDuration": 0,
"ephemeralSettingTimestamp": 0,
"disappearingModeInitiator": "chat",
"disappearingModeTrigger": "chat_settings",
"viewMode": "VISIBLE",
"productHeaderImageRejected": false,
"lastPlaybackProgress": 0,
"isDynamicReplyButtonsMsg": false,
"isCarouselCard": false,
"parentMsgId": null,
"callSilenceReason": null,
"isVideoCall": false,
"callDuration": null,
"callParticipants": null,
"isMdHistoryMsg": false,
"stickerSentTs": 0,
"isAvatar": false,
"lastUpdateFromServerTs": 0,
"invokedBotWid": null,
"bizBotType": null,
"botResponseTargetId": null,
"botPluginType": null,
"botPluginReferenceIndex": null,
"botPluginSearchProvider": null,
"botPluginSearchUrl": null,
"botPluginSearchQuery": null,
"botPluginMaybeParent": false,
"botReelPluginThumbnailCdnUrl": null,
"botMessageDisclaimerText": null,
"botMsgBodyType": null,
"reportingTokenInfo": null,
"requiresDirectConnection": false,
"bizContentPlaceholderType": null,
"hostedBizEncStateMismatch": false,
"senderOrRecipientAccountTypeHosted": false,
"placeholderCreatedWhenAccountIsHosted": false,
"device": 77,
"local": false,
"fromMe": true,
"mId": "3F4F5E90B644956AC938",
"sender": {
"id": "50498554225@c.us",
"name": "📀🧮📡🌐jose",
"shortName": "📀🧮📡🌐jose",
"pushname": "📀🧮📡🌐",
"type": "in",
"isBusiness": false,
"isEnterprise": false,
"isSmb": false,
"isContactSyncCompleted": 1,
"disappearingModeDuration": 0,
"disappearingModeSettingTimestamp": 1700599275,
"textStatusLastUpdateTime": -1,
"syncToAddressbook": true,
"formattedName": "Tú",
"isMe": true,
"isMyContact": true,
"isPSA": false,
"isUser": true,
"status": "Can't talk, WhatsApp only",
"isVerified": false,
"isWAContact": true,
"profilePicThumbObj": {
"eurl": "https://pps.whatsapp.net/v/t61.24694-24/471428085_1635189164083925_3546014480456031647_n.jpg?ccb=11-4&oh=01_Q5Aa1QEqY8vxL1FGF3t2s1OQw0t3tPQ8cS66RZvtzTy6nE1VWQ&oe=6817A633&_nc_sid=5e03e0&_nc_cat=106",
"id": "50498554225@c.us",
"img": "https://media-mia3-1.cdn.whatsapp.net/v/t61.24694-24/471428085_1635189164083925_3546014480456031647_n.jpg?stp=dst-jpg_s96x96_tt6&ccb=11-4&oh=01_Q5Aa1QELed0umu8TOLHLhNq8lHmkZ2srD3fu-IK3spzsNxkLug&oe=6817A633&_nc_sid=5e03e0&_nc_cat=106",
"imgFull": "https://media-mia3-1.cdn.whatsapp.net/v/t61.24694-24/471428085_1635189164083925_3546014480456031647_n.jpg?ccb=11-4&oh=01_Q5Aa1QEqY8vxL1FGF3t2s1OQw0t3tPQ8cS66RZvtzTy6nE1VWQ&oe=6817A633&_nc_sid=5e03e0&_nc_cat=106",
"tag": "1735668406"
},
"msgs": null
},
"senderId": null,
"timestamp": 1745718602,
"content": "amor",
"isGroupMsg": false,
"isQuotedMsgAvailable": true,
"isMedia": false,
"chat": {
"id": "50496210031@c.us",
"pendingMsgs": false,
"lastReceivedKey": {
"fromMe": true,
"remote": "50496210031@c.us",
"id": "3F009E582691FEE944F0",
"_serialized": "true_50496210031@c.us_3F009E582691FEE944F0"
},
"t": 1745717805,
"unreadCount": 0,
"unreadDividerOffset": 0,
"archive": false,
"isReadOnly": false,
"isLocked": false,
"muteExpiration": 0,
"isAutoMuted": false,
"name": "Margie (:",
"notSpam": true,
"pin": 1695127572481,
"ephemeralDuration": 0,
"ephemeralSettingTimestamp": 0,
"disappearingModeInitiator": "chat",
"disappearingModeTrigger": "chat_settings",
"createdLocally": false,
"unreadMentionsOfMe": [],
"unreadMentionCount": 0,
"hasUnreadMention": false,
"archiveAtMentionViewedInDrawer": false,
"hasChatBeenOpened": false,
"tcToken": {},
"tcTokenTimestamp": 1745616969,
"tcTokenSenderTimestamp": 1744944499,
"endOfHistoryTransferType": 0,
"pendingInitialLoading": false,
"chatlistPreview": {
"type": "reaction",
"msgKey": "false_50496210031@c.us_3FEA807956686BD2AD73",
"parentMsgKey": "true_50496210031@c.us_3F93865C2A9E8061A668",
"reactionText": "🙏",
"sender": "50496210031@c.us",
"timestamp": 1745717107750
},
"unreadEditTimestampMs": 1745508257945,
"celebrationAnimationLastPlayed": 0,
"hasRequestedWelcomeMsg": false,
"msgs": null,
"canSend": true,
"isGroup": false,
"pic": "https://pps.whatsapp.net/v/t61.24694-24/470810943_1065895391975207_6852834404866940192_n.jpg?ccb=11-4&oh=01_Q5Aa1QEWA1-AVsmMc5-23KYTOSB9RsYUB41vONjdzNZCen_qGw&oe=6817C3B1&_nc_sid=5e03e0&_nc_cat=109",
"formattedTitle": "Margie (:",
"contact": {
"id": "50496210031@c.us",
"name": "Margie (:",
"shortName": "Margie",
"pushname": "Margie Elizabeth:)",
"type": "in",
"isBusiness": false,
"isEnterprise": false,
"isSmb": false,
"isContactSyncCompleted": 1,
"disappearingModeDuration": 0,
"disappearingModeSettingTimestamp": 1671409557,
"textStatusLastUpdateTime": -1,
"syncToAddressbook": true,
"formattedName": "Margie (:",
"isMe": false,
"isMyContact": true,
"isPSA": false,
"isUser": true,
"isVerified": false,
"isWAContact": true,
"profilePicThumbObj": {
"eurl": "https://pps.whatsapp.net/v/t61.24694-24/470810943_1065895391975207_6852834404866940192_n.jpg?ccb=11-4&oh=01_Q5Aa1QEWA1-AVsmMc5-23KYTOSB9RsYUB41vONjdzNZCen_qGw&oe=6817C3B1&_nc_sid=5e03e0&_nc_cat=109"
},
"msgs": null
},
"groupMetadata": null,
"presence": {
"id": "50496210031@c.us",
"chatstates": []
},
"isOnline": false,
"participantsCount": 1
},
"isOnline": false,
"chatId": "50496210031@c.us",
"mediaData": {},
"text": "amor"
}

120
agent/gemini.js Normal file
View File

@@ -0,0 +1,120 @@
// gemini.js integración Grounding con Google Search + logs de verificación
import genai from '@google/genai';
import { config } from './config.js';
import { log } from './logger.js';
const { GoogleGenAI, createUserContent, createPartFromUri } = genai;
/*───────────────────────────────────────────────────────────────*/
/* 📜 System prompt */
/*───────────────────────────────────────────────────────────────*/
const SYSTEM_PROMPT = `
#########
Tu nombre es nucleo
##########
RESPUESTAS CORTAS y CONCISAS
sos la inteligencia artificial de Servidores de G.O.D (nuestro J.A.R.V.I.S.).
tenés derecho a sostener opiniones firmes con lógica, sin corrección política excesiva.
Respuestas casuales breves; si te extendés, hacelo en un solo mensaje.
Podés continuar conversaciones, usar chistes, analogías, etc.
`.trim();
/*───────────────────────────────────────────────────────────────*/
/* 🧠 Inicializar Gemini */
/*───────────────────────────────────────────────────────────────*/
let ai = null;
function initGemini() {
if (!config.GEMINI_KEY) throw new Error('🔑 GEMINI_API_KEY no configurada');
if (!ai) {
ai = new GoogleGenAI({ apiKey: config.GEMINI_KEY });
log('info', `🧠 Gemini SDK inicializado (${config.GEMINI_MODEL_ID})`);
}
return ai;
}
/*───────────────────────────────────────────────────────────────*/
/* 🔍 Construir tools de búsqueda */
/*───────────────────────────────────────────────────────────────*/
function buildSearchTools() {
const model = config.GEMINI_MODEL_ID;
// Los objetos literales cumplen con el esquema Tool del SDK.
if (/^gemini-2\./.test(model) || /^gemini-2\.5/.test(model)) {
return [{ google_search: {} }]; // Searchasatool
}
if (/^gemini-1\.5/.test(model)) {
return [{
google_search_retrieval: {
dynamic_retrieval_config: {
mode: 'MODE_DYNAMIC',
dynamic_threshold: 0.3,
},
},
}];
}
return [];
}
/*───────────────────────────────────────────────────────────────*/
/* 🚀 askGemini */
/*───────────────────────────────────────────────────────────────*/
export async function askGemini(historial, files = {}) {
try {
const client = initGemini();
// 1⃣ Construir "contents"
let contents;
if (typeof historial === 'string') {
contents = historial;
} else if (Array.isArray(historial)) {
const parts = [];
for (const m of historial) {
if (m.type === 'document') continue;
if (m.type === 'chat') {
parts.push(`${m.senderName}: ${m.text} -- ${m.date}`);
continue;
}
const up = files[m.msgId?.toLowerCase?.()];
if (up?.uri) {
parts.push(
createPartFromUri(up.uri, up.mimeType),
`archivo ${m.type} de ${m.senderName}: ${m.caption || m.text} -- ${m.date}`
);
}
}
contents = createUserContent(parts);
} else {
throw new Error('Formato de historial no soportado');
}
// 2⃣ Herramientas
const tools = buildSearchTools();
// 3⃣ Llamar al modelo
const response = await client.models.generateContent({
model: config.GEMINI_MODEL_ID,
contents,
config: {
systemInstruction: SYSTEM_PROMPT,
maxOutputTokens: 4096,
temperature: 0.3, // menor → mayor factualidad
tools,
response_modalities: ['TEXT'],
},
});
// 4⃣ Log de grounding
const candidate = response?.candidates?.[0];
if (candidate?.groundingMetadata) {
log('info', '🔗 GroundingMetadata presente:', JSON.stringify(candidate.groundingMetadata.webSearchQueries));
} else {
log('warn', ' Sin groundingMetadata en la respuesta');
}
if (!candidate) return '⚠️ Sin candidato.';
return candidate.content.parts.map(p => p.text).join('').trim() || '⚠️ Respuesta vacía.';
} catch (e) {
log('error', 'Gemini falló:', e.message);
if (e.response?.status === 429) return '🚦 Límite alcanzado. Probá más tarde.';
return '⚠️ No se pudo obtener respuesta de Gemini.';
}
}

51
agent/handlers.js Normal file
View File

@@ -0,0 +1,51 @@
// handlers.js
import { respuestaMCP } from './respuestas/respuestaMCP.js'; // <- NUEVA IMPORTACIÓN
import fs from 'fs/promises';
import { log } from './logger.js';
// Ya no se necesitan: sendText, fetchChatMessages, setTypingStatus, askGemini aquí
import { processMessage } from './utils/processMessage.js';
import { saveMedia } from './utils/saveMedia.js';
import { respuestaNormal } from './respuestas/respuestaNormal.js'; // <- NUEVA IMPORTACIÓN
import { respuestaBrave } from './respuestas/respuestaBrave.js'; // <- NUEVA IMPORTACIÓN
import { sendText } from './whatsapp.js'; // <- NUEVA IMPORTACIÓN
/* carpeta raíz donde saveMedia deja todo */
const MEDIA_DIR = '/media';
await fs.mkdir(MEDIA_DIR, { recursive: true });
// La función cleanForGemini se movió a respuestaNormal.js
export async function processIncoming(raw) {
// Guarda media si no es un mensaje de texto plano
if (raw.type !== 'chat') {
try {
// Nota: saveMedia podría necesitar acceso a MEDIA_DIR si no está codificado internamente
await saveMedia(raw /*, MEDIA_DIR */); // Podrías necesitar pasar MEDIA_DIR si saveMedia lo requiere
} catch (error) {
log('error', 'Error guardando media:', error);
}
}
const msg = processMessage(raw);
const text = msg.text || '';
/* ----- comando @nucleo ----- */
if (/^@nucleo(\s|$)/i.test(text)) {
// Llama a la función importada
// await respuestaNormal(msg); // <- LLAMADA A LA FUNCIÓN EXTERNA
await respuestaMCP(msg); // <- LLAMADA A LA FUNCIÓN EXTERNA
} else {
// Lógica para otros mensajes (si aplica)
// log('debug', 'Mensaje recibido no es comando @nucleo:', text);
}
if (/^@nucleo.(\s|$)/i.test(text)) {
log('info', '🧠 Generando respuesta para @nucleo...');
const respuestaObjMCP = await respuestaBrave(msg); // <- LLAMADA A LA FUNCIÓN EXTERNA
log('info', 'Respuesta de MCP:', respuestaObjMCP);
sendText(msg.chatId, respuestaObjMCP);
} else {
// Lógica para otros mensajes (si aplica)
// log('debug', 'Mensaje recibido no es comando @nucleo:', text);
}
}

35
agent/index.js Normal file
View File

@@ -0,0 +1,35 @@
/* nucleo-bot ― index.js */
import express from 'express';
import morgan from 'morgan';
import { config } from './config.js';
import { log } from './logger.js';
import { router } from './routes.js';
import {
waitForGateway,
clearWebhooks,
registerWebhook
} from './whatsapp.js';
export let globalMemory = {};
/* 🌐 Express app */
const app = express();
app.use(express.json({ limit: '1mb' }));
app.use(morgan('[:date[iso]] :method :url :status - :response-time ms'));
app.use(router);
/* 🚀 Bootstrap */
async function bootstrap() {
await waitForGateway();
await clearWebhooks();
await registerWebhook();
}
/* 🏁 Arranque */
app.listen(config.PORT, () => {
log('info', `🪪 Versión del bot: ${config.VERSION}`);
log('info', `🚀 nucleo-bot escuchando en :${config.PORT}`);
bootstrap().catch(e => log('error', 'Error en bootstrap:', e.message));
});

17
agent/logger.js Normal file
View File

@@ -0,0 +1,17 @@
/*───────────────────────────────────────────────────────────────*/
/* 🖨️ Logger */
/*───────────────────────────────────────────────────────────────*/
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc.js';
import { config } from './config.js';
dayjs.extend(utc);
export function log(level, ...args) {
const levels = ['debug', 'info', 'warn', 'error'];
if (levels.indexOf(level) >= levels.indexOf(config.LOG_LEVEL)) {
console[level === 'debug' ? 'log' : level](
`[${dayjs().utc().format()}]`, level.toUpperCase(), ...args
);
}
}

20
agent/package.json Normal file
View File

@@ -0,0 +1,20 @@
{
"name": "nucleo-bot",
"version": "0.4.0",
"type": "module",
"scripts": {
"start:mcp": "npx @philschmid/weather-mcp"
},
"dependencies": {
"axios": "^1.8.4",
"dayjs": "^1.11.11",
"express": "^4.19.2",
"morgan": "^1.10.0",
"@google/generative-ai": "^0.4.0",
"@google/genai": "^0.9.0",
"@open-wa/wa-automate": "^4.34.3",
"mime-types": "^2.1.35",
"@modelcontextprotocol/sdk": "^1.0.0",
"@philschmid/weather-mcp": "^1.0.0"
}
}

View File

@@ -0,0 +1,128 @@
// /respuesta/respuestaBrave.js
import fs from 'fs/promises';
import { log } from '../logger.js';
import {
sendText,
fetchChatMessages,
setTypingStatus
} from '../whatsapp.js';
import { processMessage } from '../utils/processMessage.js';
import { saveMedia } from '../utils/saveMedia.js';
import { GoogleGenAI } from '@google/genai';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
function sanitizeSchema(obj) {
if (Array.isArray(obj)) return obj.map(sanitizeSchema);
if (obj && typeof obj === 'object') {
const out = {};
for (const [k, v] of Object.entries(obj)) {
if (k === 'additionalProperties' || k === '$schema') continue;
out[k] = sanitizeSchema(v);
}
return out;
}
return obj;
}
const cleanForGemini = ({ media, reactions, preview, ...rest }) => rest;
export async function respuestaBrave(msg) {
const text = msg.text || '';
setTypingStatus(msg.chatId, true);
log('info', '🧠 Generando respuesta para @nucleo (Brave)…');
try {
const allraw = await fetchChatMessages(msg.chatId);
const allMsgs = allraw.map(processMessage);
const context = allMsgs.map(cleanForGemini);
let json = JSON.stringify(context);
while (json.length > 100_000 && context.length) {
context.shift();
json = JSON.stringify(context);
}
const chatName = context.length > 0 ? context[0]?.chatName : 'chat';
const prompt = [
`Eres un asistente con acceso a Brave Search mediante herramientas externas.`,
`Pregunta del usuario: ${text}`,
...context
];
const medias = allraw.filter(m => m.type !== 'chat');
const settled = await Promise.allSettled(medias.map(saveMedia));
const MAX = 20 * 1024 * 1024;
let total = 0;
const files = {};
for (const r of settled) {
if (r.status !== 'fulfilled' || !r.value) continue;
const entries = Object.entries(r.value);
if (entries.length === 0) continue;
const [msgId, obj] = entries[0];
let size = Number(obj.sizeBytes || 0);
if (!size && obj.filePath) {
try { size = (await fs.stat(obj.filePath)).size; } catch { size = 0; }
}
if (total + size > MAX && total > 0) break;
if (size > MAX) continue;
total += size;
files[msgId] = { uri: obj.uri || obj.filePath, mimeType: obj.mimeType };
}
// === MCP Brave Search ===
const client = new Client({ name: 'brave-agent', version: '1.0.0' });
const serverParams = new StdioClientTransport({
command: 'uvx',
args: ['mcp-server-fetch']
});
await client.connect(serverParams);
const mcp = await client.listTools();
const tools = mcp.tools.map(t => ({
name: t.name,
description: t.description,
parameters: sanitizeSchema({
type: 'object',
...t.inputSchema
})
}));
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash-preview-04-17',
contents: prompt,
config: {
tools: [{ functionDeclarations: tools }],
temperature: 0
}
});
let respuesta;
if (response.functionCalls && response.functionCalls.length > 0) {
const call = response.functionCalls[0];
const result = await client.callTool({ name: call.name, arguments: call.args });
await client.close();
respuesta = result.content?.[0]?.text ?? JSON.stringify(result);
} else {
await client.close();
respuesta = response.text;
}
await sendText(msg.chatId, respuesta);
log('info', '🧠 Respuesta enviada');
} catch (error) {
log('error', 'Error en respuestaBrave:', error);
await sendText(msg.chatId, 'Hubo un error al procesar tu solicitud.');
} finally {
setTypingStatus(msg.chatId, false);
}
}

View File

@@ -0,0 +1,127 @@
// /respuesta/respuestaMCP.js
import fs from 'fs/promises';
import { log } from '../logger.js';
import {
sendText,
fetchChatMessages,
setTypingStatus
} from '../whatsapp.js';
import { processMessage } from '../utils/processMessage.js';
import { saveMedia } from '../utils/saveMedia.js';
import { GoogleGenAI } from '@google/genai';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
/* limpia el esquema JSON recursivamente */
function sanitizeSchema(obj) {
if (Array.isArray(obj)) return obj.map(sanitizeSchema);
if (obj && typeof obj === 'object') {
const out = {};
for (const [k, v] of Object.entries(obj)) {
if (k === 'additionalProperties' || k === '$schema') continue;
out[k] = sanitizeSchema(v);
}
return out;
}
return obj;
}
const cleanForGemini = ({ media, reactions, preview, ...rest }) => rest;
export async function respuestaMCP(msg) {
const text = msg.text || '';
setTypingStatus(msg.chatId, true);
log('info', '🧠 Generando respuesta para @nucleo…');
try {
const allraw = await fetchChatMessages(msg.chatId);
const allMsgs = allraw.map(processMessage);
const context = allMsgs.map(cleanForGemini);
let json = JSON.stringify(context);
while (json.length > 100_000 && context.length) {
context.shift();
json = JSON.stringify(context);
}
const chatName = context.length > 0 ? context[0]?.chatName : 'chat';
const prompt = [
`Eres el asistente del grupo "${chatName}". tenes la capacidad de interactuar con las carpetas ubicacadas en el directorio "/media/mcp" y sus subcarpetas.`,
`Pregunta del usuario: ${text}`,
...context
];
const medias = allraw.filter(m => m.type !== 'chat');
const settled = await Promise.allSettled(medias.map(saveMedia));
const MAX = 20 * 1024 * 1024;
let total = 0;
const files = {};
for (const r of settled) {
if (r.status !== 'fulfilled' || !r.value) continue;
const entries = Object.entries(r.value);
if (entries.length === 0) continue;
const [msgId, obj] = entries[0];
let size = Number(obj.sizeBytes || 0);
if (!size && obj.filePath) {
try { size = (await fs.stat(obj.filePath)).size; } catch { size = 0; }
}
if (total + size > MAX && total > 0) break;
if (size > MAX) continue;
total += size;
files[msgId] = { uri: obj.uri || obj.filePath, mimeType: obj.mimeType };
}
// === MCP setup ===
const client = new Client({ name: 'mcp-agent', version: '1.0.0' });
const serverParams = new StdioClientTransport({
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', '/media/mcp']
});
await client.connect(serverParams);
const mcp = await client.listTools();
const tools = mcp.tools.map(t => ({
name: t.name,
description: t.description,
parameters: sanitizeSchema({
type: 'object',
...t.inputSchema
})
}));
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash-preview-04-17',
contents: prompt,
config: {
tools: [{ googleSearch: {} }],
temperature: 0
}
});
let respuesta;
if (response.functionCalls && response.functionCalls.length > 0) {
const call = response.functionCalls[0];
const result = await client.callTool({ name: call.name, arguments: call.args });
await client.close();
respuesta = result.content?.[0]?.text ?? JSON.stringify(result);
} else {
await client.close();
respuesta = response.text;
}
await sendText(msg.chatId, respuesta);
log('info', '🧠 Respuesta enviada');
} catch (error) {
log('error', 'Error en respuestaMCP:', error);
await sendText(msg.chatId, 'Hubo un error al procesar tu solicitud.');
} finally {
setTypingStatus(msg.chatId, false);
}
}

View File

@@ -0,0 +1,100 @@
// /respuesta/respuestaNormal.js
import fs from 'fs/promises';
import { log } from '../logger.js'; // <- Nota el '../'
import {
sendText,
fetchChatMessages,
setTypingStatus
} from '../whatsapp.js'; // <- Nota el '../'
import { askGemini } from '../gemini.js'; // <- Nota el '../'
import { processMessage } from '../utils/processMessage.js'; // <- Nota el '../'
import { saveMedia } from '../utils/saveMedia.js'; // <- Nota el '../'
/* Quita campos pesados antes de mandar a Gemini */
const cleanForGemini = ({ media, reactions, preview, ...rest }) => rest;
/**
* Procesa la lógica específica para el comando @nucleo.
* Obtiene el historial, prepara el contexto, procesa media,
* llama a Gemini y envía la respuesta.
* @param {object} msg - El objeto de mensaje procesado.
*/
export async function respuestaNormal(msg) {
const text = msg.text || ''; // Necesitamos el texto original aquí también
setTypingStatus(msg.chatId, true);
log('info', '🧠 Generando respuesta para @nucleo…');
try {
/* 1) historial completo del chat */
const allraw = await fetchChatMessages(msg.chatId);
const allMsgs = allraw.map(processMessage);
/* 2) recorta contexto a ≤100 kB */
const context = allMsgs.map(cleanForGemini);
let json = JSON.stringify(context);
while (json.length > 100_000 && context.length) {
context.shift();
json = JSON.stringify(context);
}
// Asegurarse de que el contexto no esté vacío antes de acceder a context[0]
const chatName = context.length > 0 ? context[0]?.chatName : 'chat';
/* 3) prompt */
const prompt = [
`Eres el asistente del grupo "${chatName}".`,
`Pregunta del usuario: ${text}` // Usamos el 'text' del mensaje original que activó el comando
, ...context];
/* 4) procesa medias y respeta el límite de 20 MB */
// Filtrar solo los mensajes que no son de tipo 'chat' del historial obtenido
const medias = allraw.filter(m => m.type !== 'chat');
const settled = await Promise.allSettled(medias.map(saveMedia));
const MAX = 20 * 1024 * 1024; // 20 MB
let total = 0;
const files = {};
for (const r of settled) {
if (r.status !== 'fulfilled' || !r.value) continue;
if (typeof r.value !== 'object' || r.value === null) continue;
const entries = Object.entries(r.value);
if (entries.length === 0) continue;
const [msgId, obj] = entries[0];
if (typeof obj !== 'object' || obj === null) continue;
let size = Number(obj.sizeBytes || 0);
if (!size && obj.filePath) {
try { size = (await fs.stat(obj.filePath)).size; }
catch { size = 0; }
}
// Comprobar si añadir este archivo excede el límite MÁXIMO TOTAL
// y si ya tenemos *algo* (total > 0) para evitar empezar con un archivo demasiado grande
if (total + size > MAX && total > 0) break;
// Si este archivo *por sí solo* excede el límite, saltarlo
if (size > MAX) continue;
total += size;
files[msgId] = { uri: obj.uri || obj.filePath, mimeType: obj.mimeType };
}
// log('info', '🧠 Enviando a Gemini...', { files });
/* 5) llama a Gemini y responde */
const respuesta = await askGemini(prompt, files);
await sendText(msg.chatId, respuesta);
log('info', '🧠 Respuesta enviada');
} catch (error) {
log('error', 'Error en respuestaNormal:', error);
await sendText(msg.chatId, 'Hubo un error al procesar tu solicitud.');
} finally {
setTypingStatus(msg.chatId, false);
}
}

200
agent/routes.js Normal file
View File

@@ -0,0 +1,200 @@
// routes.js
import express from 'express';
import axios from 'axios';
import dayjs from 'dayjs';
import { config } from './config.js';
import { log } from './logger.js';
import { sendText } from './whatsapp.js';
import { processIncoming } from './handlers.js';
import { processMessage } from './utils/processMessage.js';
// chats a tomar en cuenta para el bot
const relevantChats = [
'50496210031@c.us',
'120363203056794284@g.us',
'120363398335375917@g.us',
'50498554225@c.us',
'50496934012@c.us',
'50497588328@c.us',
'50489701450@c.us'
]
export const router = express.Router();
// --- Manejo de eventos del webhook ------------------------------------------------
router.post('/webhook', async (req, res) => {
const { event, data: raw } = req.body;
const data = processMessage(raw);
// Si el evento no es relevante, ignorar
if(data.chatId && !relevantChats.includes(data.chatId)) {
log('info', `Mensaje de ${data.chatId} ignorado`);
return res.sendStatus(200);
}
// console.log('----------------------------------------------------------------');
// log('debug', '↪︎ Mensaje IN →', raw);
// console.log('----------------------------------------------------------------');
// log('debug', `📩 Webhook event "${event}"`);
switch (event) {
case 'onAck':
log('info', 'Ack:', data);
break;
case 'onAddedToGroup':
log('info', 'Added to group:', data);
break;
case 'onAnyMessage':
// log('info', 'onAnyMessage', data);
log('info', 'onAnyMessage', raw.chatId);
await processIncoming(raw);
break;
case 'onBattery':
log('info', 'Battery status:', data);
break;
case 'onBroadcast':
log('info', 'Broadcast:', data);
break;
case 'onButton':
log('info', 'Button pressed:', data);
break;
case 'onCallState':
log('info', 'Call state:', data);
break;
case 'onChatDeleted':
log('info', 'Chat deleted:', data);
break;
case 'onChatOpened':
log('info', 'Chat opened:', data);
break;
case 'onChatState':
log('info', 'Chat state:', data);
break;
case 'onContactAdded':
log('info', 'Contact added:', data);
break;
case 'onGlobalParticipantsChanged':
log('info', 'Global participants changed:', data);
break;
case 'onGroupApprovalRequest':
log('info', 'Group approval request:', data);
break;
case 'onGroupChange':
log('info', 'Group change:', data);
break;
case 'onIncomingCall':
log('info', 'Incoming call:', data);
break;
case 'onLabel':
log('info', 'Label event:', data);
break;
case 'onLogout':
log('info', 'Logout:', data);
break;
case 'onMessage':
log('info', 'Message:', data);
break;
case 'onMessageDeleted':
log('info', 'Message deleted:', data);
break;
case 'onNewProduct':
log('info', 'New product:', data);
break;
case 'onOrder':
log('info', 'Order:', data);
break;
case 'onPlugged':
log('info', 'Plugged:', data);
break;
case 'onPollVote':
log('info', 'Poll vote:', data);
break;
case 'onReaction':
log('info', 'Reaction:', data);
break;
case 'onRemovedFromGroup':
log('info', 'Removed from group:', data);
break;
case 'onStateChanged':
log('info', 'State changed:', data);
break;
case 'onStory':
log('info', 'Story:', data);
break;
default:
log('warn', `Unhandled event type: "${event}"`, data);
break;
}
res.sendStatus(200);
});
// comentado el 4/26/2025
/* Debug: escanear últimos mensajes */
// router.get('/debug/scan', async (_req, res) => {
// const { data } = await axios.post(`${config.API_URL}/loadAndGetAllMessagesInChat`, {
// args: { chatId: config.GROUP_ID, includeMe: 'true', includeNotifications: 'false' }
// });
// const msgs = (data?.response ?? []).slice(-20);
// log('info', `Escaneando ${msgs.length} mensajes recientes…`);
// for (const m of msgs) await processIncoming(m);
// res.json({ ok: true, scanned: msgs.length });
// });
/* Debug: enviar mensaje */
// router.get('/debug/send', async (req, res) => {
// const text = req.query.msg ?? config.REPLY_MSG;
// const resp = await sendText(config.GROUP_ID, text);
// res.json({ ok: true, resp });
// });
/* Debug: versión */
router.get('/debug/version', (_req, res) => {
res.json({ version: config.VERSION });
});

View File

@@ -0,0 +1,80 @@
// utils/decryptMediaContent.js (ES modules)
import fs from 'fs';
import path from 'path';
import axios from 'axios';
import mime from 'mime-types';
import { decryptMedia } from '@open-wa/wa-automate';
import { log } from '../logger.js';
// 🔒 quita caracteres que rompen rutas
const safe = s => (s || '').replace(/[\\/:*?"<>|]/g, '_');
export async function decryptMediaContent(
mediaInfo,
outputDir = 'media/dec',
rawDir = 'media/raw',
filename = null
) {
const { clientUrl, t, filehash, msgId, type } = mediaInfo;
let { mimetype } = mediaInfo;
if (!clientUrl) {
log('error', '❌ Sin clientUrl, no se puede bajar');
return null;
}
// deducir mimetype si falta
if (!mimetype) {
mimetype =
mime.lookup(clientUrl) ||
(type?.startsWith('image') && 'image/jpeg') ||
(type?.startsWith('video') && 'video/mp4') ||
'application/octet-stream';
}
const ext = mime.extension(mimetype) || 'bin';
const baseName = safe(filename || msgId || filehash?.slice(0,16) || `file_${t||Date.now()}`);
const rawPath = path.join(rawDir , `${baseName}.enc`);
const decPath = path.join(outputDir, `${baseName}.${ext}`);
if (fs.existsSync(decPath)) return decPath;
fs.mkdirSync(rawDir , { recursive: true });
fs.mkdirSync(outputDir, { recursive: true });
try {
/* ───── descarga RAW (solo si no existe) ───── */
if (!fs.existsSync(rawPath)) {
const { data } = await axios
.get(clientUrl, { responseType: 'arraybuffer' })
.catch(e => {
if (e.response?.status === 410) throw new Error('URL expirada (410)');
throw e;
});
fs.writeFileSync(rawPath, Buffer.from(data));
}
/* ───── inyectar RAW para que decryptMedia no lo vuelva a bajar ───── */
const fake = {
...mediaInfo,
mimetype,
mimeType: mimetype,
_data: {
...mediaInfo,
mimetype,
mimeType: mimetype,
_raw: fs.readFileSync(rawPath)
}
};
const plain = await decryptMedia(fake);
if (!plain?.length) throw new Error('descifrado vacío');
fs.writeFileSync(decPath, plain);
log('info', `✔️ ${type || ext}${decPath}`);
return decPath;
} catch (e) {
log('error', `❌ decryptMedia falló → ${e.message}`);
return null;
}
}

View File

@@ -0,0 +1,80 @@
import dayjs from 'dayjs';
/**
* Convierte el raw de open-wa a un objeto compacto.
* Solo coloca `media` cuando hay TODO para desencriptar: mediaKey + clientUrl.
*/
export function processMessage(raw) {
const m = raw?.data ?? raw; // alias corto
const base = {
msgId : m.id,
chatId : m.chatId,
chatName : m.chat?.name ?? m.chat?.formattedTitle ?? null,
senderId : m.sender?.id ?? m.author ?? null,
senderName: m.sender?.name ?? m.notifyName ?? null,
type : m.type,
text : m.text ?? m.caption ?? '',
fromMe : !!m.fromMe,
timestamp : m.timestamp ?? m.t,
date : m.timestamp
? dayjs.unix(m.timestamp).utcOffset(-360).format('YYYY-MM-DD HH:mm:ss')
: null,
hasReactions: (m.reactions?.length ?? 0) > 0 || !!m.hasReaction,
reactions : (m.reactions ?? []).map(r => r.aggregateEmoji)
};
/* ---------- quoted ---------- */
if (m.quotedMsg) {
base.replyTo = {
id : m.quotedMsg.id,
text: m.quotedMsg.text ?? m.quotedMsg.body ?? '',
from: m.quotedParticipant ?? null,
type: m.quotedMsg.type
};
}
/* ---------- preview de enlaces ---------- */
if (m.matchedText) {
base.preview = {
url : m.matchedText,
title: m.title ?? null,
description: m.description ?? null
};
}
/* ---------- media listo para desencriptar ---------- */
const isMediaType = ['image','video','sticker','ptt','audio','document'].includes(m.type);
const hasKeys = m.mediaKey || m.mediaData?.mediaKey;
const hasUrl = m.clientUrl || m.directPath;
if (isMediaType && hasKeys && hasUrl) {
base.media = {
type : m.type,
clientUrl: m.clientUrl ?? m.directPath,
mimetype : m.mimetype,
size : m.size ?? m.mediaData?.size,
width : m.width,
height : m.height,
duration : Number(m.duration) || 0,
filename : m.filename ?? null,
caption : m.caption ?? null,
/* claves para decryptMediaContent */
mediaKey : m.mediaKey ?? m.mediaData?.mediaKey,
filehash : m.filehash ?? m.mediaData?.filehash,
t : m.t ?? m.timestamp
};
if (m.type === 'sticker') {
Object.assign(base.media, {
packId : m.stickerPackId ?? m.mediaData?.stickerPackId,
pack : m.stickerPackName ?? m.mediaData?.stickerPackName,
author : m.stickerPackPublisher ?? m.mediaData?.stickerPackPublisher,
animated: m.isLottie || m.mediaData?.isAnimated || false
});
}
}
return base;
}

80
agent/utils/saveMedia.js Normal file
View File

@@ -0,0 +1,80 @@
// utils/saveMedia.js
import fs from 'fs';
import path from 'path';
import axios from 'axios';
import mime from 'mime-types';
import { decryptMedia } from '@open-wa/wa-automate';
import { GoogleGenAI } from '@google/genai';
import { config } from '../config.js';
import { log } from '../logger.js';
const BASE_DIR = '/media';
const safe = s => (s || '').replace(/[\\/:*?"<>|]/g, '_');
const ai = new GoogleGenAI({ apiKey: config.GEMINI_KEY });
function getShortId(msgId = '') {
return msgId.replace(/^true_|^false_|@c\.us_/g, '').split('_')[0];
}
export async function saveMedia(msg) {
if (msg.type === 'chat') return null;
if (!msg.clientUrl) {
log('warn', `Sin clientUrl: ${msg.id}`);
return null;
}
const ext = mime.extension(msg.mimetype || 'application/octet-stream') || 'bin';
const chatId = safe(msg.chatId);
const shortId = (`${msg.timestamp}${msg.type}`).toLocaleLowerCase();
// const shortId = getShortId(msg.id);
const folder = path.join(BASE_DIR, chatId, safe(msg.type));
const filePath = path.join(folder, `${shortId}.${ext}`);
const fileName = shortId // solo para Files API
// Buscar primero en Files API
try {
const existing = await ai.files.get({ name: fileName });
log('info', `📂 ya existe en Files API: ${fileName}`);
return { [msg.id]: existing };
} catch (e) {
if (e.message?.includes('INVALID_ARGUMENT')) {
log('error', `files.get falló para ${fileName}: ${e.message}`);
}
}
// Buscar en disco
if (fs.existsSync(filePath)) {
log('info', `📁 ya existe local: ${fileName}`, 'subiendo a Files API');
const uploaded = await ai.files.upload({
file: fileName,
config: { mimeType: msg.mimetype || 'application/octet-stream', name: fileName },
});
log('info', `📤 subido a Files API: ${uploaded.name}`);
return { [msg.id]: uploaded };
}
try {
const raw = await axios.get(msg.clientUrl, { responseType: 'arraybuffer' });
msg._data = { ...msg, _raw: raw.data };
const buf = await decryptMedia(msg);
fs.mkdirSync(folder, { recursive: true });
fs.writeFileSync(filePath, buf);
log('info', `✅ guardado: ${filePath}`);
const uploaded = await ai.files.upload({
file: filePath,
config: { mimeType: msg.mimetype || 'application/octet-stream', name: fileName },
});
log('info', `📤 subido a Files API: ${uploaded.name}`);
return { [msg.id]: uploaded };
} catch (err) {
if (err.response?.status === 410) {
log('warn', `URL expirada (410) para ${msg.id}`);
} else {
log('error', `Error al guardar media ${msg.id}: ${err.message}`);
}
return null;
}
}

137
agent/whatsapp.js Normal file
View File

@@ -0,0 +1,137 @@
// whatsapp.js
import axios from 'axios';
import { config } from './config.js';
import { log } from './logger.js';
/* ✉️ Enviar texto -------------------------------------------------------- */
export async function sendText(to, content) {
log('info', `Enviando mensaje a ${to}: "${content}"`);
const { data } = await axios.post(`${config.API_URL}/sendText`, {
args: { to, content }
});
log('debug', 'Respuesta de /sendText →', data);
return data;
}
/* 🗨️ Traer todos los mensajes cargados en el chat ------------------------ */
export async function fetchChatMessages(chatId) {
try {
const { data } = await axios.post(`${config.API_URL}/getAllMessagesInChat`, {
args: {
chatId,
includeMe: 'true',
includeNotifications: 'false'
}
});
return data?.response ?? [];
} catch (e) {
log('error', 'Fallo /getAllMessagesInChat:', e.response?.data ?? e.message);
return [];
}
}
/* ⏳ Esperar gateway ------------------------------------------------------ */
export async function waitForGateway() {
for (let i = 1; i <= config.MAX_ATTEMPTS; i++) {
try {
await axios.get(`${config.API_URL}/api-docs`);
log('info', '🟢 whatsapp-gateway listo');
return;
} catch {
log('warn', `Gateway no responde (intento ${i}/${config.MAX_ATTEMPTS})…`);
await new Promise(r => setTimeout(r, config.RETRY_MS));
}
}
throw new Error('whatsapp-gateway no respondió a tiempo');
}
/* 🧹 Limpiar webhooks anteriores ----------------------------------------- */
export async function clearWebhooks() {
try {
const { data } = await axios.post(`${config.API_URL}/listWebhooks`);
const hooks = data?.response ?? [];
if (!hooks.length) {
log('info', 'Sin webhooks previos que limpiar');
return;
}
log('info', `Eliminando ${hooks.length} webhooks…`);
const results = await Promise.allSettled(
hooks.map(h => axios.post(`${config.API_URL}/removeWebhook`, { args: { webhookId: h.id } }))
);
results.forEach((r, i) => {
const id = hooks[i].id;
if (r.status === 'fulfilled' && r.value?.data?.response === true) {
log('debug', `✔️ Eliminado webhook ${id}`);
} else {
log('warn', `⚠️ Falló eliminar webhook ${id}`);
}
});
const ok = results.filter(r => r.status === 'fulfilled' && r.value?.data?.response === true).length;
log('info', `Limpieza OK (${ok}/${hooks.length} eliminados)`);
} catch (e) {
log('error', 'Fallo limpiando webhooks:', e.response?.data ?? e.message);
}
}
// --- Registro del webhook con todos los eventos disponibles ----------
export async function registerWebhook() {
const url = `http://nucleo-bot:${config.PORT}/webhook`;
const eventConfig = {
onAck: false, // ❌
onAddedToGroup: true,
onAnyMessage: true,
onBattery: true,
onBroadcast: true,
onButton: true,
onCallState: false, // ❌
onChatDeleted: true,
onChatOpened: true,
onChatState: true,
onContactAdded: true,
onGlobalParticipantsChanged: true,
onGroupApprovalRequest: true,
onGroupChange: true,
onIncomingCall: false, // ❌
onLabel: true,
onLogout: true,
onMessage: false, // ❌
onMessageDeleted: true,
onNewProduct: true,
onOrder: true,
onPlugged: false, // ❌
onPollVote: true,
onReaction: true,
onRemovedFromGroup: false, // ❌
onStateChanged: false, // ❌
onStory: false, // ❌
};
// usa el config de eventos para filtrar los habilitados (el config de arriba)
const allEvents = Object.entries(eventConfig)
.filter(([_, enabled]) => enabled)
.map(([event]) => event);
const { data } = await axios.post(`${config.API_URL}/registerWebhook`, {
args: { url, events: allEvents, id: 'nucleo-bot' }
});
log('info', '✔️ Webhook registrado:', data);
}
export async function setTypingStatus(chatId, status) {
try {
const { data } = await axios.post(`${config.API_URL}/simulateTyping`, {
args: { to: chatId, on: status }
});
log('debug', 'Respuesta de /setChatState →', data);
} catch (e) {
log('error', 'Fallo /setChatState:', e.response?.data ?? e.message);
}
}

3
api/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
node_modules
# Keep environment variables out of version control
.env

587
api/node_modules/.package-lock.json generated vendored
View File

@@ -1,38 +1,132 @@
{
"name": "api",
"name": "planilla-api",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/accepts": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
"node_modules/@esbuild/win32-x64": {
"version": "0.25.4",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz",
"integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@prisma/client": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.7.0.tgz",
"integrity": "sha512-+k61zZn1XHjbZul8q6TdQLpuI/cvyfil87zqK2zpreNIXyXtpUv3+H/oM69hcsFcZXaokHJIzPAt5Z8C8eK2QA==",
"hasInstallScript": true,
"engines": {
"node": ">=18.18"
},
"peerDependencies": {
"prisma": "*",
"typescript": ">=5.1.0"
},
"peerDependenciesMeta": {
"prisma": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
"node_modules/@prisma/config": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.7.0.tgz",
"integrity": "sha512-di8QDdvSz7DLUi3OOcCHSwxRNeW7jtGRUD2+Z3SdNE3A+pPiNT8WgUJoUyOwJmUr5t+JA2W15P78C/N+8RXrOA==",
"dependencies": {
"mime-types": "^3.0.0",
"negotiator": "^1.0.0"
"esbuild": ">=0.12 <1",
"esbuild-register": "3.6.0"
}
},
"node_modules/@prisma/debug": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.7.0.tgz",
"integrity": "sha512-RabHn9emKoYFsv99RLxvfG2GHzWk2ZI1BuVzqYtmMSIcuGboHY5uFt3Q3boOREM9de6z5s3bQoyKeWnq8Fz22w=="
},
"node_modules/@prisma/engines": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.7.0.tgz",
"integrity": "sha512-3wDMesnOxPrOsq++e5oKV9LmIiEazFTRFZrlULDQ8fxdub5w4NgRBoxtWbvXmj2nJVCnzuz6eFix3OhIqsZ1jw==",
"hasInstallScript": true,
"dependencies": {
"@prisma/debug": "6.7.0",
"@prisma/engines-version": "6.7.0-36.3cff47a7f5d65c3ea74883f1d736e41d68ce91ed",
"@prisma/fetch-engine": "6.7.0",
"@prisma/get-platform": "6.7.0"
}
},
"node_modules/@prisma/engines-version": {
"version": "6.7.0-36.3cff47a7f5d65c3ea74883f1d736e41d68ce91ed",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.7.0-36.3cff47a7f5d65c3ea74883f1d736e41d68ce91ed.tgz",
"integrity": "sha512-EvpOFEWf1KkJpDsBCrih0kg3HdHuaCnXmMn7XFPObpFTzagK1N0Q0FMnYPsEhvARfANP5Ok11QyoTIRA2hgJTA=="
},
"node_modules/@prisma/fetch-engine": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.7.0.tgz",
"integrity": "sha512-zLlAGnrkmioPKJR4Yf7NfW3hftcvqeNNEHleMZK9yX7RZSkhmxacAYyfGsCcqRt47jiZ7RKdgE0Wh2fWnm7WsQ==",
"dependencies": {
"@prisma/debug": "6.7.0",
"@prisma/engines-version": "6.7.0-36.3cff47a7f5d65c3ea74883f1d736e41d68ce91ed",
"@prisma/get-platform": "6.7.0"
}
},
"node_modules/@prisma/get-platform": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.7.0.tgz",
"integrity": "sha512-i9IH5lO4fQwnMLvQLYNdgVh9TK3PuWBfQd7QLk/YurnAIg+VeADcZDbmhAi4XBBDD+hDif9hrKyASu0hbjwabw==",
"dependencies": {
"@prisma/debug": "6.7.0"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
"node_modules/body-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"dependencies": {
"bytes": "^3.1.2",
"content-type": "^1.0.5",
"debug": "^4.4.0",
"http-errors": "^2.0.0",
"iconv-lite": "^0.6.3",
"on-finished": "^2.4.1",
"qs": "^6.14.0",
"raw-body": "^3.0.0",
"type-is": "^2.0.0"
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.13.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">=18"
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/bytes": {
@@ -71,9 +165,9 @@
}
},
"node_modules/content-disposition": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
"integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"dependencies": {
"safe-buffer": "5.2.1"
},
@@ -90,35 +184,24 @@
}
},
"node_modules/cookie": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
"engines": {
"node": ">=6.6.0"
}
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
"node_modules/debug": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
"ms": "2.0.0"
}
},
"node_modules/depd": {
@@ -129,6 +212,15 @@
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -182,6 +274,77 @@
"node": ">= 0.4"
}
},
"node_modules/esbuild": {
"version": "0.25.4",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz",
"integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==",
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.4",
"@esbuild/android-arm": "0.25.4",
"@esbuild/android-arm64": "0.25.4",
"@esbuild/android-x64": "0.25.4",
"@esbuild/darwin-arm64": "0.25.4",
"@esbuild/darwin-x64": "0.25.4",
"@esbuild/freebsd-arm64": "0.25.4",
"@esbuild/freebsd-x64": "0.25.4",
"@esbuild/linux-arm": "0.25.4",
"@esbuild/linux-arm64": "0.25.4",
"@esbuild/linux-ia32": "0.25.4",
"@esbuild/linux-loong64": "0.25.4",
"@esbuild/linux-mips64el": "0.25.4",
"@esbuild/linux-ppc64": "0.25.4",
"@esbuild/linux-riscv64": "0.25.4",
"@esbuild/linux-s390x": "0.25.4",
"@esbuild/linux-x64": "0.25.4",
"@esbuild/netbsd-arm64": "0.25.4",
"@esbuild/netbsd-x64": "0.25.4",
"@esbuild/openbsd-arm64": "0.25.4",
"@esbuild/openbsd-x64": "0.25.4",
"@esbuild/sunos-x64": "0.25.4",
"@esbuild/win32-arm64": "0.25.4",
"@esbuild/win32-ia32": "0.25.4",
"@esbuild/win32-x64": "0.25.4"
}
},
"node_modules/esbuild-register": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz",
"integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==",
"dependencies": {
"debug": "^4.3.4"
},
"peerDependencies": {
"esbuild": ">=0.12 <1"
}
},
"node_modules/esbuild-register/node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/esbuild-register/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=="
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -196,40 +359,44 @@
}
},
"node_modules/express": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.0",
"content-disposition": "^1.0.0",
"content-type": "^1.0.5",
"cookie": "^0.7.1",
"cookie-signature": "^1.2.1",
"debug": "^4.4.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"finalhandler": "^2.1.0",
"fresh": "^2.0.0",
"http-errors": "^2.0.0",
"merge-descriptors": "^2.0.0",
"mime-types": "^3.0.0",
"on-finished": "^2.4.1",
"once": "^1.4.0",
"parseurl": "^1.3.3",
"proxy-addr": "^2.0.7",
"qs": "^6.14.0",
"range-parser": "^1.2.1",
"router": "^2.2.0",
"send": "^1.1.0",
"serve-static": "^2.2.0",
"statuses": "^2.0.1",
"type-is": "^2.0.1",
"vary": "^1.1.2"
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.7.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.19.0",
"serve-static": "1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 18"
"node": ">= 0.10.0"
},
"funding": {
"type": "opencollective",
@@ -237,16 +404,17 @@
}
},
"node_modules/finalhandler": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
"dependencies": {
"debug": "^4.4.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"on-finished": "^2.4.1",
"parseurl": "^1.3.3",
"statuses": "^2.0.1"
"debug": "2.6.9",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
@@ -261,11 +429,11 @@
}
},
"node_modules/fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"engines": {
"node": ">= 0.8"
"node": ">= 0.6"
}
},
"node_modules/function-bind": {
@@ -360,11 +528,11 @@
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
@@ -383,11 +551,6 @@
"node": ">= 0.10"
}
},
"node_modules/is-promise": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -397,56 +560,80 @@
}
},
"node_modules/media-typer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"engines": {
"node": ">= 0.8"
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
"engines": {
"node": ">=18"
},
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "^1.54.0"
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"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=="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/node-cron": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.0.5.tgz",
"integrity": "sha512-XN5PwNBniFz6y9Ic/VChtrBtlXfAqdKUFou7nid/1pLGNFKO7cXN1nmLKUL7KLnM/Y4nUe5/EC+GsAghCj/N5g==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@@ -469,14 +656,6 @@
"node": ">= 0.8"
}
},
"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==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -486,12 +665,9 @@
}
},
"node_modules/path-to-regexp": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
"engines": {
"node": ">=16"
}
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
},
"node_modules/pg": {
"version": "8.15.6",
@@ -609,6 +785,33 @@
"node": ">=0.10.0"
}
},
"node_modules/prisma": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.7.0.tgz",
"integrity": "sha512-vArg+4UqnQ13CVhc2WUosemwh6hr6cr6FY2uzDvCIFwH8pu8BXVv38PktoMLVjtX7sbYThxbnZF5YiR8sN2clw==",
"hasInstallScript": true,
"dependencies": {
"@prisma/config": "6.7.0",
"@prisma/engines": "6.7.0"
},
"bin": {
"prisma": "build/index.js"
},
"engines": {
"node": ">=18.18"
},
"optionalDependencies": {
"fsevents": "2.3.3"
},
"peerDependencies": {
"typescript": ">=5.1.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -622,11 +825,11 @@
}
},
"node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"dependencies": {
"side-channel": "^1.1.0"
"side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
@@ -644,34 +847,19 @@
}
},
"node_modules/raw-body": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.6.3",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/router": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
"dependencies": {
"debug": "^4.4.0",
"depd": "^2.0.0",
"is-promise": "^4.0.0",
"parseurl": "^1.3.3",
"path-to-regexp": "^8.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -697,38 +885,53 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"dependencies": {
"debug": "^4.3.5",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"fresh": "^2.0.0",
"http-errors": "^2.0.0",
"mime-types": "^3.0.1",
"ms": "^2.1.3",
"on-finished": "^2.4.1",
"range-parser": "^1.2.1",
"statuses": "^2.0.1"
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
"engines": {
"node": ">= 18"
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/send/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=="
},
"node_modules/serve-static": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
"dependencies": {
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"parseurl": "^1.3.3",
"send": "^1.2.0"
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.19.0"
},
"engines": {
"node": ">= 18"
"node": ">= 0.8.0"
}
},
"node_modules/setprototypeof": {
@@ -829,13 +1032,12 @@
}
},
"node_modules/type-is": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": {
"content-type": "^1.0.5",
"media-typer": "^1.1.0",
"mime-types": "^3.0.0"
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
@@ -849,6 +1051,14 @@
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@@ -857,11 +1067,6 @@
"node": ">= 0.8"
}
},
"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=="
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",

View File

@@ -1,10 +1,3 @@
2.0.0 / 2024-08-31
==================
* Drop node <18 support
* deps: mime-types@^3.0.0
* deps: negotiator@^1.0.0
1.3.8 / 2022-02-02
==================

2
api/node_modules/accepts/README.md generated vendored
View File

@@ -74,7 +74,7 @@ appears in the `types` array). If nothing in `types` is accepted, then `false`
is returned.
The `types` array can contain full MIME types or file extensions. Any value
that is not a full MIME type is passed to `require('mime-types').lookup`.
that is not a full MIME types is passed to `require('mime-types').lookup`.
#### .types()

2
api/node_modules/accepts/index.js generated vendored
View File

@@ -229,7 +229,7 @@ function extToMime (type) {
* Check if mime is valid.
*
* @param {String} type
* @return {Boolean}
* @return {String}
* @private
*/

View File

@@ -1,7 +1,7 @@
{
"name": "accepts",
"description": "Higher-level content negotiation",
"version": "2.0.0",
"version": "1.3.8",
"contributors": [
"Douglas Christopher Wilson <doug@somethingdoug.com>",
"Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
@@ -9,8 +9,8 @@
"license": "MIT",
"repository": "jshttp/accepts",
"dependencies": {
"mime-types": "^3.0.0",
"negotiator": "^1.0.0"
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"devDependencies": {
"deep-equal": "1.0.1",

View File

@@ -1,69 +1,10 @@
2.2.0 / 2025-03-27
=========================
* refactor: normalize common options for all parsers
* deps:
* iconv-lite@^0.6.3
2.1.0 / 2025-02-10
=========================
* deps:
* type-is@^2.0.0
* debug@^4.4.0
* Removed destroy
* refactor: prefix built-in node module imports
* use the node require cache instead of custom caching
2.0.2 / 2024-10-31
=========================
* remove `unpipe` package and use native `unpipe()` method
2.0.1 / 2024-09-10
=========================
* Restore expected behavior `extended` to `false`
2.0.0 / 2024-09-10
=========================
* Propagate changes from 1.20.3
* add brotli support #406
* Breaking Change: Node.js 18 is the minimum supported version
2.0.0-beta.2 / 2023-02-23
=========================
This incorporates all changes after 1.19.1 up to 1.20.2.
* Remove deprecated `bodyParser()` combination middleware
* deps: debug@3.1.0
- Add `DEBUG_HIDE_DATE` environment variable
- Change timer to per-namespace instead of global
- Change non-TTY date format
- Remove `DEBUG_FD` environment variable support
- Support 256 namespace colors
* deps: iconv-lite@0.5.2
- Add encoding cp720
- Add encoding UTF-32
* deps: raw-body@3.0.0-beta.1
2.0.0-beta.1 / 2021-12-17
=========================
* Drop support for Node.js 0.8
* `req.body` is no longer always initialized to `{}`
- it is left `undefined` unless a body is parsed
* `urlencoded` parser now defaults `extended` to `false`
* Use `on-finished` to determine when body read
1.20.3 / 2024-09-10
===================
* deps: qs@6.13.0
* add `depth` option to customize the depth level in the parser
* IMPORTANT: The default `depth` level for parsing URL-encoded data is now `32` (previously was `Infinity`)
1.20.2 / 2023-02-21
===================

View File

@@ -51,12 +51,14 @@ $ npm install body-parser
## API
```js
const bodyParser = require('body-parser')
var bodyParser = require('body-parser')
```
The `bodyParser` object exposes various factories to create middlewares. All
middlewares will populate the `req.body` property with the parsed body when
the `Content-Type` request header matches the `type` option.
the `Content-Type` request header matches the `type` option, or an empty
object (`{}`) if there was no body to parse, the `Content-Type` was not matched,
or an error occurred.
The various errors returned by this module are described in the
[errors section](#errors).
@@ -65,8 +67,8 @@ The various errors returned by this module are described in the
Returns middleware that only parses `json` and only looks at requests where
the `Content-Type` header matches the `type` option. This parser accepts any
Unicode encoding of the body and supports automatic inflation of `gzip`,
`br` (brotli) and `deflate` encodings.
Unicode encoding of the body and supports automatic inflation of `gzip` and
`deflate` encodings.
A new `body` object containing the parsed data is populated on the `request`
object after the middleware (i.e. `req.body`).
@@ -120,8 +122,7 @@ encoding of the request. The parsing can be aborted by throwing an error.
Returns middleware that parses all bodies as a `Buffer` and only looks at
requests where the `Content-Type` header matches the `type` option. This
parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate`
encodings.
parser supports automatic inflation of `gzip` and `deflate` encodings.
A new `body` object containing the parsed data is populated on the `request`
object after the middleware (i.e. `req.body`). This will be a `Buffer` object
@@ -166,8 +167,7 @@ encoding of the request. The parsing can be aborted by throwing an error.
Returns middleware that parses all bodies as a string and only looks at
requests where the `Content-Type` header matches the `type` option. This
parser supports automatic inflation of `gzip`, `br` (brotli) and `deflate`
encodings.
parser supports automatic inflation of `gzip` and `deflate` encodings.
A new `body` string containing the parsed data is populated on the `request`
object after the middleware (i.e. `req.body`). This will be a string of the
@@ -217,7 +217,7 @@ encoding of the request. The parsing can be aborted by throwing an error.
Returns middleware that only parses `urlencoded` bodies and only looks at
requests where the `Content-Type` header matches the `type` option. This
parser accepts only UTF-8 encoding of the body and supports automatic
inflation of `gzip`, `br` (brotli) and `deflate` encodings.
inflation of `gzip` and `deflate` encodings.
A new `body` object containing the parsed data is populated on the `request`
object after the middleware (i.e. `req.body`). This object will contain
@@ -231,12 +231,16 @@ any of the following keys:
##### extended
The "extended" syntax allows for rich objects and arrays to be encoded into the
URL-encoded format, allowing for a JSON-like experience with URL-encoded. For
more information, please [see the qs
library](https://www.npmjs.org/package/qs#readme).
The `extended` option allows to choose between parsing the URL-encoded data
with the `querystring` library (when `false`) or the `qs` library (when
`true`). The "extended" syntax allows for rich objects and arrays to be
encoded into the URL-encoded format, allowing for a JSON-like experience
with URL-encoded. For more information, please
[see the qs library](https://www.npmjs.org/package/qs#readme).
Defaults to `false`.
Defaults to `true`, but using the default has been deprecated. Please
research into the difference between `qs` and `querystring` and choose the
appropriate setting.
##### inflate
@@ -274,23 +278,6 @@ The `verify` option, if supplied, is called as `verify(req, res, buf, encoding)`
where `buf` is a `Buffer` of the raw request body and `encoding` is the
encoding of the request. The parsing can be aborted by throwing an error.
##### defaultCharset
The default charset to parse as, if not specified in content-type. Must be
either `utf-8` or `iso-8859-1`. Defaults to `utf-8`.
##### charsetSentinel
Whether to let the value of the `utf8` parameter take precedence as the charset
selector. It requires the form to contain a parameter named `utf8` with a value
of `✓`. Defaults to `false`.
##### interpretNumericEntities
Whether to decode numeric entities such as `&#9786;` when parsing an iso-8859-1
form. Defaults to `false`.
#### depth
The `depth` option is used to configure the maximum depth of the `qs` library when `extended` is `true`. This allows you to limit the amount of keys that are parsed and can be useful to prevent certain types of abuse. Defaults to `32`. It is recommended to keep this value as low as possible.
@@ -404,13 +391,13 @@ top-level middleware, which will parse the bodies of all incoming requests.
This is the simplest setup.
```js
const express = require('express')
const bodyParser = require('body-parser')
var express = require('express')
var bodyParser = require('body-parser')
const app = express()
var app = express()
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded())
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
@@ -418,7 +405,7 @@ app.use(bodyParser.json())
app.use(function (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.write('you posted:\n')
res.end(String(JSON.stringify(req.body, null, 2)))
res.end(JSON.stringify(req.body, null, 2))
})
```
@@ -429,26 +416,24 @@ need them. In general, this is the most recommended way to use body-parser with
Express.
```js
const express = require('express')
const bodyParser = require('body-parser')
var express = require('express')
var bodyParser = require('body-parser')
const app = express()
var app = express()
// create application/json parser
const jsonParser = bodyParser.json()
var jsonParser = bodyParser.json()
// create application/x-www-form-urlencoded parser
const urlencodedParser = bodyParser.urlencoded()
var urlencodedParser = bodyParser.urlencoded({ extended: false })
// POST /login gets urlencoded bodies
app.post('/login', urlencodedParser, function (req, res) {
if (!req.body || !req.body.username) res.sendStatus(400)
res.send('welcome, ' + req.body.username)
})
// POST /api/users gets JSON bodies
app.post('/api/users', jsonParser, function (req, res) {
if (!req.body) res.sendStatus(400)
// create user in req.body
})
```
@@ -459,10 +444,10 @@ All the parsers accept a `type` option which allows you to change the
`Content-Type` that the middleware will parse.
```js
const express = require('express')
const bodyParser = require('body-parser')
var express = require('express')
var bodyParser = require('body-parser')
const app = express()
var app = express()
// parse various different custom JSON types as JSON
app.use(bodyParser.json({ type: 'application/*+json' }))

View File

@@ -6,6 +6,20 @@
'use strict'
/**
* Module dependencies.
* @private
*/
var deprecate = require('depd')('body-parser')
/**
* Cache of loaded parsers.
* @private
*/
var parsers = Object.create(null)
/**
* @typedef Parsers
* @type {function}
@@ -20,7 +34,8 @@
* @type {Parsers}
*/
exports = module.exports = bodyParser
exports = module.exports = deprecate.function(bodyParser,
'bodyParser: use individual json/urlencoded middlewares')
/**
* JSON parser.
@@ -30,7 +45,7 @@ exports = module.exports = bodyParser
Object.defineProperty(exports, 'json', {
configurable: true,
enumerable: true,
get: () => require('./lib/types/json')
get: createParserGetter('json')
})
/**
@@ -41,7 +56,7 @@ Object.defineProperty(exports, 'json', {
Object.defineProperty(exports, 'raw', {
configurable: true,
enumerable: true,
get: () => require('./lib/types/raw')
get: createParserGetter('raw')
})
/**
@@ -52,7 +67,7 @@ Object.defineProperty(exports, 'raw', {
Object.defineProperty(exports, 'text', {
configurable: true,
enumerable: true,
get: () => require('./lib/types/text')
get: createParserGetter('text')
})
/**
@@ -63,7 +78,7 @@ Object.defineProperty(exports, 'text', {
Object.defineProperty(exports, 'urlencoded', {
configurable: true,
enumerable: true,
get: () => require('./lib/types/urlencoded')
get: createParserGetter('urlencoded')
})
/**
@@ -75,6 +90,67 @@ Object.defineProperty(exports, 'urlencoded', {
* @public
*/
function bodyParser () {
throw new Error('The bodyParser() generic has been split into individual middleware to use instead.')
function bodyParser (options) {
// use default type for parsers
var opts = Object.create(options || null, {
type: {
configurable: true,
enumerable: true,
value: undefined,
writable: true
}
})
var _urlencoded = exports.urlencoded(opts)
var _json = exports.json(opts)
return function bodyParser (req, res, next) {
_json(req, res, function (err) {
if (err) return next(err)
_urlencoded(req, res, next)
})
}
}
/**
* Create a getter for loading a parser.
* @private
*/
function createParserGetter (name) {
return function get () {
return loadParser(name)
}
}
/**
* Load a parser module.
* @private
*/
function loadParser (parserName) {
var parser = parsers[parserName]
if (parser !== undefined) {
return parser
}
// this uses a switch for static require analysis
switch (parserName) {
case 'json':
parser = require('./lib/types/json')
break
case 'raw':
parser = require('./lib/types/raw')
break
case 'text':
parser = require('./lib/types/text')
break
case 'urlencoded':
parser = require('./lib/types/urlencoded')
break
}
// store to prevent invoking require()
return (parsers[parserName] = parser)
}

View File

@@ -12,10 +12,12 @@
*/
var createError = require('http-errors')
var destroy = require('destroy')
var getBody = require('raw-body')
var iconv = require('iconv-lite')
var onFinished = require('on-finished')
var zlib = require('node:zlib')
var unpipe = require('unpipe')
var zlib = require('zlib')
/**
* Module exports.
@@ -40,6 +42,9 @@ function read (req, res, next, parse, debug, options) {
var opts = options
var stream
// flag as parsed
req._body = true
// read options
var encoding = opts.encoding !== null
? opts.encoding
@@ -88,8 +93,8 @@ function read (req, res, next, parse, debug, options) {
// unpipe from stream and destroy
if (stream !== req) {
req.unpipe()
stream.destroy()
unpipe(req)
destroy(stream, true)
}
// read off entire request
@@ -120,7 +125,7 @@ function read (req, res, next, parse, debug, options) {
str = typeof body !== 'string' && encoding !== null
? iconv.decode(body, encoding)
: body
req.body = parse(str, encoding)
req.body = parse(str)
} catch (err) {
next(createError(400, err, {
body: str,
@@ -146,6 +151,7 @@ function read (req, res, next, parse, debug, options) {
function contentstream (req, debug, inflate) {
var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
var length = req.headers['content-length']
var stream
debug('content-encoding "%s"', encoding)
@@ -156,40 +162,29 @@ function contentstream (req, debug, inflate) {
})
}
if (encoding === 'identity') {
req.length = length
return req
}
var stream = createDecompressionStream(encoding, debug)
req.pipe(stream)
return stream
}
/**
* Create a decompression stream for the given encoding.
* @param {string} encoding
* @param {function} debug
* @return {object}
* @api private
*/
function createDecompressionStream (encoding, debug) {
switch (encoding) {
case 'deflate':
stream = zlib.createInflate()
debug('inflate body')
return zlib.createInflate()
req.pipe(stream)
break
case 'gzip':
stream = zlib.createGunzip()
debug('gunzip body')
return zlib.createGunzip()
case 'br':
debug('brotli decompress body')
return zlib.createBrotliDecompress()
req.pipe(stream)
break
case 'identity':
stream = req
stream.length = length
break
default:
throw createError(415, 'unsupported content encoding "' + encoding + '"', {
encoding: encoding,
type: 'encoding.unsupported'
})
}
return stream
}
/**

View File

@@ -12,12 +12,12 @@
* @private
*/
var bytes = require('bytes')
var contentType = require('content-type')
var createError = require('http-errors')
var debug = require('debug')('body-parser:json')
var isFinished = require('on-finished').isFinished
var read = require('../read')
var typeis = require('type-is')
var { getCharset, normalizeOptions } = require('../utils')
/**
* Module exports.
@@ -51,10 +51,25 @@ var JSON_SYNTAX_REGEXP = /#+/g
*/
function json (options) {
var { inflate, limit, verify, shouldParse } = normalizeOptions(options, 'application/json')
var opts = options || {}
var reviver = options?.reviver
var strict = options?.strict !== false
var limit = typeof opts.limit !== 'number'
? bytes.parse(opts.limit || '100kb')
: opts.limit
var inflate = opts.inflate !== false
var reviver = opts.reviver
var strict = opts.strict !== false
var type = opts.type || 'application/json'
var verify = opts.verify || false
if (verify !== false && typeof verify !== 'function') {
throw new TypeError('option verify must be function')
}
// create the appropriate type checking function
var shouldParse = typeof type !== 'function'
? typeChecker(type)
: type
function parse (body) {
if (body.length === 0) {
@@ -84,15 +99,13 @@ function json (options) {
}
return function jsonParser (req, res, next) {
if (isFinished(req)) {
if (req._body) {
debug('body already parsed')
next()
return
}
if (!('body' in req)) {
req.body = undefined
}
req.body = req.body || {}
// skip requests without bodies
if (!typeis.hasBody(req)) {
@@ -124,9 +137,9 @@ function json (options) {
// read
read(req, res, next, parse, debug, {
encoding: charset,
inflate,
limit,
verify
inflate: inflate,
limit: limit,
verify: verify
})
}
}
@@ -180,6 +193,21 @@ function firstchar (str) {
: undefined
}
/**
* Get the charset of a request.
*
* @param {object} req
* @api private
*/
function getCharset (req) {
try {
return (contentType.parse(req).parameters.charset || '').toLowerCase()
} catch (e) {
return undefined
}
}
/**
* Normalize a SyntaxError for JSON.parse.
*
@@ -204,3 +232,16 @@ function normalizeJsonSyntaxError (error, obj) {
return error
}
/**
* Get the simple type checker.
*
* @param {string} type
* @return {function}
*/
function typeChecker (type) {
return function checkType (req) {
return Boolean(typeis(req, type))
}
}

View File

@@ -10,11 +10,10 @@
* Module dependencies.
*/
var bytes = require('bytes')
var debug = require('debug')('body-parser:raw')
var isFinished = require('on-finished').isFinished
var read = require('../read')
var typeis = require('type-is')
var { normalizeOptions } = require('../utils')
/**
* Module exports.
@@ -31,22 +30,36 @@ module.exports = raw
*/
function raw (options) {
var { inflate, limit, verify, shouldParse } = normalizeOptions(options, 'application/octet-stream')
var opts = options || {}
var inflate = opts.inflate !== false
var limit = typeof opts.limit !== 'number'
? bytes.parse(opts.limit || '100kb')
: opts.limit
var type = opts.type || 'application/octet-stream'
var verify = opts.verify || false
if (verify !== false && typeof verify !== 'function') {
throw new TypeError('option verify must be function')
}
// create the appropriate type checking function
var shouldParse = typeof type !== 'function'
? typeChecker(type)
: type
function parse (buf) {
return buf
}
return function rawParser (req, res, next) {
if (isFinished(req)) {
if (req._body) {
debug('body already parsed')
next()
return
}
if (!('body' in req)) {
req.body = undefined
}
req.body = req.body || {}
// skip requests without bodies
if (!typeis.hasBody(req)) {
@@ -67,9 +80,22 @@ function raw (options) {
// read
read(req, res, next, parse, debug, {
encoding: null,
inflate,
limit,
verify
inflate: inflate,
limit: limit,
verify: verify
})
}
}
/**
* Get the simple type checker.
*
* @param {string} type
* @return {function}
*/
function typeChecker (type) {
return function checkType (req) {
return Boolean(typeis(req, type))
}
}

View File

@@ -10,11 +10,11 @@
* Module dependencies.
*/
var bytes = require('bytes')
var contentType = require('content-type')
var debug = require('debug')('body-parser:text')
var isFinished = require('on-finished').isFinished
var read = require('../read')
var typeis = require('type-is')
var { getCharset, normalizeOptions } = require('../utils')
/**
* Module exports.
@@ -31,24 +31,37 @@ module.exports = text
*/
function text (options) {
var { inflate, limit, verify, shouldParse } = normalizeOptions(options, 'text/plain')
var opts = options || {}
var defaultCharset = options?.defaultCharset || 'utf-8'
var defaultCharset = opts.defaultCharset || 'utf-8'
var inflate = opts.inflate !== false
var limit = typeof opts.limit !== 'number'
? bytes.parse(opts.limit || '100kb')
: opts.limit
var type = opts.type || 'text/plain'
var verify = opts.verify || false
if (verify !== false && typeof verify !== 'function') {
throw new TypeError('option verify must be function')
}
// create the appropriate type checking function
var shouldParse = typeof type !== 'function'
? typeChecker(type)
: type
function parse (buf) {
return buf
}
return function textParser (req, res, next) {
if (isFinished(req)) {
if (req._body) {
debug('body already parsed')
next()
return
}
if (!('body' in req)) {
req.body = undefined
}
req.body = req.body || {}
// skip requests without bodies
if (!typeis.hasBody(req)) {
@@ -72,9 +85,37 @@ function text (options) {
// read
read(req, res, next, parse, debug, {
encoding: charset,
inflate,
limit,
verify
inflate: inflate,
limit: limit,
verify: verify
})
}
}
/**
* Get the charset of a request.
*
* @param {object} req
* @api private
*/
function getCharset (req) {
try {
return (contentType.parse(req).parameters.charset || '').toLowerCase()
} catch (e) {
return undefined
}
}
/**
* Get the simple type checker.
*
* @param {string} type
* @return {function}
*/
function typeChecker (type) {
return function checkType (req) {
return Boolean(typeis(req, type))
}
}

View File

@@ -12,13 +12,13 @@
* @private
*/
var bytes = require('bytes')
var contentType = require('content-type')
var createError = require('http-errors')
var debug = require('debug')('body-parser:urlencoded')
var isFinished = require('on-finished').isFinished
var deprecate = require('depd')('body-parser')
var read = require('../read')
var typeis = require('type-is')
var qs = require('qs')
var { getCharset, normalizeOptions } = require('../utils')
/**
* Module exports.
@@ -26,6 +26,12 @@ var { getCharset, normalizeOptions } = require('../utils')
module.exports = urlencoded
/**
* Cache of parser modules.
*/
var parsers = Object.create(null)
/**
* Create a middleware to parse urlencoded bodies.
*
@@ -35,32 +41,52 @@ module.exports = urlencoded
*/
function urlencoded (options) {
var { inflate, limit, verify, shouldParse } = normalizeOptions(options, 'application/x-www-form-urlencoded')
var opts = options || {}
var defaultCharset = options?.defaultCharset || 'utf-8'
if (defaultCharset !== 'utf-8' && defaultCharset !== 'iso-8859-1') {
throw new TypeError('option defaultCharset must be either utf-8 or iso-8859-1')
// notice because option default will flip in next major
if (opts.extended === undefined) {
deprecate('undefined extended: provide extended option')
}
var extended = opts.extended !== false
var inflate = opts.inflate !== false
var limit = typeof opts.limit !== 'number'
? bytes.parse(opts.limit || '100kb')
: opts.limit
var type = opts.type || 'application/x-www-form-urlencoded'
var verify = opts.verify || false
var depth = typeof opts.depth !== 'number'
? Number(opts.depth || 32)
: opts.depth
if (verify !== false && typeof verify !== 'function') {
throw new TypeError('option verify must be function')
}
// create the appropriate query parser
var queryparse = createQueryParser(options)
var queryparse = extended
? extendedparser(opts)
: simpleparser(opts)
function parse (body, encoding) {
// create the appropriate type checking function
var shouldParse = typeof type !== 'function'
? typeChecker(type)
: type
function parse (body) {
return body.length
? queryparse(body, encoding)
? queryparse(body)
: {}
}
return function urlencodedParser (req, res, next) {
if (isFinished(req)) {
if (req._body) {
debug('body already parsed')
next()
return
}
if (!('body' in req)) {
req.body = undefined
}
req.body = req.body || {}
// skip requests without bodies
if (!typeis.hasBody(req)) {
@@ -79,8 +105,8 @@ function urlencoded (options) {
}
// assert charset
var charset = getCharset(req) || defaultCharset
if (charset !== 'utf-8' && charset !== 'iso-8859-1') {
var charset = getCharset(req) || 'utf-8'
if (charset !== 'utf-8') {
debug('invalid charset')
next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
charset: charset,
@@ -91,10 +117,12 @@ function urlencoded (options) {
// read
read(req, res, next, parse, debug, {
debug: debug,
encoding: charset,
inflate,
limit,
verify
inflate: inflate,
limit: limit,
verify: verify,
depth: depth
})
}
}
@@ -105,14 +133,15 @@ function urlencoded (options) {
* @param {object} options
*/
function createQueryParser (options) {
var extended = Boolean(options?.extended)
var parameterLimit = options?.parameterLimit !== undefined
? options?.parameterLimit
function extendedparser (options) {
var parameterLimit = options.parameterLimit !== undefined
? options.parameterLimit
: 1000
var charsetSentinel = options?.charsetSentinel
var interpretNumericEntities = options?.interpretNumericEntities
var depth = extended ? (options?.depth !== undefined ? options?.depth : 32) : 0
var depth = typeof options.depth !== 'number'
? Number(options.depth || 32)
: options.depth
var parse = parser('qs')
if (isNaN(parameterLimit) || parameterLimit < 1) {
throw new TypeError('option parameterLimit must be a positive number')
@@ -126,7 +155,7 @@ function createQueryParser (options) {
parameterLimit = parameterLimit | 0
}
return function queryparse (body, encoding) {
return function queryparse (body) {
var paramCount = parameterCount(body, parameterLimit)
if (paramCount === undefined) {
@@ -136,19 +165,16 @@ function createQueryParser (options) {
})
}
var arrayLimit = extended ? Math.max(100, paramCount) : 0
var arrayLimit = Math.max(100, paramCount)
debug('parse ' + (extended ? 'extended ' : '') + 'urlencoding')
debug('parse extended urlencoding')
try {
return qs.parse(body, {
return parse(body, {
allowPrototypes: true,
arrayLimit: arrayLimit,
depth: depth,
charsetSentinel: charsetSentinel,
interpretNumericEntities: interpretNumericEntities,
charset: encoding,
parameterLimit: parameterLimit,
strictDepth: true
strictDepth: true,
parameterLimit: parameterLimit
})
} catch (err) {
if (err instanceof RangeError) {
@@ -162,6 +188,21 @@ function createQueryParser (options) {
}
}
/**
* Get the charset of a request.
*
* @param {object} req
* @api private
*/
function getCharset (req) {
try {
return (contentType.parse(req).parameters.charset || '').toLowerCase()
} catch (e) {
return undefined
}
}
/**
* Count the number of parameters, stopping once limit reached
*
@@ -171,7 +212,96 @@ function createQueryParser (options) {
*/
function parameterCount (body, limit) {
var len = body.split('&').length
var count = 0
var index = 0
return len > limit ? undefined : len - 1
while ((index = body.indexOf('&', index)) !== -1) {
count++
index++
if (count === limit) {
return undefined
}
}
return count
}
/**
* Get parser for module name dynamically.
*
* @param {string} name
* @return {function}
* @api private
*/
function parser (name) {
var mod = parsers[name]
if (mod !== undefined) {
return mod.parse
}
// this uses a switch for static require analysis
switch (name) {
case 'qs':
mod = require('qs')
break
case 'querystring':
mod = require('querystring')
break
}
// store to prevent invoking require()
parsers[name] = mod
return mod.parse
}
/**
* Get the simple query parser.
*
* @param {object} options
*/
function simpleparser (options) {
var parameterLimit = options.parameterLimit !== undefined
? options.parameterLimit
: 1000
var parse = parser('querystring')
if (isNaN(parameterLimit) || parameterLimit < 1) {
throw new TypeError('option parameterLimit must be a positive number')
}
if (isFinite(parameterLimit)) {
parameterLimit = parameterLimit | 0
}
return function queryparse (body) {
var paramCount = parameterCount(body, parameterLimit)
if (paramCount === undefined) {
debug('too many parameters')
throw createError(413, 'too many parameters', {
type: 'parameters.too.many'
})
}
debug('parse urlencoding')
return parse(body, undefined, undefined, { maxKeys: parameterLimit })
}
}
/**
* Get the simple type checker.
*
* @param {string} type
* @return {function}
*/
function typeChecker (type) {
return function checkType (req) {
return Boolean(typeis(req, type))
}
}

View File

@@ -1,83 +0,0 @@
'use strict'
/**
* Module dependencies.
*/
var bytes = require('bytes')
var contentType = require('content-type')
var typeis = require('type-is')
/**
* Module exports.
*/
module.exports = {
getCharset,
normalizeOptions
}
/**
* Get the charset of a request.
*
* @param {object} req
* @api private
*/
function getCharset (req) {
try {
return (contentType.parse(req).parameters.charset || '').toLowerCase()
} catch {
return undefined
}
}
/**
* Get the simple type checker.
*
* @param {string | string[]} type
* @return {function}
*/
function typeChecker (type) {
return function checkType (req) {
return Boolean(typeis(req, type))
}
}
/**
* Normalizes the common options for all parsers.
*
* @param {object} options options to normalize
* @param {string | string[] | function} defaultType default content type(s) or a function to determine it
* @returns {object}
*/
function normalizeOptions (options, defaultType) {
if (!defaultType) {
// Parsers must define a default content type
throw new TypeError('defaultType must be provided')
}
var inflate = options?.inflate !== false
var limit = typeof options?.limit !== 'number'
? bytes.parse(options?.limit || '100kb')
: options?.limit
var type = options?.type || defaultType
var verify = options?.verify || false
if (verify !== false && typeof verify !== 'function') {
throw new TypeError('option verify must be function')
}
// create the appropriate type checking function
var shouldParse = typeof type !== 'function'
? typeChecker(type)
: type
return {
inflate,
limit,
verify,
shouldParse
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "body-parser",
"description": "Node.js body parsing middleware",
"version": "2.2.0",
"version": "1.20.3",
"contributors": [
"Douglas Christopher Wilson <doug@somethingdoug.com>",
"Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
@@ -9,15 +9,18 @@
"license": "MIT",
"repository": "expressjs/body-parser",
"dependencies": {
"bytes": "^3.1.2",
"content-type": "^1.0.5",
"debug": "^4.4.0",
"http-errors": "^2.0.0",
"iconv-lite": "^0.6.3",
"on-finished": "^2.4.1",
"qs": "^6.14.0",
"raw-body": "^3.0.0",
"type-is": "^2.0.0"
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.13.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"devDependencies": {
"eslint": "8.34.0",
@@ -27,23 +30,27 @@
"eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-standard": "4.1.0",
"mocha": "^11.1.0",
"nyc": "^17.1.0",
"supertest": "^7.0.0"
"methods": "1.1.2",
"mocha": "10.2.0",
"nyc": "15.1.0",
"safe-buffer": "5.2.1",
"supertest": "6.3.3"
},
"files": [
"lib/",
"LICENSE",
"HISTORY.md",
"SECURITY.md",
"index.js"
],
"engines": {
"node": ">=18"
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
},
"scripts": {
"lint": "eslint .",
"test": "mocha --reporter spec --check-leaks test/",
"test-ci": "nyc --reporter=lcovonly --reporter=text npm test",
"test": "mocha --require test/support/env --reporter spec --check-leaks --bail test/",
"test-ci": "nyc --reporter=lcov --reporter=text npm test",
"test-cov": "nyc --reporter=html --reporter=text npm test"
}
}

View File

@@ -1,9 +1,3 @@
1.0.0 / 2024-08-31
==================
* drop node <18
* allow utf8 as alias for utf-8
0.5.4 / 2021-12-10
==================

View File

@@ -243,7 +243,7 @@ function format (obj) {
for (var i = 0; i < params.length; i++) {
param = params[i]
var val = param.slice(-1) === '*'
var val = param.substr(-1) === '*'
? ustring(parameters[param])
: qstring(parameters[param])
@@ -281,7 +281,6 @@ function decodefield (str) {
value = getlatin1(binary)
break
case 'utf-8':
case 'utf8':
value = Buffer.from(binary, 'binary').toString('utf8')
break
default:
@@ -333,7 +332,7 @@ function parse (string) {
var value
// calculate index to start at
index = PARAM_REGEXP.lastIndex = match[0].slice(-1) === ';'
index = PARAM_REGEXP.lastIndex = match[0].substr(-1) === ';'
? index - 1
: index
@@ -370,7 +369,7 @@ function parse (string) {
if (value[0] === '"') {
// remove quotes and escapes
value = value
.slice(1, -1)
.substr(1, value.length - 2)
.replace(QESC_REGEXP, '$1')
}

View File

@@ -1,7 +1,7 @@
{
"name": "content-disposition",
"description": "Create and parse Content-Disposition header",
"version": "1.0.0",
"version": "0.5.4",
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
"license": "MIT",
"keywords": [
@@ -23,8 +23,8 @@
"eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "5.2.0",
"eslint-plugin-standard": "4.1.0",
"mocha": "^9.2.2",
"nyc": "15.1.0"
"istanbul": "0.4.5",
"mocha": "9.1.3"
},
"files": [
"LICENSE",
@@ -38,7 +38,7 @@
"scripts": {
"lint": "eslint .",
"test": "mocha --reporter spec --bail --check-leaks test/",
"test-ci": "nyc --reporter=lcovonly --reporter=text npm test",
"test-cov": "nyc --reporter=html --reporter=text npm test"
"test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/"
}
}

View File

@@ -1,35 +1,3 @@
1.2.2 / 2024-10-29
==================
* various metadata/documentation tweaks (incl. #51)
1.2.1 / 2023-02-27
==================
* update annotations for allowed secret key types (#44, thanks @jyasskin!)
1.2.0 / 2022-02-17
==================
* allow buffer and other node-supported types as key (#33)
* be pickier about extra content after signed portion (#40)
* some internal code clarity/cleanup improvements (#26)
1.1.0 / 2018-01-18
==================
* switch to built-in `crypto.timingSafeEqual` for validation instead of previous double-hash method (thank you @jodevsa!)
1.0.7 / 2023-04-12
==================
Later release for older node.js versions. See the [v1.0.x branch notes](https://github.com/tj/node-cookie-signature/blob/v1.0.x/History.md#107--2023-04-12).
1.0.6 / 2015-02-03
==================

View File

@@ -1,22 +0,0 @@
(The MIT License)
Copyright (c) 20122024 LearnBoost <tj@learnboost.com> and other contributors;
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -16,8 +16,27 @@ cookie.unsign(val, 'tobiiscool').should.equal('hello');
cookie.unsign(val, 'luna').should.be.false;
```
## License
## License
MIT.
(The MIT License)
See LICENSE file for details.
Copyright (c) 2012 LearnBoost &lt;tj@learnboost.com&gt;
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -8,14 +8,14 @@ var crypto = require('crypto');
* Sign the given `val` with `secret`.
*
* @param {String} val
* @param {String|NodeJS.ArrayBufferView|crypto.KeyObject} secret
* @param {String} secret
* @return {String}
* @api private
*/
exports.sign = function(val, secret){
if ('string' != typeof val) throw new TypeError("Cookie value must be provided as a string.");
if (null == secret) throw new TypeError("Secret key must be provided.");
if ('string' != typeof secret) throw new TypeError("Secret string must be provided.");
return val + '.' + crypto
.createHmac('sha256', secret)
.update(val)
@@ -24,24 +24,28 @@ exports.sign = function(val, secret){
};
/**
* Unsign and decode the given `input` with `secret`,
* Unsign and decode the given `val` with `secret`,
* returning `false` if the signature is invalid.
*
* @param {String} input
* @param {String|NodeJS.ArrayBufferView|crypto.KeyObject} secret
* @param {String} val
* @param {String} secret
* @return {String|Boolean}
* @api private
*/
exports.unsign = function(input, secret){
if ('string' != typeof input) throw new TypeError("Signed cookie string must be provided.");
if (null == secret) throw new TypeError("Secret key must be provided.");
var tentativeValue = input.slice(0, input.lastIndexOf('.')),
expectedInput = exports.sign(tentativeValue, secret),
expectedBuffer = Buffer.from(expectedInput),
inputBuffer = Buffer.from(input);
return (
expectedBuffer.length === inputBuffer.length &&
crypto.timingSafeEqual(expectedBuffer, inputBuffer)
) ? tentativeValue : false;
exports.unsign = function(val, secret){
if ('string' != typeof val) throw new TypeError("Signed cookie string must be provided.");
if ('string' != typeof secret) throw new TypeError("Secret string must be provided.");
var str = val.slice(0, val.lastIndexOf('.'))
, mac = exports.sign(str, secret);
return sha1(mac) == sha1(val) ? str : false;
};
/**
* Private
*/
function sha1(str){
return crypto.createHash('sha1').update(str).digest('hex');
}

View File

@@ -1,24 +1,18 @@
{
"name": "cookie-signature",
"version": "1.2.2",
"main": "index.js",
"version": "1.0.6",
"description": "Sign and unsign cookies",
"keywords": ["cookie", "sign", "unsign"],
"author": "TJ Holowaychuk <tj@learnboost.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/visionmedia/node-cookie-signature.git"
},
"repository": { "type": "git", "url": "https://github.com/visionmedia/node-cookie-signature.git"},
"dependencies": {},
"engines": {
"node": ">=6.6.0"
},
"devDependencies": {
"mocha": "*",
"should": "*"
},
"scripts": {
"test": "mocha --require should --reporter spec"
}
},
"main": "index"
}

3
api/node_modules/cookie/index.js generated vendored
View File

@@ -21,7 +21,6 @@ exports.serialize = serialize;
*/
var __toString = Object.prototype.toString
var __hasOwnProperty = Object.prototype.hasOwnProperty
/**
* RegExp to match cookie-name in RFC 6265 sec 4.1.1
@@ -131,7 +130,7 @@ function parse(str, opt) {
var key = str.slice(keyStartIdx, keyEndIdx);
// only assign once
if (!__hasOwnProperty.call(obj, key)) {
if (!obj.hasOwnProperty(key)) {
var valStartIdx = startIndex(str, eqIdx + 1, endIdx);
var valEndIdx = endIndex(str, endIdx, valStartIdx);

View File

@@ -1,7 +1,7 @@
{
"name": "cookie",
"description": "HTTP server cookie parsing and serialization",
"version": "0.7.2",
"version": "0.7.1",
"author": "Roman Shtylman <shtylman@gmail.com>",
"contributors": [
"Douglas Christopher Wilson <doug@somethingdoug.com>"

19
api/node_modules/debug/LICENSE generated vendored
View File

@@ -1,20 +1,19 @@
(The MIT License)
Copyright (c) 2014-2017 TJ Holowaychuk <tj@vision-media.ca>
Copyright (c) 2018-2021 Josh Junon
Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the 'Software'), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the 'Software'), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

293
api/node_modules/debug/README.md generated vendored
View File

@@ -1,11 +1,12 @@
# debug
[![OpenCollective](https://opencollective.com/debug/backers/badge.svg)](#backers)
[![Build Status](https://travis-ci.org/visionmedia/debug.svg?branch=master)](https://travis-ci.org/visionmedia/debug) [![Coverage Status](https://coveralls.io/repos/github/visionmedia/debug/badge.svg?branch=master)](https://coveralls.io/github/visionmedia/debug?branch=master) [![Slack](https://visionmedia-community-slackin.now.sh/badge.svg)](https://visionmedia-community-slackin.now.sh/) [![OpenCollective](https://opencollective.com/debug/backers/badge.svg)](#backers)
[![OpenCollective](https://opencollective.com/debug/sponsors/badge.svg)](#sponsors)
<img width="647" src="https://user-images.githubusercontent.com/71256/29091486-fa38524c-7c37-11e7-895f-e7ec8e1039b6.png">
A tiny JavaScript debugging utility modelled after Node.js core's debugging
technique. Works in Node.js and web browsers.
A tiny node.js debugging utility modelled after node core's debugging technique.
**Discussion around the V3 API is under way [here](https://github.com/visionmedia/debug/issues/370)**
## Installation
@@ -17,7 +18,7 @@ $ npm install debug
`debug` exposes a function; simply pass this function the name of your module, and it will return a decorated version of `console.error` for you to pass debug statements to. This will allow you to toggle the debug output for different parts of your module as well as the module as a whole.
Example [_app.js_](./examples/node/app.js):
Example _app.js_:
```js
var debug = require('debug')('http')
@@ -26,7 +27,7 @@ var debug = require('debug')('http')
// fake app
debug('booting %o', name);
debug('booting %s', name);
http.createServer(function(req, res){
debug(req.method + ' ' + req.url);
@@ -40,148 +41,81 @@ http.createServer(function(req, res){
require('./worker');
```
Example [_worker.js_](./examples/node/worker.js):
Example _worker.js_:
```js
var a = require('debug')('worker:a')
, b = require('debug')('worker:b');
var debug = require('debug')('worker');
function work() {
a('doing lots of uninteresting work');
setTimeout(work, Math.random() * 1000);
}
work();
function workb() {
b('doing some work');
setTimeout(workb, Math.random() * 2000);
}
workb();
setInterval(function(){
debug('doing some work');
}, 1000);
```
The `DEBUG` environment variable is then used to enable these based on space or
comma-delimited names.
The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples:
Here are some examples:
![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png)
<img width="647" alt="screen shot 2017-08-08 at 12 53 04 pm" src="https://user-images.githubusercontent.com/71256/29091703-a6302cdc-7c38-11e7-8304-7c0b3bc600cd.png">
<img width="647" alt="screen shot 2017-08-08 at 12 53 38 pm" src="https://user-images.githubusercontent.com/71256/29091700-a62a6888-7c38-11e7-800b-db911291ca2b.png">
<img width="647" alt="screen shot 2017-08-08 at 12 53 25 pm" src="https://user-images.githubusercontent.com/71256/29091701-a62ea114-7c38-11e7-826a-2692bedca740.png">
![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png)
#### Windows command prompt notes
#### Windows note
##### CMD
On Windows the environment variable is set using the `set` command.
On Windows the environment variable is set using the `set` command.
```cmd
set DEBUG=*,-not_this
```
```cmd
set DEBUG=*,-not_this
```
Note that PowerShell uses different syntax to set environment variables.
Example:
```cmd
set DEBUG=* & node app.js
```
##### PowerShell (VS Code default)
PowerShell uses different syntax to set environment variables.
```cmd
$env:DEBUG = "*,-not_this"
```
Example:
```cmd
$env:DEBUG='app';node app.js
```
```cmd
$env:DEBUG = "*,-not_this"
```
Then, run the program to be debugged as usual.
npm script example:
```js
"windowsDebug": "@powershell -Command $env:DEBUG='*';node app.js",
```
## Namespace Colors
Every debug instance has a color generated for it based on its namespace name.
This helps when visually parsing the debug output to identify which debug instance
a debug line belongs to.
#### Node.js
In Node.js, colors are enabled when stderr is a TTY. You also _should_ install
the [`supports-color`](https://npmjs.org/supports-color) module alongside debug,
otherwise debug will only use a small handful of basic colors.
<img width="521" src="https://user-images.githubusercontent.com/71256/29092181-47f6a9e6-7c3a-11e7-9a14-1928d8a711cd.png">
#### Web Browser
Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
option. These are WebKit web inspectors, Firefox ([since version
31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
and the Firebug plugin for Firefox (any version).
<img width="524" src="https://user-images.githubusercontent.com/71256/29092033-b65f9f2e-7c39-11e7-8e32-f6f0d8e865c1.png">
## Millisecond diff
When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
<img width="647" src="https://user-images.githubusercontent.com/71256/29091486-fa38524c-7c37-11e7-895f-e7ec8e1039b6.png">
![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png)
When stdout is not a TTY, `Date#toISOString()` is used, making it more useful for logging the debug information as shown below:
<img width="647" src="https://user-images.githubusercontent.com/71256/29091956-6bd78372-7c39-11e7-8c55-c948396d6edd.png">
When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below:
![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png)
## Conventions
If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". If you append a "*" to the end of your name, it will always be enabled regardless of the setting of the DEBUG environment variable. You can then use it for normal output as well as debug output.
If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser".
## Wildcards
The `*` character may be used as a wildcard. Suppose for example your library has
debuggers named "connect:bodyParser", "connect:compress", "connect:session",
instead of listing all three with
`DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do
`DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
The `*` character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
You can also exclude specific debuggers by prefixing them with a "-" character.
For example, `DEBUG=*,-connect:*` would include all debuggers except those
starting with "connect:".
You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with "connect:".
## Environment Variables
When running through Node.js, you can set a few environment variables that will
change the behavior of the debug logging:
When running through Node.js, you can set a few environment variables that will
change the behavior of the debug logging:
| Name | Purpose |
|-----------|-------------------------------------------------|
| `DEBUG` | Enables/disables specific debugging namespaces. |
| `DEBUG_HIDE_DATE` | Hide date from debug output (non-TTY). |
| `DEBUG_COLORS`| Whether or not to use colors in the debug output. |
| `DEBUG_DEPTH` | Object inspection depth. |
| `DEBUG_DEPTH` | Object inspection depth. |
| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. |
__Note:__ The environment variables beginning with `DEBUG_` end up being
converted into an Options object that gets used with `%o`/`%O` formatters.
See the Node.js documentation for
[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options)
for the complete list.
__Note:__ The environment variables beginning with `DEBUG_` end up being
converted into an Options object that gets used with `%o`/`%O` formatters.
See the Node.js documentation for
[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options)
for the complete list.
## Formatters
Debug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting.
Below are the officially supported formatters:
Debug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting. Below are the officially supported formatters:
| Formatter | Representation |
|-----------|----------------|
@@ -192,12 +126,9 @@ Below are the officially supported formatters:
| `%j` | JSON. Replaced with the string '[Circular]' if the argument contains circular references. |
| `%%` | Single percent sign ('%'). This does not consume an argument. |
### Custom formatters
You can add custom formatters by extending the `debug.formatters` object.
For example, if you wanted to add support for rendering a Buffer as hex with
`%h`, you could do something like:
You can add custom formatters by extending the `debug.formatters` object. For example, if you wanted to add support for rendering a Buffer as hex with `%h`, you could do something like:
```js
const createDebug = require('debug')
@@ -211,16 +142,14 @@ debug('this is hex: %h', new Buffer('hello world'))
// foo this is hex: 68656c6c6f20776f726c6421 +0ms
```
## Browser support
You can build a browser-ready script using [browserify](https://github.com/substack/node-browserify),
or just use the [browserify-as-a-service](https://wzrd.in/) [build](https://wzrd.in/standalone/debug@latest),
if you don't want to build it yourself.
## Browser Support
You can build a browser-ready script using [browserify](https://github.com/substack/node-browserify),
or just use the [browserify-as-a-service](https://wzrd.in/) [build](https://wzrd.in/standalone/debug@latest),
if you don't want to build it yourself.
Debug's enable state is currently persisted by `localStorage`.
Consider the situation shown below where you have `worker:a` and `worker:b`,
and wish to debug both. You can enable this using `localStorage.debug`:
Debug's enable state is currently persisted by `localStorage`.
Consider the situation shown below where you have `worker:a` and `worker:b`,
and wish to debug both. You can enable this using `localStorage.debug`:
```js
localStorage.debug = 'worker:*'
@@ -241,15 +170,23 @@ setInterval(function(){
}, 1200);
```
In Chromium-based web browsers (e.g. Brave, Chrome, and Electron), the JavaScript console will—by default—only show messages logged by `debug` if the "Verbose" log level is _enabled_.
#### Web Inspector Colors
Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
option. These are WebKit web inspectors, Firefox ([since version
31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
and the Firebug plugin for Firefox (any version).
Colored output looks something like:
![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png)
<img width="647" src="https://user-images.githubusercontent.com/7143133/152083257-29034707-c42c-4959-8add-3cee850e6fcf.png">
## Output streams
By default `debug` will log to stderr, however this can be configured per-namespace by overriding the `log` method:
Example [_stdout.js_](./examples/node/stdout.js):
Example _stdout.js_:
```js
var debug = require('debug');
@@ -271,118 +208,13 @@ error('now goes to stdout via console.info');
log('still goes to stdout, but via console.info now');
```
## Extend
You can simply extend debugger
```js
const log = require('debug')('auth');
//creates new debug instance with extended namespace
const logSign = log.extend('sign');
const logLogin = log.extend('login');
log('hello'); // auth hello
logSign('hello'); //auth:sign hello
logLogin('hello'); //auth:login hello
```
## Set dynamically
You can also enable debug dynamically by calling the `enable()` method :
```js
let debug = require('debug');
console.log(1, debug.enabled('test'));
debug.enable('test');
console.log(2, debug.enabled('test'));
debug.disable();
console.log(3, debug.enabled('test'));
```
print :
```
1 false
2 true
3 false
```
Usage :
`enable(namespaces)`
`namespaces` can include modes separated by a colon and wildcards.
Note that calling `enable()` completely overrides previously set DEBUG variable :
```
$ DEBUG=foo node -e 'var dbg = require("debug"); dbg.enable("bar"); console.log(dbg.enabled("foo"))'
=> false
```
`disable()`
Will disable all namespaces. The functions returns the namespaces currently
enabled (and skipped). This can be useful if you want to disable debugging
temporarily without knowing what was enabled to begin with.
For example:
```js
let debug = require('debug');
debug.enable('foo:*,-foo:bar');
let namespaces = debug.disable();
debug.enable(namespaces);
```
Note: There is no guarantee that the string will be identical to the initial
enable string, but semantically they will be identical.
## Checking whether a debug target is enabled
After you've created a debug instance, you can determine whether or not it is
enabled by checking the `enabled` property:
```javascript
const debug = require('debug')('http');
if (debug.enabled) {
// do stuff...
}
```
You can also manually toggle this property to force the debug instance to be
enabled or disabled.
## Usage in child processes
Due to the way `debug` detects if the output is a TTY or not, colors are not shown in child processes when `stderr` is piped. A solution is to pass the `DEBUG_COLORS=1` environment variable to the child process.
For example:
```javascript
worker = fork(WORKER_WRAP_PATH, [workerPath], {
stdio: [
/* stdin: */ 0,
/* stdout: */ 'pipe',
/* stderr: */ 'pipe',
'ipc',
],
env: Object.assign({}, process.env, {
DEBUG_COLORS: 1 // without this settings, colors won't be shown
}),
});
worker.stderr.pipe(process.stderr, { end: false });
```
## Authors
- TJ Holowaychuk
- Nathan Rajlich
- Andrew Rhyne
- Josh Junon
## Backers
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/debug#backer)]
@@ -458,8 +290,7 @@ Become a sponsor and get your logo on our README on Github with a link to your s
(The MIT License)
Copyright (c) 2014-2017 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
Copyright (c) 2018-2021 Josh Junon
Copyright (c) 2014-2016 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

60
api/node_modules/debug/package.json generated vendored
View File

@@ -1,65 +1,49 @@
{
"name": "debug",
"version": "4.4.0",
"version": "2.6.9",
"repository": {
"type": "git",
"url": "git://github.com/debug-js/debug.git"
"url": "git://github.com/visionmedia/debug.git"
},
"description": "Lightweight debugging utility for Node.js and the browser",
"description": "small debugging utility",
"keywords": [
"debug",
"log",
"debugger"
],
"files": [
"src",
"LICENSE",
"README.md"
],
"author": "Josh Junon (https://github.com/qix-)",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
"TJ Holowaychuk <tj@vision-media.ca>",
"Nathan Rajlich <nathan@tootallnate.net> (http://n8.io)",
"Andrew Rhyne <rhyneandrew@gmail.com>"
],
"license": "MIT",
"scripts": {
"lint": "xo",
"test": "npm run test:node && npm run test:browser && npm run lint",
"test:node": "istanbul cover _mocha -- test.js test.node.js",
"test:browser": "karma start --single-run",
"test:coverage": "cat ./coverage/lcov.info | coveralls"
},
"dependencies": {
"ms": "^2.1.3"
"ms": "2.0.0"
},
"devDependencies": {
"brfs": "^2.0.1",
"browserify": "^16.2.3",
"coveralls": "^3.0.2",
"browserify": "9.0.3",
"chai": "^3.5.0",
"concurrently": "^3.1.0",
"coveralls": "^2.11.15",
"eslint": "^3.12.1",
"istanbul": "^0.4.5",
"karma": "^3.1.4",
"karma-browserify": "^6.0.0",
"karma-chrome-launcher": "^2.2.0",
"karma": "^1.3.0",
"karma-chai": "^0.1.0",
"karma-mocha": "^1.3.0",
"mocha": "^5.2.0",
"karma-phantomjs-launcher": "^1.0.2",
"karma-sinon": "^1.0.5",
"mocha": "^3.2.0",
"mocha-lcov-reporter": "^1.2.0",
"sinon": "^14.0.0",
"xo": "^0.23.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
"rimraf": "^2.5.4",
"sinon": "^1.17.6",
"sinon-chai": "^2.8.0"
},
"main": "./src/index.js",
"browser": "./src/browser.js",
"engines": {
"node": ">=6.0"
},
"xo": {
"rules": {
"import/extensions": "off"
"component": {
"scripts": {
"debug/index.js": "browser.js",
"debug/debug.js": "debug.js"
}
}
}

299
api/node_modules/debug/src/browser.js generated vendored
View File

@@ -1,106 +1,31 @@
/* eslint-env browser */
/**
* This is the web browser implementation of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = require('./debug');
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.storage = localstorage();
exports.destroy = (() => {
let warned = false;
return () => {
if (!warned) {
warned = true;
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
}
};
})();
exports.storage = 'undefined' != typeof chrome
&& 'undefined' != typeof chrome.storage
? chrome.storage.local
: localstorage();
/**
* Colors.
*/
exports.colors = [
'#0000CC',
'#0000FF',
'#0033CC',
'#0033FF',
'#0066CC',
'#0066FF',
'#0099CC',
'#0099FF',
'#00CC00',
'#00CC33',
'#00CC66',
'#00CC99',
'#00CCCC',
'#00CCFF',
'#3300CC',
'#3300FF',
'#3333CC',
'#3333FF',
'#3366CC',
'#3366FF',
'#3399CC',
'#3399FF',
'#33CC00',
'#33CC33',
'#33CC66',
'#33CC99',
'#33CCCC',
'#33CCFF',
'#6600CC',
'#6600FF',
'#6633CC',
'#6633FF',
'#66CC00',
'#66CC33',
'#9900CC',
'#9900FF',
'#9933CC',
'#9933FF',
'#99CC00',
'#99CC33',
'#CC0000',
'#CC0033',
'#CC0066',
'#CC0099',
'#CC00CC',
'#CC00FF',
'#CC3300',
'#CC3333',
'#CC3366',
'#CC3399',
'#CC33CC',
'#CC33FF',
'#CC6600',
'#CC6633',
'#CC9900',
'#CC9933',
'#CCCC00',
'#CCCC33',
'#FF0000',
'#FF0033',
'#FF0066',
'#FF0099',
'#FF00CC',
'#FF00FF',
'#FF3300',
'#FF3333',
'#FF3366',
'#FF3399',
'#FF33CC',
'#FF33FF',
'#FF6600',
'#FF6633',
'#FF9900',
'#FF9933',
'#FFCC00',
'#FFCC33'
'lightseagreen',
'forestgreen',
'goldenrod',
'dodgerblue',
'darkorchid',
'crimson'
];
/**
@@ -111,35 +36,39 @@ exports.colors = [
* TODO: add a `localStorage` variable to explicitly enable/disable colors
*/
// eslint-disable-next-line complexity
function useColors() {
// NB: In an Electron preload script, document will be defined but not fully
// initialized. Since we know we're in Chrome, we'll just detect this case
// explicitly
if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {
return true;
}
// NB: In an Electron preload script, document will be defined but not fully
// initialized. Since we know we're in Chrome, we'll just detect this case
// explicitly
if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') {
return true;
}
// Internet Explorer and Edge do not support colors.
if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
return false;
}
let m;
// Is webkit? http://stackoverflow.com/a/16459606/376773
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
// eslint-disable-next-line no-return-assign
return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
// Is firebug? http://stackoverflow.com/a/398120/376773
(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
// Is firefox >= v31?
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
(typeof navigator !== 'undefined' && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31) ||
// Double check webkit in userAgent just in case we are in a worker
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
// is webkit? http://stackoverflow.com/a/16459606/376773
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
// is firebug? http://stackoverflow.com/a/398120/376773
(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
// is firefox >= v31?
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
// double check webkit in userAgent just in case we are in a worker
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
}
/**
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
*/
exports.formatters.j = function(v) {
try {
return JSON.stringify(v);
} catch (err) {
return '[UnexpectedJSONParseError]: ' + err.message;
}
};
/**
* Colorize log arguments if enabled.
*
@@ -147,49 +76,52 @@ function useColors() {
*/
function formatArgs(args) {
args[0] = (this.useColors ? '%c' : '') +
this.namespace +
(this.useColors ? ' %c' : ' ') +
args[0] +
(this.useColors ? '%c ' : ' ') +
'+' + module.exports.humanize(this.diff);
var useColors = this.useColors;
if (!this.useColors) {
return;
}
args[0] = (useColors ? '%c' : '')
+ this.namespace
+ (useColors ? ' %c' : ' ')
+ args[0]
+ (useColors ? '%c ' : ' ')
+ '+' + exports.humanize(this.diff);
const c = 'color: ' + this.color;
args.splice(1, 0, c, 'color: inherit');
if (!useColors) return;
// The final "%c" is somewhat tricky, because there could be other
// arguments passed either before or after the %c, so we need to
// figure out the correct index to insert the CSS into
let index = 0;
let lastC = 0;
args[0].replace(/%[a-zA-Z%]/g, match => {
if (match === '%%') {
return;
}
index++;
if (match === '%c') {
// We only are interested in the *last* %c
// (the user may have provided their own)
lastC = index;
}
});
var c = 'color: ' + this.color;
args.splice(1, 0, c, 'color: inherit')
args.splice(lastC, 0, c);
// the final "%c" is somewhat tricky, because there could be other
// arguments passed either before or after the %c, so we need to
// figure out the correct index to insert the CSS into
var index = 0;
var lastC = 0;
args[0].replace(/%[a-zA-Z%]/g, function(match) {
if ('%%' === match) return;
index++;
if ('%c' === match) {
// we only are interested in the *last* %c
// (the user may have provided their own)
lastC = index;
}
});
args.splice(lastC, 0, c);
}
/**
* Invokes `console.debug()` when available.
* No-op when `console.debug` is not a "function".
* If `console.debug` is not available, falls back
* to `console.log`.
* Invokes `console.log()` when available.
* No-op when `console.log` is not a "function".
*
* @api public
*/
exports.log = console.debug || console.log || (() => {});
function log() {
// this hackery is required for IE8/9, where
// the `console.log` function doesn't have 'apply'
return 'object' === typeof console
&& console.log
&& Function.prototype.apply.call(console.log, console, arguments);
}
/**
* Save `namespaces`.
@@ -197,17 +129,15 @@ exports.log = console.debug || console.log || (() => {});
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
try {
if (namespaces) {
exports.storage.setItem('debug', namespaces);
} else {
exports.storage.removeItem('debug');
}
} catch (error) {
// Swallow
// XXX (@Qix-) should we be logging these?
}
try {
if (null == namespaces) {
exports.storage.removeItem('debug');
} else {
exports.storage.debug = namespaces;
}
} catch(e) {}
}
/**
@@ -216,23 +146,27 @@ function save(namespaces) {
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
let r;
try {
r = exports.storage.getItem('debug');
} catch (error) {
// Swallow
// XXX (@Qix-) should we be logging these?
}
var r;
try {
r = exports.storage.debug;
} catch(e) {}
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
if (!r && typeof process !== 'undefined' && 'env' in process) {
r = process.env.DEBUG;
}
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
if (!r && typeof process !== 'undefined' && 'env' in process) {
r = process.env.DEBUG;
}
return r;
return r;
}
/**
* Enable namespaces listed in `localStorage.debug` initially.
*/
exports.enable(load());
/**
* Localstorage attempts to return the localstorage.
*
@@ -245,28 +179,7 @@ function load() {
*/
function localstorage() {
try {
// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context
// The Browser also has localStorage in the global context.
return localStorage;
} catch (error) {
// Swallow
// XXX (@Qix-) should we be logging these?
}
try {
return window.localStorage;
} catch (e) {}
}
module.exports = require('./common')(exports);
const {formatters} = module.exports;
/**
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
*/
formatters.j = function (v) {
try {
return JSON.stringify(v);
} catch (error) {
return '[UnexpectedJSONParseError]: ' + error.message;
}
};

292
api/node_modules/debug/src/common.js generated vendored
View File

@@ -1,292 +0,0 @@
/**
* This is the common logic for both the Node.js and web browser
* implementations of `debug()`.
*/
function setup(env) {
createDebug.debug = createDebug;
createDebug.default = createDebug;
createDebug.coerce = coerce;
createDebug.disable = disable;
createDebug.enable = enable;
createDebug.enabled = enabled;
createDebug.humanize = require('ms');
createDebug.destroy = destroy;
Object.keys(env).forEach(key => {
createDebug[key] = env[key];
});
/**
* The currently active debug mode names, and names to skip.
*/
createDebug.names = [];
createDebug.skips = [];
/**
* Map of special "%n" handling functions, for the debug "format" argument.
*
* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
*/
createDebug.formatters = {};
/**
* Selects a color for a debug namespace
* @param {String} namespace The namespace string for the debug instance to be colored
* @return {Number|String} An ANSI color code for the given namespace
* @api private
*/
function selectColor(namespace) {
let hash = 0;
for (let i = 0; i < namespace.length; i++) {
hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
hash |= 0; // Convert to 32bit integer
}
return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
}
createDebug.selectColor = selectColor;
/**
* Create a debugger with the given `namespace`.
*
* @param {String} namespace
* @return {Function}
* @api public
*/
function createDebug(namespace) {
let prevTime;
let enableOverride = null;
let namespacesCache;
let enabledCache;
function debug(...args) {
// Disabled?
if (!debug.enabled) {
return;
}
const self = debug;
// Set `diff` timestamp
const curr = Number(new Date());
const ms = curr - (prevTime || curr);
self.diff = ms;
self.prev = prevTime;
self.curr = curr;
prevTime = curr;
args[0] = createDebug.coerce(args[0]);
if (typeof args[0] !== 'string') {
// Anything else let's inspect with %O
args.unshift('%O');
}
// Apply any `formatters` transformations
let index = 0;
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
// If we encounter an escaped % then don't increase the array index
if (match === '%%') {
return '%';
}
index++;
const formatter = createDebug.formatters[format];
if (typeof formatter === 'function') {
const val = args[index];
match = formatter.call(self, val);
// Now we need to remove `args[index]` since it's inlined in the `format`
args.splice(index, 1);
index--;
}
return match;
});
// Apply env-specific formatting (colors, etc.)
createDebug.formatArgs.call(self, args);
const logFn = self.log || createDebug.log;
logFn.apply(self, args);
}
debug.namespace = namespace;
debug.useColors = createDebug.useColors();
debug.color = createDebug.selectColor(namespace);
debug.extend = extend;
debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.
Object.defineProperty(debug, 'enabled', {
enumerable: true,
configurable: false,
get: () => {
if (enableOverride !== null) {
return enableOverride;
}
if (namespacesCache !== createDebug.namespaces) {
namespacesCache = createDebug.namespaces;
enabledCache = createDebug.enabled(namespace);
}
return enabledCache;
},
set: v => {
enableOverride = v;
}
});
// Env-specific initialization logic for debug instances
if (typeof createDebug.init === 'function') {
createDebug.init(debug);
}
return debug;
}
function extend(namespace, delimiter) {
const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
newDebug.log = this.log;
return newDebug;
}
/**
* Enables a debug mode by namespaces. This can include modes
* separated by a colon and wildcards.
*
* @param {String} namespaces
* @api public
*/
function enable(namespaces) {
createDebug.save(namespaces);
createDebug.namespaces = namespaces;
createDebug.names = [];
createDebug.skips = [];
const split = (typeof namespaces === 'string' ? namespaces : '')
.trim()
.replace(' ', ',')
.split(',')
.filter(Boolean);
for (const ns of split) {
if (ns[0] === '-') {
createDebug.skips.push(ns.slice(1));
} else {
createDebug.names.push(ns);
}
}
}
/**
* Checks if the given string matches a namespace template, honoring
* asterisks as wildcards.
*
* @param {String} search
* @param {String} template
* @return {Boolean}
*/
function matchesTemplate(search, template) {
let searchIndex = 0;
let templateIndex = 0;
let starIndex = -1;
let matchIndex = 0;
while (searchIndex < search.length) {
if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === '*')) {
// Match character or proceed with wildcard
if (template[templateIndex] === '*') {
starIndex = templateIndex;
matchIndex = searchIndex;
templateIndex++; // Skip the '*'
} else {
searchIndex++;
templateIndex++;
}
} else if (starIndex !== -1) { // eslint-disable-line no-negated-condition
// Backtrack to the last '*' and try to match more characters
templateIndex = starIndex + 1;
matchIndex++;
searchIndex = matchIndex;
} else {
return false; // No match
}
}
// Handle trailing '*' in template
while (templateIndex < template.length && template[templateIndex] === '*') {
templateIndex++;
}
return templateIndex === template.length;
}
/**
* Disable debug output.
*
* @return {String} namespaces
* @api public
*/
function disable() {
const namespaces = [
...createDebug.names,
...createDebug.skips.map(namespace => '-' + namespace)
].join(',');
createDebug.enable('');
return namespaces;
}
/**
* Returns true if the given mode name is enabled, false otherwise.
*
* @param {String} name
* @return {Boolean}
* @api public
*/
function enabled(name) {
for (const skip of createDebug.skips) {
if (matchesTemplate(name, skip)) {
return false;
}
}
for (const ns of createDebug.names) {
if (matchesTemplate(name, ns)) {
return true;
}
}
return false;
}
/**
* Coerce `val`.
*
* @param {Mixed} val
* @return {Mixed}
* @api private
*/
function coerce(val) {
if (val instanceof Error) {
return val.stack || val.message;
}
return val;
}
/**
* XXX DO NOT USE. This is a temporary stub function.
* XXX It WILL be removed in the next major release.
*/
function destroy() {
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
}
createDebug.enable(createDebug.load());
return createDebug;
}
module.exports = setup;

View File

@@ -1,10 +1,10 @@
/**
* Detect Electron renderer / nwjs process, which is node, but we should
* Detect Electron renderer process, which is node, but we should
* treat as a browser.
*/
if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) {
module.exports = require('./browser.js');
if (typeof process !== 'undefined' && process.type === 'renderer') {
module.exports = require('./browser.js');
} else {
module.exports = require('./node.js');
module.exports = require('./node.js');
}

337
api/node_modules/debug/src/node.js generated vendored
View File

@@ -2,23 +2,22 @@
* Module dependencies.
*/
const tty = require('tty');
const util = require('util');
var tty = require('tty');
var util = require('util');
/**
* This is the Node.js implementation of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = require('./debug');
exports.init = init;
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.destroy = util.deprecate(
() => {},
'Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.'
);
/**
* Colors.
@@ -26,138 +25,80 @@ exports.destroy = util.deprecate(
exports.colors = [6, 2, 3, 4, 5, 1];
try {
// Optional dependency (as in, doesn't need to be installed, NOT like optionalDependencies in package.json)
// eslint-disable-next-line import/no-extraneous-dependencies
const supportsColor = require('supports-color');
if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) {
exports.colors = [
20,
21,
26,
27,
32,
33,
38,
39,
40,
41,
42,
43,
44,
45,
56,
57,
62,
63,
68,
69,
74,
75,
76,
77,
78,
79,
80,
81,
92,
93,
98,
99,
112,
113,
128,
129,
134,
135,
148,
149,
160,
161,
162,
163,
164,
165,
166,
167,
168,
169,
170,
171,
172,
173,
178,
179,
184,
185,
196,
197,
198,
199,
200,
201,
202,
203,
204,
205,
206,
207,
208,
209,
214,
215,
220,
221
];
}
} catch (error) {
// Swallow - we only care if `supports-color` is available; it doesn't have to be.
}
/**
* Build up the default `inspectOpts` object from the environment variables.
*
* $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
*/
exports.inspectOpts = Object.keys(process.env).filter(key => {
return /^debug_/i.test(key);
}).reduce((obj, key) => {
// Camel-case
const prop = key
.substring(6)
.toLowerCase()
.replace(/_([a-z])/g, (_, k) => {
return k.toUpperCase();
});
exports.inspectOpts = Object.keys(process.env).filter(function (key) {
return /^debug_/i.test(key);
}).reduce(function (obj, key) {
// camel-case
var prop = key
.substring(6)
.toLowerCase()
.replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() });
// Coerce string value into JS value
let val = process.env[key];
if (/^(yes|on|true|enabled)$/i.test(val)) {
val = true;
} else if (/^(no|off|false|disabled)$/i.test(val)) {
val = false;
} else if (val === 'null') {
val = null;
} else {
val = Number(val);
}
// coerce string value into JS value
var val = process.env[key];
if (/^(yes|on|true|enabled)$/i.test(val)) val = true;
else if (/^(no|off|false|disabled)$/i.test(val)) val = false;
else if (val === 'null') val = null;
else val = Number(val);
obj[prop] = val;
return obj;
obj[prop] = val;
return obj;
}, {});
/**
* The file descriptor to write the `debug()` calls to.
* Set the `DEBUG_FD` env variable to override with another value. i.e.:
*
* $ DEBUG_FD=3 node script.js 3>debug.log
*/
var fd = parseInt(process.env.DEBUG_FD, 10) || 2;
if (1 !== fd && 2 !== fd) {
util.deprecate(function(){}, 'except for stderr(2) and stdout(1), any other usage of DEBUG_FD is deprecated. Override debug.log if you want to use a different log function (https://git.io/debug_fd)')()
}
var stream = 1 === fd ? process.stdout :
2 === fd ? process.stderr :
createWritableStdioStream(fd);
/**
* Is stdout a TTY? Colored output is enabled when `true`.
*/
function useColors() {
return 'colors' in exports.inspectOpts ?
Boolean(exports.inspectOpts.colors) :
tty.isatty(process.stderr.fd);
return 'colors' in exports.inspectOpts
? Boolean(exports.inspectOpts.colors)
: tty.isatty(fd);
}
/**
* Map %o to `util.inspect()`, all on a single line.
*/
exports.formatters.o = function(v) {
this.inspectOpts.colors = this.useColors;
return util.inspect(v, this.inspectOpts)
.split('\n').map(function(str) {
return str.trim()
}).join(' ');
};
/**
* Map %o to `util.inspect()`, allowing multiple lines if needed.
*/
exports.formatters.O = function(v) {
this.inspectOpts.colors = this.useColors;
return util.inspect(v, this.inspectOpts);
};
/**
* Adds ANSI color escape codes if enabled.
*
@@ -165,33 +106,27 @@ function useColors() {
*/
function formatArgs(args) {
const {namespace: name, useColors} = this;
var name = this.namespace;
var useColors = this.useColors;
if (useColors) {
const c = this.color;
const colorCode = '\u001B[3' + (c < 8 ? c : '8;5;' + c);
const prefix = ` ${colorCode};1m${name} \u001B[0m`;
if (useColors) {
var c = this.color;
var prefix = ' \u001b[3' + c + ';1m' + name + ' ' + '\u001b[0m';
args[0] = prefix + args[0].split('\n').join('\n' + prefix);
args.push(colorCode + 'm+' + module.exports.humanize(this.diff) + '\u001B[0m');
} else {
args[0] = getDate() + name + ' ' + args[0];
}
}
function getDate() {
if (exports.inspectOpts.hideDate) {
return '';
}
return new Date().toISOString() + ' ';
args[0] = prefix + args[0].split('\n').join('\n' + prefix);
args.push('\u001b[3' + c + 'm+' + exports.humanize(this.diff) + '\u001b[0m');
} else {
args[0] = new Date().toUTCString()
+ ' ' + name + ' ' + args[0];
}
}
/**
* Invokes `util.formatWithOptions()` with the specified arguments and writes to stderr.
* Invokes `util.format()` with the specified arguments and writes to `stream`.
*/
function log(...args) {
return process.stderr.write(util.formatWithOptions(exports.inspectOpts, ...args) + '\n');
function log() {
return stream.write(util.format.apply(util, arguments) + '\n');
}
/**
@@ -200,14 +135,15 @@ function log(...args) {
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
if (namespaces) {
process.env.DEBUG = namespaces;
} else {
// If you set a process.env field to null or undefined, it gets cast to the
// string 'null' or 'undefined'. Just delete instead.
delete process.env.DEBUG;
}
if (null == namespaces) {
// If you set a process.env field to null or undefined, it gets cast to the
// string 'null' or 'undefined'. Just delete instead.
delete process.env.DEBUG;
} else {
process.env.DEBUG = namespaces;
}
}
/**
@@ -218,7 +154,75 @@ function save(namespaces) {
*/
function load() {
return process.env.DEBUG;
return process.env.DEBUG;
}
/**
* Copied from `node/src/node.js`.
*
* XXX: It's lame that node doesn't expose this API out-of-the-box. It also
* relies on the undocumented `tty_wrap.guessHandleType()` which is also lame.
*/
function createWritableStdioStream (fd) {
var stream;
var tty_wrap = process.binding('tty_wrap');
// Note stream._type is used for test-module-load-list.js
switch (tty_wrap.guessHandleType(fd)) {
case 'TTY':
stream = new tty.WriteStream(fd);
stream._type = 'tty';
// Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
break;
case 'FILE':
var fs = require('fs');
stream = new fs.SyncWriteStream(fd, { autoClose: false });
stream._type = 'fs';
break;
case 'PIPE':
case 'TCP':
var net = require('net');
stream = new net.Socket({
fd: fd,
readable: false,
writable: true
});
// FIXME Should probably have an option in net.Socket to create a
// stream from an existing fd which is writable only. But for now
// we'll just add this hack and set the `readable` member to false.
// Test: ./node test/fixtures/echo.js < /etc/passwd
stream.readable = false;
stream.read = null;
stream._type = 'pipe';
// FIXME Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
break;
default:
// Probably an error on in uv_guess_handle()
throw new Error('Implement me. Unknown stream file type!');
}
// For supporting legacy API we put the FD here.
stream.fd = fd;
stream._isStdio = true;
return stream;
}
/**
@@ -228,36 +232,17 @@ function load() {
* differently for a particular `debug` instance.
*/
function init(debug) {
debug.inspectOpts = {};
function init (debug) {
debug.inspectOpts = {};
const keys = Object.keys(exports.inspectOpts);
for (let i = 0; i < keys.length; i++) {
debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
}
var keys = Object.keys(exports.inspectOpts);
for (var i = 0; i < keys.length; i++) {
debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
}
}
module.exports = require('./common')(exports);
const {formatters} = module.exports;
/**
* Map %o to `util.inspect()`, all on a single line.
* Enable namespaces listed in `process.env.DEBUG` initially.
*/
formatters.o = function (v) {
this.inspectOpts.colors = this.useColors;
return util.inspect(v, this.inspectOpts)
.split('\n')
.map(str => str.trim())
.join(' ');
};
/**
* Map %O to `util.inspect()`, allowing multiple lines if needed.
*/
formatters.O = function (v) {
this.inspectOpts.colors = this.useColors;
return util.inspect(v, this.inspectOpts);
};
exports.enable(load());

232
api/node_modules/express/History.md generated vendored
View File

@@ -1,227 +1,25 @@
5.1.0 / 2025-03-31
========================
* Add support for `Uint8Array` in `res.send()`
* Add support for ETag option in `res.sendFile()`
* Add support for multiple links with the same rel in `res.links()`
* Add funding field to package.json
* perf: use loop for acceptParams
* refactor: prefix built-in node module imports
* deps: remove `setprototypeof`
* deps: remove `safe-buffer`
* deps: remove `utils-merge`
* deps: remove `methods`
* deps: remove `depd`
* deps: `debug@^4.4.0`
* deps: `body-parser@^2.2.0`
* deps: `router@^2.2.0`
* deps: `content-type@^1.0.5`
* deps: `finalhandler@^2.1.0`
* deps: `qs@^6.14.0`
* deps: `server-static@2.2.0`
* deps: `type-is@2.0.1`
5.0.1 / 2024-10-08
4.21.2 / 2024-11-06
==========
* Update `cookie` semver lock to address [CVE-2024-47764](https://nvd.nist.gov/vuln/detail/CVE-2024-47764)
* deps: path-to-regexp@0.1.12
- Fix backtracking protection
* deps: path-to-regexp@0.1.11
- Throws an error on invalid path values
5.0.0 / 2024-09-10
=========================
* remove:
- `path-is-absolute` dependency - use `path.isAbsolute` instead
* breaking:
* `res.status()` accepts only integers, and input must be greater than 99 and less than 1000
* will throw a `RangeError: Invalid status code: ${code}. Status code must be greater than 99 and less than 1000.` for inputs outside this range
* will throw a `TypeError: Invalid status code: ${code}. Status code must be an integer.` for non integer inputs
* deps: send@1.0.0
* `res.redirect('back')` and `res.location('back')` is no longer a supported magic string, explicitly use `req.get('Referrer') || '/'`.
* change:
- `res.clearCookie` will ignore user provided `maxAge` and `expires` options
* deps: cookie-signature@^1.2.1
* deps: debug@4.3.6
* deps: merge-descriptors@^2.0.0
* deps: serve-static@^2.1.0
* deps: qs@6.13.0
* deps: accepts@^2.0.0
* deps: mime-types@^3.0.0
- `application/javascript` => `text/javascript`
* deps: type-is@^2.0.0
* deps: content-disposition@^1.0.0
* deps: finalhandler@^2.0.0
* deps: fresh@^2.0.0
* deps: body-parser@^2.0.1
* deps: send@^1.1.0
4.21.1 / 2024-10-08
==========
5.0.0-beta.3 / 2024-03-25
=========================
* Backported a fix for [CVE-2024-47764](https://nvd.nist.gov/vuln/detail/CVE-2024-47764)
This incorporates all changes after 4.19.1 up to 4.19.2.
5.0.0-beta.2 / 2024-03-20
=========================
4.21.0 / 2024-09-11
==========
This incorporates all changes after 4.17.2 up to 4.19.1.
5.0.0-beta.1 / 2022-02-14
=========================
This is the first Express 5.0 beta release, based off 4.17.2 and includes
changes from 5.0.0-alpha.8.
* change:
- Default "query parser" setting to `'simple'`
- Requires Node.js 4+
- Use `mime-types` for file to content type mapping
* deps: array-flatten@3.0.0
* deps: body-parser@2.0.0-beta.1
- `req.body` is no longer always initialized to `{}`
- `urlencoded` parser now defaults `extended` to `false`
- Use `on-finished` to determine when body read
* deps: router@2.0.0-beta.1
- Add new `?`, `*`, and `+` parameter modifiers
- Internalize private `router.process_params` method
- Matching group expressions are only RegExp syntax
- Named matching groups no longer available by position in `req.params`
- Regular expressions can only be used in a matching group
- Remove `debug` dependency
- Special `*` path segment behavior removed
- deps: array-flatten@3.0.0
- deps: parseurl@~1.3.3
- deps: path-to-regexp@3.2.0
- deps: setprototypeof@1.2.0
* deps: send@1.0.0-beta.1
- Change `dotfiles` option default to `'ignore'`
- Remove `hidden` option; use `dotfiles` option instead
- Use `mime-types` for file to content type mapping
- deps: debug@3.1.0
* deps: serve-static@2.0.0-beta.1
- Change `dotfiles` option default to `'ignore'`
- Remove `hidden` option; use `dotfiles` option instead
- Use `mime-types` for file to content type mapping
- Remove `express.static.mime` export; use `mime-types` package instead
- deps: send@1.0.0-beta.1
5.0.0-alpha.8 / 2020-03-25
==========================
This is the eighth Express 5.0 alpha release, based off 4.17.1 and includes
changes from 5.0.0-alpha.7.
5.0.0-alpha.7 / 2018-10-26
==========================
This is the seventh Express 5.0 alpha release, based off 4.16.4 and includes
changes from 5.0.0-alpha.6.
The major change with this alpha is the basic support for returned, rejected
Promises in the router.
* remove:
- `path-to-regexp` dependency
* deps: debug@3.1.0
- Add `DEBUG_HIDE_DATE` environment variable
- Change timer to per-namespace instead of global
- Change non-TTY date format
- Remove `DEBUG_FD` environment variable support
- Support 256 namespace colors
* deps: router@2.0.0-alpha.1
- Add basic support for returned, rejected Promises
- Fix JSDoc for `Router` constructor
- deps: debug@3.1.0
- deps: parseurl@~1.3.2
- deps: setprototypeof@1.1.0
- deps: utils-merge@1.0.1
5.0.0-alpha.6 / 2017-09-24
==========================
This is the sixth Express 5.0 alpha release, based off 4.15.5 and includes
changes from 5.0.0-alpha.5.
* remove:
- `res.redirect(url, status)` signature - use `res.redirect(status, url)`
- `res.send(status, body)` signature - use `res.status(status).send(body)`
* deps: router@~1.3.1
- deps: debug@2.6.8
5.0.0-alpha.5 / 2017-03-06
==========================
This is the fifth Express 5.0 alpha release, based off 4.15.2 and includes
changes from 5.0.0-alpha.4.
5.0.0-alpha.4 / 2017-03-01
==========================
This is the fourth Express 5.0 alpha release, based off 4.15.0 and includes
changes from 5.0.0-alpha.3.
* remove:
- Remove Express 3.x middleware error stubs
* deps: router@~1.3.0
- Add `next("router")` to exit from router
- Fix case where `router.use` skipped requests routes did not
- Skip routing when `req.url` is not set
- Use `%o` in path debug to tell types apart
- deps: debug@2.6.1
- deps: setprototypeof@1.0.3
- perf: add fast match path for `*` route
5.0.0-alpha.3 / 2017-01-28
==========================
This is the third Express 5.0 alpha release, based off 4.14.1 and includes
changes from 5.0.0-alpha.2.
* remove:
- `res.json(status, obj)` signature - use `res.status(status).json(obj)`
- `res.jsonp(status, obj)` signature - use `res.status(status).jsonp(obj)`
- `res.vary()` (no arguments) -- provide a field name as an argument
* deps: array-flatten@2.1.1
* deps: path-is-absolute@1.0.1
* deps: router@~1.1.5
- deps: array-flatten@2.0.1
- deps: methods@~1.1.2
- deps: parseurl@~1.3.1
- deps: setprototypeof@1.0.2
5.0.0-alpha.2 / 2015-07-06
==========================
This is the second Express 5.0 alpha release, based off 4.13.1 and includes
changes from 5.0.0-alpha.1.
* remove:
- `app.param(fn)`
- `req.param()` -- use `req.params`, `req.body`, or `req.query` instead
* change:
- `res.render` callback is always async, even for sync view engines
- The leading `:` character in `name` for `app.param(name, fn)` is no longer removed
- Use `router` module for routing
- Use `path-is-absolute` module for absolute path detection
5.0.0-alpha.1 / 2014-11-06
==========================
This is the first Express 5.0 alpha release, based off 4.10.1.
* remove:
- `app.del` - use `app.delete`
- `req.acceptsCharset` - use `req.acceptsCharsets`
- `req.acceptsEncoding` - use `req.acceptsEncodings`
- `req.acceptsLanguage` - use `req.acceptsLanguages`
- `res.json(obj, status)` signature - use `res.json(status, obj)`
- `res.jsonp(obj, status)` signature - use `res.jsonp(status, obj)`
- `res.send(body, status)` signature - use `res.send(status, body)`
- `res.send(status)` signature - use `res.sendStatus(status)`
- `res.sendfile` - use `res.sendFile` instead
- `express.query` middleware
* change:
- `req.host` now returns host (`hostname:port`) - use `req.hostname` for only hostname
- `req.query` is now a getter instead of a plain property
* add:
- `app.router` is a reference to the base router
* Deprecate `res.location("back")` and `res.redirect("back")` magic string
* deps: serve-static@1.16.2
* includes send@0.19.0
* deps: finalhandler@1.3.1
* deps: qs@6.13.0
4.20.0 / 2024-09-10
==========

100
api/node_modules/express/Readme.md generated vendored
View File

@@ -1,6 +1,6 @@
[![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](https://expressjs.com/)
[![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](http://expressjs.com/)
**Fast, unopinionated, minimalist web framework for [Node.js](https://nodejs.org).**
**Fast, unopinionated, minimalist web framework for [Node.js](http://nodejs.org).**
**This project has a [Code of Conduct][].**
@@ -20,16 +20,16 @@
[![NPM Version][npm-version-image]][npm-url]
[![NPM Install Size][npm-install-size-image]][npm-install-size-url]
[![NPM Downloads][npm-downloads-image]][npm-downloads-url]
[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]
```js
import express from 'express'
const express = require('express')
const app = express()
app.get('/', (req, res) => {
app.get('/', function (req, res) {
res.send('Hello World')
})
@@ -42,7 +42,7 @@ This is a [Node.js](https://nodejs.org/en/) module available through the
[npm registry](https://www.npmjs.com/).
Before installing, [download and install Node.js](https://nodejs.org/en/download/).
Node.js 18 or higher is required.
Node.js 0.10 or higher is required.
If this is a brand new project, make sure to create a `package.json` first with
the [`npm init` command](https://docs.npmjs.com/creating-a-package-json-file).
@@ -50,11 +50,11 @@ the [`npm init` command](https://docs.npmjs.com/creating-a-package-json-file).
Installation is done using the
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
```bash
npm install express
```console
$ npm install express
```
Follow [our installing guide](https://expressjs.com/en/starter/installing.html)
Follow [our installing guide](http://expressjs.com/en/starter/installing.html)
for more information.
## Features
@@ -69,11 +69,14 @@ for more information.
## Docs & Community
* [Website and Documentation](https://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]
* [#express](https://web.libera.chat/#express) on [Libera Chat](https://libera.chat) IRC
* [GitHub Organization](https://github.com/expressjs) for Official Middleware & Modules
* [Github Discussions](https://github.com/expressjs/discussions) for discussion on the development and usage of Express
* Visit the [Wiki](https://github.com/expressjs/express/wiki)
* [Google Group](https://groups.google.com/group/express-js) for discussion
* [Gitter](https://gitter.im/expressjs/express) for support and discussion
**PROTIP** Be sure to read the [migration guide to v5](https://expressjs.com/en/guide/migrating-5)
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/expressjs/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/expressjs/express/wiki/New-features-in-4.x).
## Quick Start
@@ -81,26 +84,26 @@ for more information.
Install the executable. The executable's major version will match Express's:
```bash
npm install -g express-generator@4
```console
$ npm install -g express-generator@4
```
Create the app:
```bash
express /tmp/foo && cd /tmp/foo
```console
$ express /tmp/foo && cd /tmp/foo
```
Install dependencies:
```bash
npm install
```console
$ npm install
```
Start the server:
```bash
npm start
```console
$ npm start
```
View the website at: http://localhost:3000
@@ -112,32 +115,29 @@ npm start
HTTP APIs.
Express does not force you to use any specific ORM or template engine. With support for over
14 template engines via [@ladjs/consolidate](https://github.com/ladjs/consolidate),
14 template engines via [Consolidate.js](https://github.com/tj/consolidate.js),
you can quickly craft your perfect framework.
## Examples
To view the examples, clone the Express repository:
To view the examples, clone the Express repo and install the dependencies:
```bash
git clone https://github.com/expressjs/express.git --depth 1 && cd express
```
Then install the dependencies:
```bash
npm install
```console
$ git clone https://github.com/expressjs/express.git --depth 1
$ cd express
$ npm install
```
Then run whichever example you want:
```bash
node examples/content-negotiation
```console
$ node examples/content-negotiation
```
## Contributing
[![Linux Build][github-actions-ci-image]][github-actions-ci-url]
[![Windows Build][appveyor-image]][appveyor-url]
[![Test Coverage][coveralls-image]][coveralls-url]
The Express.js project welcomes all constructive contributions. Contributions take many forms,
@@ -152,16 +152,11 @@ If you discover a security vulnerability in Express, please see [Security Polici
### Running Tests
To run the test suite, first install the dependencies:
To run the test suite, first install the dependencies, then run `npm test`:
```bash
npm install
```
Then run `npm test`:
```bash
npm test
```console
$ npm install
$ npm test
```
## People
@@ -197,16 +192,18 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj)
### Triagers
* [aravindvnair99](https://github.com/aravindvnair99) - **Aravind Nair**
* [bjohansebas](https://github.com/bjohansebas) - **Sebastian Beltran**
* [carpasse](https://github.com/carpasse) - **Carlos Serrano**
* [CBID2](https://github.com/CBID2) - **Christine Belzie**
* [dpopp07](https://github.com/dpopp07) - **Dustin Popp**
* [enyoghasim](https://github.com/enyoghasim) - **David Enyoghasim**
* [UlisesGascon](https://github.com/UlisesGascon) - **Ulises Gascón** (he/him)
* [mertcanaltin](https://github.com/mertcanaltin) - **Mert Can Altin**
* [0ss](https://github.com/0ss) - **Salah**
* [import-brain](https://github.com/import-brain) - **Eric Cheng** (he/him)
* [3imed-jaberi](https://github.com/3imed-jaberi) - **Imed Jaberi**
* [dakshkhetan](https://github.com/dakshkhetan) - **Daksh Khetan** (he/him)
* [lucasraziel](https://github.com/lucasraziel) - **Lucas Soares Do Rego**
* [IamLizu](https://github.com/IamLizu) - **S M Mahmudul Hasan** (he/him)
* [Phillip9587](https://github.com/Phillip9587) - **Phillip Barta**
* [Sushmeet](https://github.com/Sushmeet) - **Sushmeet Sunger**
* [rxmarbles](https://github.com/rxmarbles) **Rick Markins** (He/him)
<details>
<summary>Triagers emeriti members</summary>
@@ -239,13 +236,6 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj)
* [sheplu](https://github.com/sheplu) - **Jean Burellier**
* [tarunyadav1](https://github.com/tarunyadav1) - **Tarun yadav**
* [tunniclm](https://github.com/tunniclm) - **Mike Tunnicliffe**
* [enyoghasim](https://github.com/enyoghasim) - **David Enyoghasim**
* [0ss](https://github.com/0ss) - **Salah**
* [import-brain](https://github.com/import-brain) - **Eric Cheng** (he/him)
* [dakshkhetan](https://github.com/dakshkhetan) - **Daksh Khetan** (he/him)
* [lucasraziel](https://github.com/lucasraziel) - **Lucas Soares Do Rego**
* [mertcanaltin](https://github.com/mertcanaltin) - **Mert Can Altin**
</details>
@@ -253,12 +243,16 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj)
[MIT](LICENSE)
[appveyor-image]: https://badgen.net/appveyor/ci/dougwilson/express/master?label=windows
[appveyor-url]: https://ci.appveyor.com/project/dougwilson/express
[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/express/master
[coveralls-url]: https://coveralls.io/r/expressjs/express?branch=master
[github-actions-ci-image]: https://badgen.net/github/checks/expressjs/express/master?label=CI
[github-actions-ci-image]: https://badgen.net/github/checks/expressjs/express/master?label=linux
[github-actions-ci-url]: https://github.com/expressjs/express/actions/workflows/ci.yml
[npm-downloads-image]: https://badgen.net/npm/dm/express
[npm-downloads-url]: https://npmcharts.com/compare/express?minimal=true
[npm-install-size-image]: https://badgen.net/packagephobia/install/express
[npm-install-size-url]: https://packagephobia.com/result?p=express
[npm-url]: https://npmjs.org/package/express
[npm-version-image]: https://badgen.net/npm/v/express
[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/express/badge

View File

@@ -14,24 +14,29 @@
*/
var finalhandler = require('finalhandler');
var Router = require('./router');
var methods = require('methods');
var middleware = require('./middleware/init');
var query = require('./middleware/query');
var debug = require('debug')('express:application');
var View = require('./view');
var http = require('node:http');
var methods = require('./utils').methods;
var http = require('http');
var compileETag = require('./utils').compileETag;
var compileQueryParser = require('./utils').compileQueryParser;
var compileTrust = require('./utils').compileTrust;
var resolve = require('node:path').resolve;
var once = require('once')
var Router = require('router');
var deprecate = require('depd')('express');
var flatten = require('array-flatten');
var merge = require('utils-merge');
var resolve = require('path').resolve;
var setPrototypeOf = require('setprototypeof')
/**
* Module variables.
* @private
*/
var hasOwnProperty = Object.prototype.hasOwnProperty
var slice = Array.prototype.slice;
var flatten = Array.prototype.flat;
/**
* Application prototype.
@@ -57,29 +62,11 @@ var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default';
*/
app.init = function init() {
var router = null;
this.cache = Object.create(null);
this.engines = Object.create(null);
this.settings = Object.create(null);
this.cache = {};
this.engines = {};
this.settings = {};
this.defaultConfiguration();
// Setup getting to lazily add base router
Object.defineProperty(this, 'router', {
configurable: true,
enumerable: true,
get: function getrouter() {
if (router === null) {
router = new Router({
caseSensitive: this.enabled('case sensitive routing'),
strict: this.enabled('strict routing')
});
}
return router;
}
});
};
/**
@@ -94,7 +81,7 @@ app.defaultConfiguration = function defaultConfiguration() {
this.enable('x-powered-by');
this.set('etag', 'weak');
this.set('env', env);
this.set('query parser', 'simple')
this.set('query parser', 'extended');
this.set('subdomain offset', 2);
this.set('trust proxy', false);
@@ -115,10 +102,10 @@ app.defaultConfiguration = function defaultConfiguration() {
}
// inherit protos
Object.setPrototypeOf(this.request, parent.request)
Object.setPrototypeOf(this.response, parent.response)
Object.setPrototypeOf(this.engines, parent.engines)
Object.setPrototypeOf(this.settings, parent.settings)
setPrototypeOf(this.request, parent.request)
setPrototypeOf(this.response, parent.response)
setPrototypeOf(this.engines, parent.engines)
setPrototypeOf(this.settings, parent.settings)
});
// setup locals
@@ -138,6 +125,32 @@ app.defaultConfiguration = function defaultConfiguration() {
if (env === 'production') {
this.enable('view cache');
}
Object.defineProperty(this, 'router', {
get: function() {
throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.');
}
});
};
/**
* lazily adds the base router if it has not yet been added.
*
* We cannot add the base router in the defaultConfiguration because
* it reads app settings which might be set after that has run.
*
* @private
*/
app.lazyrouter = function lazyrouter() {
if (!this._router) {
this._router = new Router({
caseSensitive: this.enabled('case sensitive routing'),
strict: this.enabled('strict routing')
});
this._router.use(query(this.get('query parser fn')));
this._router.use(middleware.init(this));
}
};
/**
@@ -150,31 +163,22 @@ app.defaultConfiguration = function defaultConfiguration() {
*/
app.handle = function handle(req, res, callback) {
var router = this._router;
// final handler
var done = callback || finalhandler(req, res, {
env: this.get('env'),
onerror: logerror.bind(this)
});
// set powered by header
if (this.enabled('x-powered-by')) {
res.setHeader('X-Powered-By', 'Express');
// no routes
if (!router) {
debug('no routes defined on app');
done();
return;
}
// set circular references
req.res = res;
res.req = req;
// alter the prototypes
Object.setPrototypeOf(req, this.request)
Object.setPrototypeOf(res, this.response)
// setup locals
if (!res.locals) {
res.locals = Object.create(null);
}
this.router.handle(req, res, done);
router.handle(req, res, done);
};
/**
@@ -207,14 +211,15 @@ app.use = function use(fn) {
}
}
var fns = flatten.call(slice.call(arguments, offset), Infinity);
var fns = flatten(slice.call(arguments, offset));
if (fns.length === 0) {
throw new TypeError('app.use() requires a middleware function')
}
// get router
var router = this.router;
// setup router
this.lazyrouter();
var router = this._router;
fns.forEach(function (fn) {
// non-express app
@@ -230,8 +235,8 @@ app.use = function use(fn) {
router.use(path, function mounted_app(req, res, next) {
var orig = req.app;
fn.handle(req, res, function (err) {
Object.setPrototypeOf(req, orig.request)
Object.setPrototypeOf(res, orig.response)
setPrototypeOf(req, orig.request)
setPrototypeOf(res, orig.response)
next(err);
});
});
@@ -254,7 +259,8 @@ app.use = function use(fn) {
*/
app.route = function route(path) {
return this.router.route(path);
this.lazyrouter();
return this._router.route(path);
};
/**
@@ -320,6 +326,8 @@ app.engine = function engine(ext, fn) {
*/
app.param = function param(name, fn) {
this.lazyrouter();
if (Array.isArray(name)) {
for (var i = 0; i < name.length; i++) {
this.param(name[i], fn);
@@ -328,7 +336,7 @@ app.param = function param(name, fn) {
return this;
}
this.router.param(name, fn);
this._router.param(name, fn);
return this;
};
@@ -351,7 +359,17 @@ app.param = function param(name, fn) {
app.set = function set(setting, val) {
if (arguments.length === 1) {
// app.get(setting)
return this.settings[setting];
var settings = this.settings
while (settings && settings !== Object.prototype) {
if (hasOwnProperty.call(settings, setting)) {
return settings[setting]
}
settings = Object.getPrototypeOf(settings)
}
return undefined
}
debug('set "%s" to %o', setting, val);
@@ -468,14 +486,16 @@ app.disable = function disable(setting) {
* Delegate `.VERB(...)` calls to `router.VERB(...)`.
*/
methods.forEach(function (method) {
app[method] = function (path) {
methods.forEach(function(method){
app[method] = function(path){
if (method === 'get' && arguments.length === 1) {
// app.get(setting)
return this.set(path);
}
var route = this.route(path);
this.lazyrouter();
var route = this._router.route(path);
route[method].apply(route, slice.call(arguments, 1));
return this;
};
@@ -492,7 +512,9 @@ methods.forEach(function (method) {
*/
app.all = function all(path) {
var route = this.route(path);
this.lazyrouter();
var route = this._router.route(path);
var args = slice.call(arguments, 1);
for (var i = 0; i < methods.length; i++) {
@@ -502,6 +524,10 @@ app.all = function all(path) {
return this;
};
// del -> delete alias
app.del = deprecate.function(app.delete, 'app.del: Use app.delete instead');
/**
* Render the given view `name` name with `options`
* and a callback accepting an error and the
@@ -524,6 +550,7 @@ app.render = function render(name, options, callback) {
var done = callback;
var engines = this.engines;
var opts = options;
var renderOptions = {};
var view;
// support callback function as second arg
@@ -532,8 +559,16 @@ app.render = function render(name, options, callback) {
opts = {};
}
// merge app.locals
merge(renderOptions, this.locals);
// merge options._locals
if (opts._locals) {
merge(renderOptions, opts._locals);
}
// merge options
var renderOptions = { ...this.locals, ...opts._locals, ...opts };
merge(renderOptions, opts);
// set .cache unless explicitly provided
if (renderOptions.cache == null) {
@@ -583,8 +618,8 @@ app.render = function render(name, options, callback) {
* and HTTPS server you may do so with the "http"
* and "https" modules as shown here:
*
* var http = require('node:http')
* , https = require('node:https')
* var http = require('http')
* , https = require('https')
* , express = require('express')
* , app = express();
*
@@ -596,14 +631,9 @@ app.render = function render(name, options, callback) {
*/
app.listen = function listen() {
var server = http.createServer(this)
var args = Array.prototype.slice.call(arguments)
if (typeof args[args.length - 1] === 'function') {
var done = args[args.length - 1] = once(args[args.length - 1])
server.once('error', done)
}
return server.listen.apply(server, args)
}
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
/**
* Log error using console.error.

View File

@@ -13,10 +13,11 @@
*/
var bodyParser = require('body-parser')
var EventEmitter = require('node:events').EventEmitter;
var EventEmitter = require('events').EventEmitter;
var mixin = require('merge-descriptors');
var proto = require('./application');
var Router = require('router');
var Route = require('./router/route');
var Router = require('./router');
var req = require('./request');
var res = require('./response');
@@ -67,7 +68,7 @@ exports.response = res;
* Expose constructors.
*/
exports.Route = Router.Route;
exports.Route = Route;
exports.Router = Router;
/**
@@ -75,7 +76,41 @@ exports.Router = Router;
*/
exports.json = bodyParser.json
exports.query = require('./middleware/query');
exports.raw = bodyParser.raw
exports.static = require('serve-static');
exports.text = bodyParser.text
exports.urlencoded = bodyParser.urlencoded
/**
* Replace removed middleware with an appropriate error message.
*/
var removedMiddlewares = [
'bodyParser',
'compress',
'cookieSession',
'session',
'logger',
'cookieParser',
'favicon',
'responseTime',
'errorHandler',
'timeout',
'methodOverride',
'vhost',
'csrf',
'directory',
'limit',
'multipart',
'staticCache'
]
removedMiddlewares.forEach(function (name) {
Object.defineProperty(exports, name, {
get: function () {
throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.');
},
configurable: true
});
});

View File

@@ -14,9 +14,10 @@
*/
var accepts = require('accepts');
var isIP = require('node:net').isIP;
var deprecate = require('depd')('express');
var isIP = require('net').isIP;
var typeis = require('type-is');
var http = require('node:http');
var http = require('http');
var fresh = require('fresh');
var parseRange = require('range-parser');
var parse = require('parseurl');
@@ -146,6 +147,9 @@ req.acceptsEncodings = function(){
return accept.encodings.apply(accept, arguments);
};
req.acceptsEncoding = deprecate.function(req.acceptsEncodings,
'req.acceptsEncoding: Use acceptsEncodings instead');
/**
* Check if the given `charset`s are acceptable,
* otherwise you should respond with 406 "Not Acceptable".
@@ -160,6 +164,9 @@ req.acceptsCharsets = function(){
return accept.charsets.apply(accept, arguments);
};
req.acceptsCharset = deprecate.function(req.acceptsCharsets,
'req.acceptsCharset: Use acceptsCharsets instead');
/**
* Check if the given `lang`s are acceptable,
* otherwise you should respond with 406 "Not Acceptable".
@@ -174,6 +181,9 @@ req.acceptsLanguages = function(){
return accept.languages.apply(accept, arguments);
};
req.acceptsLanguage = deprecate.function(req.acceptsLanguages,
'req.acceptsLanguage: Use acceptsLanguages instead');
/**
* Parse Range header field, capping to the given `size`.
*
@@ -206,27 +216,38 @@ req.range = function range(size, options) {
};
/**
* Parse the query string of `req.url`.
* Return the value of param `name` when present or `defaultValue`.
*
* This uses the "query parser" setting to parse the raw
* string into an object.
* - Checks route placeholders, ex: _/user/:id_
* - Checks body params, ex: id=12, {"id":12}
* - Checks query string params, ex: ?id=12
*
* To utilize request bodies, `req.body`
* should be an object. This can be done by using
* the `bodyParser()` middleware.
*
* @param {String} name
* @param {Mixed} [defaultValue]
* @return {String}
* @api public
* @public
*/
defineGetter(req, 'query', function query(){
var queryparse = this.app.get('query parser fn');
req.param = function param(name, defaultValue) {
var params = this.params || {};
var body = this.body || {};
var query = this.query || {};
if (!queryparse) {
// parsing is disabled
return Object.create(null);
}
var args = arguments.length === 1
? 'name'
: 'name, default';
deprecate('req.param(' + args + '): Use req.params, req.body, or req.query instead');
var querystring = parse(this).query;
if (null != params[name] && params.hasOwnProperty(name)) return params[name];
if (null != body[name]) return body[name];
if (null != query[name]) return query[name];
return queryparse(querystring);
});
return defaultValue;
};
/**
* Check if the incoming request contains the "Content-Type"
@@ -393,7 +414,7 @@ defineGetter(req, 'path', function path() {
});
/**
* Parse the "Host" header field to a host.
* Parse the "Host" header field to a hostname.
*
* When the "trust proxy" setting trusts the socket
* address, the "X-Forwarded-Host" header field will
@@ -403,35 +424,18 @@ defineGetter(req, 'path', function path() {
* @public
*/
defineGetter(req, 'host', function host(){
defineGetter(req, 'hostname', function hostname(){
var trust = this.app.get('trust proxy fn');
var val = this.get('X-Forwarded-Host');
var host = this.get('X-Forwarded-Host');
if (!val || !trust(this.connection.remoteAddress, 0)) {
val = this.get('Host');
} else if (val.indexOf(',') !== -1) {
if (!host || !trust(this.connection.remoteAddress, 0)) {
host = this.get('Host');
} else if (host.indexOf(',') !== -1) {
// Note: X-Forwarded-Host is normally only ever a
// single value, but this is to be safe.
val = val.substring(0, val.indexOf(',')).trimRight()
host = host.substring(0, host.indexOf(',')).trimRight()
}
return val || undefined;
});
/**
* Parse the "Host" header field to a hostname.
*
* When the "trust proxy" setting trusts the socket
* address, the "X-Forwarded-Host" header field will
* be trusted.
*
* @return {String}
* @api public
*/
defineGetter(req, 'hostname', function hostname(){
var host = this.host;
if (!host) return;
// IPv6 literal support
@@ -445,9 +449,15 @@ defineGetter(req, 'hostname', function hostname(){
: host;
});
// TODO: change req.host to return host in next major
defineGetter(req, 'host', deprecate.function(function host(){
return this.hostname;
}, 'req.host: Use req.hostname instead'));
/**
* Check if the request is fresh, aka
* Last-Modified or the ETag
* Last-Modified and/or the ETag
* still match.
*
* @return {Boolean}

View File

@@ -12,16 +12,18 @@
* @private
*/
var Buffer = require('safe-buffer').Buffer
var contentDisposition = require('content-disposition');
var createError = require('http-errors')
var deprecate = require('depd')('express');
var encodeUrl = require('encodeurl');
var escapeHtml = require('escape-html');
var http = require('node:http');
var http = require('http');
var isAbsolute = require('./utils').isAbsolute;
var onFinished = require('on-finished');
var mime = require('mime-types')
var path = require('node:path');
var pathIsAbsolute = require('node:path').isAbsolute;
var path = require('path');
var statuses = require('statuses')
var merge = require('utils-merge');
var sign = require('cookie-signature').sign;
var normalizeType = require('./utils').normalizeType;
var normalizeTypes = require('./utils').normalizeTypes;
@@ -29,6 +31,7 @@ var setCharset = require('./utils').setCharset;
var cookie = require('cookie');
var send = require('send');
var extname = path.extname;
var mime = send.mime;
var resolve = path.resolve;
var vary = require('vary');
@@ -47,28 +50,24 @@ var res = Object.create(http.ServerResponse.prototype)
module.exports = res
/**
* Set the HTTP status code for the response.
* Module variables.
* @private
*/
var charsetRegExp = /;\s*charset\s*=/;
/**
* Set status `code`.
*
* Expects an integer value between 100 and 999 inclusive.
* Throws an error if the provided status code is not an integer or if it's outside the allowable range.
*
* @param {number} code - The HTTP status code to set.
* @return {ServerResponse} - Returns itself for chaining methods.
* @throws {TypeError} If `code` is not an integer.
* @throws {RangeError} If `code` is outside the range 100 to 999.
* @param {Number} code
* @return {ServerResponse}
* @public
*/
res.status = function status(code) {
// Check if the status code is not an integer
if (!Number.isInteger(code)) {
throw new TypeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be an integer.`);
if ((typeof code === 'string' || Math.floor(code) !== code) && code > 99 && code < 1000) {
deprecate('res.status(' + JSON.stringify(code) + '): use res.status(' + Math.floor(code) + ') instead')
}
// Check if the status code is outside of Node's valid range
if (code < 100 || code > 999) {
throw new RangeError(`Invalid status code: ${JSON.stringify(code)}. Status code must be greater than 99 and less than 1000.`);
}
this.statusCode = code;
return this;
};
@@ -80,11 +79,7 @@ res.status = function status(code) {
*
* res.links({
* next: 'http://api.example.com/users?page=2',
* last: 'http://api.example.com/users?page=5',
* pages: [
* 'http://api.example.com/users?page=1',
* 'http://api.example.com/users?page=2'
* ]
* last: 'http://api.example.com/users?page=5'
* });
*
* @param {Object} links
@@ -92,18 +87,11 @@ res.status = function status(code) {
* @public
*/
res.links = function(links) {
res.links = function(links){
var link = this.get('Link') || '';
if (link) link += ', ';
return this.set('Link', link + Object.keys(links).map(function(rel) {
// Allow multiple links if links[rel] is an array
if (Array.isArray(links[rel])) {
return links[rel].map(function (singleLink) {
return `<${singleLink}>; rel="${rel}"`;
}).join(', ');
} else {
return `<${links[rel]}>; rel="${rel}"`;
}
return this.set('Link', link + Object.keys(links).map(function(rel){
return '<' + links[rel] + '>; rel="' + rel + '"';
}).join(', '));
};
@@ -129,6 +117,31 @@ res.send = function send(body) {
// settings
var app = this.app;
// allow status / body
if (arguments.length === 2) {
// res.send(body, status) backwards compat
if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') {
deprecate('res.send(body, status): Use res.status(status).send(body) instead');
this.statusCode = arguments[1];
} else {
deprecate('res.send(status, body): Use res.status(status).send(body) instead');
this.statusCode = arguments[0];
chunk = arguments[1];
}
}
// disambiguate res.send(status) and res.send(status, num)
if (typeof chunk === 'number' && arguments.length === 1) {
// res.send(status) will set status message as text string
if (!this.get('Content-Type')) {
this.type('txt');
}
deprecate('res.send(status): Use res.sendStatus(status) instead');
this.statusCode = chunk;
chunk = statuses.message[chunk]
}
switch (typeof chunk) {
// string defaulting to html
case 'string':
@@ -141,7 +154,7 @@ res.send = function send(body) {
case 'object':
if (chunk === null) {
chunk = '';
} else if (ArrayBuffer.isView(chunk)) {
} else if (Buffer.isBuffer(chunk)) {
if (!this.get('Content-Type')) {
this.type('bin');
}
@@ -194,7 +207,7 @@ res.send = function send(body) {
}
// freshness
if (req.fresh) this.status(304);
if (req.fresh) this.statusCode = 304;
// strip irrelevant headers
if (204 === this.statusCode || 304 === this.statusCode) {
@@ -235,12 +248,27 @@ res.send = function send(body) {
*/
res.json = function json(obj) {
var val = obj;
// allow status / body
if (arguments.length === 2) {
// res.json(body, status) backwards compat
if (typeof arguments[1] === 'number') {
deprecate('res.json(obj, status): Use res.status(status).json(obj) instead');
this.statusCode = arguments[1];
} else {
deprecate('res.json(status, obj): Use res.status(status).json(obj) instead');
this.statusCode = arguments[0];
val = arguments[1];
}
}
// settings
var app = this.app;
var escape = app.get('json escape')
var replacer = app.get('json replacer');
var spaces = app.get('json spaces');
var body = stringify(obj, replacer, spaces, escape)
var body = stringify(val, replacer, spaces, escape)
// content-type
if (!this.get('Content-Type')) {
@@ -263,12 +291,27 @@ res.json = function json(obj) {
*/
res.jsonp = function jsonp(obj) {
var val = obj;
// allow status / body
if (arguments.length === 2) {
// res.jsonp(body, status) backwards compat
if (typeof arguments[1] === 'number') {
deprecate('res.jsonp(obj, status): Use res.status(status).jsonp(obj) instead');
this.statusCode = arguments[1];
} else {
deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead');
this.statusCode = arguments[0];
val = arguments[1];
}
}
// settings
var app = this.app;
var escape = app.get('json escape')
var replacer = app.get('json replacer');
var spaces = app.get('json spaces');
var body = stringify(obj, replacer, spaces, escape)
var body = stringify(val, replacer, spaces, escape)
var callback = this.req.query[app.get('jsonp callback name')];
// content-type
@@ -326,7 +369,7 @@ res.jsonp = function jsonp(obj) {
res.sendStatus = function sendStatus(statusCode) {
var body = statuses.message[statusCode] || String(statusCode)
this.status(statusCode);
this.statusCode = statusCode;
this.type('txt');
return this.send(body);
@@ -394,15 +437,12 @@ res.sendFile = function sendFile(path, options, callback) {
opts = {};
}
if (!opts.root && !pathIsAbsolute(path)) {
if (!opts.root && !isAbsolute(path)) {
throw new TypeError('path must be absolute or specify root to res.sendFile');
}
// create file stream
var pathname = encodeURI(path);
// wire application etag option to send
opts.etag = this.app.enabled('etag');
var file = send(req, pathname, opts);
// transfer
@@ -417,6 +457,78 @@ res.sendFile = function sendFile(path, options, callback) {
});
};
/**
* Transfer the file at the given `path`.
*
* Automatically sets the _Content-Type_ response header field.
* The callback `callback(err)` is invoked when the transfer is complete
* or when an error occurs. Be sure to check `res.headersSent`
* if you wish to attempt responding, as the header and some data
* may have already been transferred.
*
* Options:
*
* - `maxAge` defaulting to 0 (can be string converted by `ms`)
* - `root` root directory for relative filenames
* - `headers` object of headers to serve with file
* - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them
*
* Other options are passed along to `send`.
*
* Examples:
*
* The following example illustrates how `res.sendfile()` may
* be used as an alternative for the `static()` middleware for
* dynamic situations. The code backing `res.sendfile()` is actually
* the same code, so HTTP cache support etc is identical.
*
* app.get('/user/:uid/photos/:file', function(req, res){
* var uid = req.params.uid
* , file = req.params.file;
*
* req.user.mayViewFilesFrom(uid, function(yes){
* if (yes) {
* res.sendfile('/uploads/' + uid + '/' + file);
* } else {
* res.send(403, 'Sorry! you cant see that.');
* }
* });
* });
*
* @public
*/
res.sendfile = function (path, options, callback) {
var done = callback;
var req = this.req;
var res = this;
var next = req.next;
var opts = options || {};
// support function as second arg
if (typeof options === 'function') {
done = options;
opts = {};
}
// create file stream
var file = send(req, path, opts);
// transfer
sendfile(res, file, opts, function (err) {
if (done) return done(err);
if (err && err.code === 'EISDIR') return next();
// next() all but write errors
if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {
next(err);
}
});
};
res.sendfile = deprecate.function(res.sendfile,
'res.sendfile: Use res.sendFile instead');
/**
* Transfer the file at the given `path` as an attachment.
*
@@ -487,10 +599,8 @@ res.download = function download (path, filename, options, callback) {
};
/**
* Set _Content-Type_ response header with `type` through `mime.contentType()`
* Set _Content-Type_ response header with `type` through `mime.lookup()`
* when it does not contain "/", or set the Content-Type to `type` otherwise.
* When no mapping is found though `mime.contentType()`, the type is set to
* "application/octet-stream".
*
* Examples:
*
@@ -508,7 +618,7 @@ res.download = function download (path, filename, options, callback) {
res.contentType =
res.type = function contentType(type) {
var ct = type.indexOf('/') === -1
? (mime.contentType(type) || 'application/octet-stream')
? mime.lookup(type)
: type;
return this.set('Content-Type', ct);
@@ -657,9 +767,6 @@ res.append = function append(field, val) {
*
* Aliased as `res.header()`.
*
* When the set header is "Content-Type", the type is expanded to include
* the charset if not present using `mime.contentType()`.
*
* @param {String|Object} field
* @param {String|Array} val
* @return {ServerResponse} for chaining
@@ -678,7 +785,10 @@ res.header = function header(field, val) {
if (Array.isArray(value)) {
throw new TypeError('Content-Type cannot be set to an Array');
}
value = mime.contentType(value)
if (!charsetRegExp.test(value)) {
var charset = mime.charsets.lookup(value.split(';')[0]);
if (charset) value += '; charset=' + charset.toLowerCase();
}
}
this.setHeader(field, value);
@@ -712,10 +822,15 @@ res.get = function(field){
*/
res.clearCookie = function clearCookie(name, options) {
// Force cookie expiration by setting expires to the past
const opts = { path: '/', ...options, expires: new Date(1)};
// ensure maxAge is not passed
delete opts.maxAge
if (options) {
if (options.maxAge) {
deprecate('res.clearCookie: Passing "options.maxAge" is deprecated. In v5.0.0 of Express, this option will be ignored, as res.clearCookie will automatically set cookies to expire immediately. Please update your code to omit this option.');
}
if (options.expires) {
deprecate('res.clearCookie: Passing "options.expires" is deprecated. In v5.0.0 of Express, this option will be ignored, as res.clearCookie will automatically set cookies to expire immediately. Please update your code to omit this option.');
}
}
var opts = merge({ expires: new Date(1), path: '/' }, options);
return this.cookie(name, '', opts);
};
@@ -745,7 +860,7 @@ res.clearCookie = function clearCookie(name, options) {
*/
res.cookie = function (name, value, options) {
var opts = { ...options };
var opts = merge({}, options);
var secret = this.req.secret;
var signed = opts.signed;
@@ -797,13 +912,27 @@ res.cookie = function (name, value, options) {
*/
res.location = function location(url) {
return this.set('Location', encodeUrl(url));
var loc;
// "back" is an alias for the referrer
if (url === 'back') {
deprecate('res.location("back"): use res.location(req.get("Referrer") || "/") and refer to https://dub.sh/security-redirect for best practices');
loc = this.req.get('Referrer') || '/';
} else {
loc = String(url);
}
return this.set('Location', encodeUrl(loc));
};
/**
* Redirect to the given `url` with optional response `status`
* defaulting to 302.
*
* The resulting `url` is determined by `res.location()`, so
* it will play nicely with mounted apps, relative paths,
* `"back"` etc.
*
* Examples:
*
* res.redirect('/foo/bar');
@@ -821,8 +950,13 @@ res.redirect = function redirect(url) {
// allow status / url
if (arguments.length === 2) {
status = arguments[0]
address = arguments[1]
if (typeof arguments[0] === 'number') {
status = arguments[0];
address = arguments[1];
} else {
deprecate('res.redirect(url, status): Use res.redirect(status, url) instead');
status = arguments[1];
}
}
// Set location header
@@ -845,7 +979,7 @@ res.redirect = function redirect(url) {
});
// Respond
this.status(status);
this.statusCode = status;
this.set('Content-Length', Buffer.byteLength(body));
if (this.req.method === 'HEAD') {
@@ -865,6 +999,12 @@ res.redirect = function redirect(url) {
*/
res.vary = function(field){
// checks for back-compat
if (!field || (Array.isArray(field) && !field.length)) {
deprecate('res.vary(): Provide a field name');
return this;
}
vary(this, field);
return this;

104
api/node_modules/express/lib/utils.js generated vendored
View File

@@ -12,20 +12,17 @@
* @api private
*/
var { METHODS } = require('node:http');
var Buffer = require('safe-buffer').Buffer
var contentDisposition = require('content-disposition');
var contentType = require('content-type');
var deprecate = require('depd')('express');
var flatten = require('array-flatten');
var mime = require('send').mime;
var etag = require('etag');
var mime = require('mime-types')
var proxyaddr = require('proxy-addr');
var qs = require('qs');
var querystring = require('querystring');
/**
* A list of lowercased HTTP methods that are supported by Node.js.
* @api private
*/
exports.methods = METHODS.map((method) => method.toLowerCase());
/**
* Return strong ETag for `body`.
*
@@ -48,6 +45,31 @@ exports.etag = createETagGenerator({ weak: false })
exports.wetag = createETagGenerator({ weak: true })
/**
* Check if `path` looks absolute.
*
* @param {String} path
* @return {Boolean}
* @api private
*/
exports.isAbsolute = function(path){
if ('/' === path[0]) return true;
if (':' === path[1] && ('\\' === path[2] || '/' === path[2])) return true; // Windows device path
if ('\\\\' === path.substring(0, 2)) return true; // Microsoft Azure absolute path
};
/**
* Flatten the given `arr`.
*
* @param {Array} arr
* @return {Array}
* @api private
*/
exports.flatten = deprecate.function(flatten,
'utils.flatten: use array-flatten npm module instead');
/**
* Normalize the given `type`, for example "html" becomes "text/html".
*
@@ -59,7 +81,7 @@ exports.wetag = createETagGenerator({ weak: true })
exports.normalizeType = function(type){
return ~type.indexOf('/')
? acceptParams(type)
: { value: (mime.lookup(type) || 'application/octet-stream'), params: {} }
: { value: mime.lookup(type), params: {} };
};
/**
@@ -70,10 +92,27 @@ exports.normalizeType = function(type){
* @api private
*/
exports.normalizeTypes = function(types) {
return types.map(exports.normalizeType);
exports.normalizeTypes = function(types){
var ret = [];
for (var i = 0; i < types.length; ++i) {
ret.push(exports.normalizeType(types[i]));
}
return ret;
};
/**
* Generate Content-Disposition header appropriate for the filename.
* non-ascii filenames are urlencoded and a filename* parameter is added
*
* @param {String} filename
* @return {String}
* @api private
*/
exports.contentDisposition = deprecate.function(contentDisposition,
'utils.contentDisposition: use content-disposition npm module instead');
/**
* Parse accept params `str` returning an
@@ -85,33 +124,16 @@ exports.normalizeTypes = function(types) {
*/
function acceptParams (str) {
var length = str.length;
var colonIndex = str.indexOf(';');
var index = colonIndex === -1 ? length : colonIndex;
var ret = { value: str.slice(0, index).trim(), quality: 1, params: {} };
var parts = str.split(/ *; */);
var ret = { value: parts[0], quality: 1, params: {} }
while (index < length) {
var splitIndex = str.indexOf('=', index);
if (splitIndex === -1) break;
var colonIndex = str.indexOf(';', index);
var endIndex = colonIndex === -1 ? length : colonIndex;
if (splitIndex > endIndex) {
index = str.lastIndexOf(';', splitIndex - 1) + 1;
continue;
}
var key = str.slice(index, splitIndex).trim();
var value = str.slice(splitIndex + 1, endIndex).trim();
if (key === 'q') {
ret.quality = parseFloat(value);
for (var i = 1; i < parts.length; ++i) {
var pms = parts[i].split(/ *= */);
if ('q' === pms[0]) {
ret.quality = parseFloat(pms[1]);
} else {
ret.params[key] = value;
ret.params[pms[0]] = pms[1];
}
index = endIndex + 1;
}
return ret;
@@ -170,6 +192,7 @@ exports.compileQueryParser = function compileQueryParser(val) {
fn = querystring.parse;
break;
case false:
fn = newObject;
break;
case 'extended':
fn = parseExtendedQueryString;
@@ -267,3 +290,14 @@ function parseExtendedQueryString(str) {
allowPrototypes: true
});
}
/**
* Return new empty object.
*
* @return {Object}
* @api private
*/
function newObject() {
return {};
}

29
api/node_modules/express/lib/view.js generated vendored
View File

@@ -14,8 +14,8 @@
*/
var debug = require('debug')('express:view');
var path = require('node:path');
var fs = require('node:fs');
var path = require('path');
var fs = require('fs');
/**
* Module variables.
@@ -131,31 +131,8 @@ View.prototype.lookup = function lookup(name) {
*/
View.prototype.render = function render(options, callback) {
var sync = true;
debug('render "%s"', this.path);
// render, normalizing sync callbacks
this.engine(this.path, options, function onRender() {
if (!sync) {
return callback.apply(this, arguments);
}
// copy arguments
var args = new Array(arguments.length);
var cntx = this;
for (var i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
// force callback to be async
return process.nextTick(function renderTick() {
return callback.apply(cntx, args);
});
});
sync = false;
this.engine(this.path, options, callback);
};
/**

View File

@@ -1,7 +1,7 @@
{
"name": "express",
"description": "Fast, unopinionated, minimalist web framework",
"version": "5.1.0",
"version": "4.21.2",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
"Aaron Heckmann <aaron.heckmann+github@gmail.com>",
@@ -14,7 +14,7 @@
],
"license": "MIT",
"repository": "expressjs/express",
"homepage": "https://expressjs.com/",
"homepage": "http://expressjs.com/",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
@@ -32,54 +32,58 @@
"api"
],
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.0",
"content-disposition": "^1.0.0",
"content-type": "^1.0.5",
"cookie": "^0.7.1",
"cookie-signature": "^1.2.1",
"debug": "^4.4.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"finalhandler": "^2.1.0",
"fresh": "^2.0.0",
"http-errors": "^2.0.0",
"merge-descriptors": "^2.0.0",
"mime-types": "^3.0.0",
"on-finished": "^2.4.1",
"once": "^1.4.0",
"parseurl": "^1.3.3",
"proxy-addr": "^2.0.7",
"qs": "^6.14.0",
"range-parser": "^1.2.1",
"router": "^2.2.0",
"send": "^1.1.0",
"serve-static": "^2.2.0",
"statuses": "^2.0.1",
"type-is": "^2.0.1",
"vary": "^1.1.2"
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.7.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.19.0",
"serve-static": "1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"devDependencies": {
"after": "0.8.2",
"connect-redis": "^8.0.1",
"cookie-parser": "1.4.7",
"cookie-session": "2.1.0",
"ejs": "^3.1.10",
"connect-redis": "3.4.2",
"cookie-parser": "1.4.6",
"cookie-session": "2.0.0",
"ejs": "3.1.9",
"eslint": "8.47.0",
"express-session": "^1.18.1",
"express-session": "1.17.2",
"hbs": "4.2.0",
"marked": "^15.0.3",
"marked": "0.7.0",
"method-override": "3.0.0",
"mocha": "^10.7.3",
"mocha": "10.2.0",
"morgan": "1.10.0",
"nyc": "^17.1.0",
"nyc": "15.1.0",
"pbkdf2-password": "1.2.1",
"supertest": "^6.3.0",
"supertest": "6.3.0",
"vhost": "~3.0.2"
},
"engines": {
"node": ">= 18"
"node": ">= 0.10.0"
},
"files": [
"LICENSE",
@@ -90,7 +94,7 @@
],
"scripts": {
"lint": "eslint .",
"test": "mocha --require test/support/env --reporter spec --check-leaks test/ test/acceptance/",
"test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
"test-ci": "nyc --exclude examples --exclude test --exclude benchmarks --reporter=lcovonly --reporter=text npm test",
"test-cov": "nyc --exclude examples --exclude test --exclude benchmarks --reporter=html --reporter=text npm test",
"test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"

View File

@@ -1,26 +1,3 @@
v2.1.0 / 2025-03-05
==================
* deps:
* use caret notation for dependency versions
* encodeurl@^2.0.0
* debug@^4.4.0
* remove `ServerResponse.headersSent` support check
* remove setImmediate support check
* update test dependencies
* remove unnecessary devDependency `safe-buffer`
* remove `unpipe` package and use native `unpipe()` method
* remove unnecessary devDependency `readable-stream`
* refactor: use object spread to copy error headers
* refactor: use replaceAll instead of replace with a regex
* refactor: replace setHeaders function with optimized inline header setting
v2.0.0 / 2024-09-02
==================
* drop support for node <18
* ignore status message for HTTP/2 (#53)
v1.3.1 / 2024-09-11
==================

View File

@@ -17,12 +17,20 @@ var escapeHtml = require('escape-html')
var onFinished = require('on-finished')
var parseUrl = require('parseurl')
var statuses = require('statuses')
var unpipe = require('unpipe')
/**
* Module variables.
* @private
*/
var DOUBLE_SPACE_REGEXP = /\x20{2}/g
var NEWLINE_REGEXP = /\n/g
/* istanbul ignore next */
var defer = typeof setImmediate === 'function'
? setImmediate
: function (fn) { process.nextTick(fn.bind.apply(fn, arguments)) }
var isFinished = onFinished.isFinished
/**
@@ -34,8 +42,8 @@ var isFinished = onFinished.isFinished
function createHtmlDocument (message) {
var body = escapeHtml(message)
.replaceAll('\n', '<br>')
.replaceAll(' ', ' &nbsp;')
.replace(NEWLINE_REGEXP, '<br>')
.replace(DOUBLE_SPACE_REGEXP, ' &nbsp;')
return '<!DOCTYPE html>\n' +
'<html lang="en">\n' +
@@ -81,7 +89,7 @@ function finalhandler (req, res, options) {
var status
// ignore 404 on in-flight response
if (!err && res.headersSent) {
if (!err && headersSent(res)) {
debug('cannot 404 after headers sent')
return
}
@@ -111,11 +119,11 @@ function finalhandler (req, res, options) {
// schedule onerror callback
if (err && onerror) {
setImmediate(onerror, err, req, res)
defer(onerror, err, req, res)
}
// cannot actually respond
if (res.headersSent) {
if (headersSent(res)) {
debug('cannot %d after headers sent', status)
if (req.socket) {
req.socket.destroy()
@@ -141,7 +149,15 @@ function getErrorHeaders (err) {
return undefined
}
return { ...err.headers }
var headers = Object.create(null)
var keys = Object.keys(err.headers)
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
headers[key] = err.headers[key]
}
return headers
}
/**
@@ -230,6 +246,20 @@ function getResponseStatusCode (res) {
return status
}
/**
* Determine if the response headers have been sent.
*
* @param {object} res
* @returns {boolean}
* @private
*/
function headersSent (res) {
return typeof res.headersSent !== 'boolean'
? Boolean(res._header)
: res.headersSent
}
/**
* Send response.
*
@@ -259,9 +289,7 @@ function send (req, res, status, headers, message) {
res.removeHeader('Content-Range')
// response headers
for (const [key, value] of Object.entries(headers ?? {})) {
res.setHeader(key, value)
}
setHeaders(res, headers)
// security headers
res.setHeader('Content-Security-Policy', "default-src 'none'")
@@ -285,9 +313,29 @@ function send (req, res, status, headers, message) {
}
// unpipe everything from the request
req.unpipe()
unpipe(req)
// flush the request
onFinished(req, write)
req.resume()
}
/**
* Set response headers from an object.
*
* @param {OutgoingMessage} res
* @param {object} headers
* @private
*/
function setHeaders (res, headers) {
if (!headers) {
return
}
var keys = Object.keys(headers)
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
res.setHeader(key, headers[key])
}
}

View File

@@ -1,17 +1,18 @@
{
"name": "finalhandler",
"description": "Node.js final http responder",
"version": "2.1.0",
"version": "1.3.1",
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
"license": "MIT",
"repository": "pillarjs/finalhandler",
"dependencies": {
"debug": "^4.4.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"on-finished": "^2.4.1",
"parseurl": "^1.3.3",
"statuses": "^2.0.1"
"debug": "2.6.9",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
"devDependencies": {
"eslint": "7.32.0",
@@ -21,13 +22,16 @@
"eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "5.2.0",
"eslint-plugin-standard": "4.1.0",
"mocha": "^11.0.1",
"nyc": "^17.1.0",
"supertest": "^7.0.0"
"mocha": "10.0.0",
"nyc": "15.1.0",
"readable-stream": "2.3.6",
"safe-buffer": "5.2.1",
"supertest": "6.2.4"
},
"files": [
"LICENSE",
"HISTORY.md",
"SECURITY.md",
"index.js"
],
"engines": {

10
api/node_modules/fresh/HISTORY.md generated vendored
View File

@@ -1,13 +1,3 @@
2.0.0 - 2024-09-04
==========
* Drop support for Node.js <18
1.0.0 - 2024-09-04
==========
* Drop support for Node.js below 0.8
* Fix: Ignore `If-Modified-Since` in the presence of `If-None-Match`, according to [spec](https://www.rfc-editor.org/rfc/rfc9110.html#section-13.1.3-5). Fixes [#35](https://github.com/jshttp/fresh/issues/35)
0.5.2 / 2017-09-13
==================

18
api/node_modules/fresh/README.md generated vendored
View File

@@ -3,7 +3,7 @@
[![NPM Version][npm-image]][npm-url]
[![NPM Downloads][downloads-image]][downloads-url]
[![Node.js Version][node-version-image]][node-version-url]
[![Build Status][ci-image]][ci-url]
[![Build Status][travis-image]][travis-url]
[![Test Coverage][coveralls-image]][coveralls-url]
HTTP response freshness testing
@@ -20,6 +20,8 @@ $ npm install fresh
## API
<!-- eslint-disable no-unused-vars -->
```js
var fresh = require('fresh')
```
@@ -40,7 +42,7 @@ to make handling these requests transparent.
This module is designed to only follow the HTTP specifications, not
to work-around all kinda of client bugs (especially since this module
typically does not receive enough information to understand what the
typically does not recieve enough information to understand what the
client actually is).
There is a known issue that in certain versions of Safari, Safari
@@ -55,16 +57,16 @@ links to further reading on this Safari bug.
### API usage
<!-- eslint-disable no-redeclare -->
<!-- eslint-disable no-redeclare, no-undef -->
```js
var reqHeaders = { 'if-none-match': '"foo"' }
var resHeaders = { etag: '"bar"' }
var resHeaders = { 'etag': '"bar"' }
fresh(reqHeaders, resHeaders)
// => false
var reqHeaders = { 'if-none-match': '"foo"' }
var resHeaders = { etag: '"foo"' }
var resHeaders = { 'etag': '"foo"' }
fresh(reqHeaders, resHeaders)
// => true
```
@@ -93,7 +95,7 @@ var server = http.createServer(function (req, res) {
function isFresh (req, res) {
return fresh(req.headers, {
etag: res.getHeader('ETag'),
'etag': res.getHeader('ETag'),
'last-modified': res.getHeader('Last-Modified')
})
}
@@ -105,12 +107,12 @@ server.listen(3000)
[MIT](LICENSE)
[ci-image]: https://img.shields.io/github/workflow/status/jshttp/fresh/ci/master?label=ci
[ci-url]: https://github.com/jshttp/fresh/actions/workflows/ci.yml
[npm-image]: https://img.shields.io/npm/v/fresh.svg
[npm-url]: https://npmjs.org/package/fresh
[node-version-image]: https://img.shields.io/node/v/fresh.svg
[node-version-url]: https://nodejs.org/en/
[travis-image]: https://img.shields.io/travis/jshttp/fresh/master.svg
[travis-url]: https://travis-ci.org/jshttp/fresh
[coveralls-image]: https://img.shields.io/coveralls/jshttp/fresh/master.svg
[coveralls-url]: https://coveralls.io/r/jshttp/fresh?branch=master
[downloads-image]: https://img.shields.io/npm/dm/fresh.svg

17
api/node_modules/fresh/index.js generated vendored
View File

@@ -48,26 +48,27 @@ function fresh (reqHeaders, resHeaders) {
return false
}
// if-none-match takes precedent over if-modified-since
if (noneMatch) {
if (noneMatch === '*') {
return true
}
var etag = resHeaders.etag
// if-none-match
if (noneMatch && noneMatch !== '*') {
var etag = resHeaders['etag']
if (!etag) {
return false
}
var etagStale = true
var matches = parseTokenList(noneMatch)
for (var i = 0; i < matches.length; i++) {
var match = matches[i]
if (match === etag || match === 'W/' + etag || 'W/' + match === etag) {
return true
etagStale = false
break
}
}
return false
if (etagStale) {
return false
}
}
// if-modified-since

30
api/node_modules/fresh/package.json generated vendored
View File

@@ -1,7 +1,7 @@
{
"name": "fresh",
"description": "HTTP response freshness testing",
"version": "2.0.0",
"version": "0.5.2",
"author": "TJ Holowaychuk <tj@vision-media.ca> (http://tjholowaychuk.com)",
"contributors": [
"Douglas Christopher Wilson <doug@somethingdoug.com>",
@@ -18,15 +18,15 @@
"devDependencies": {
"beautify-benchmark": "0.2.4",
"benchmark": "2.1.4",
"eslint": "8.12.0",
"eslint-config-standard": "14.1.1",
"eslint-plugin-import": "2.25.4",
"eslint-plugin-markdown": "2.2.1",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "6.0.0",
"eslint-plugin-standard": "4.1.0",
"mocha": "9.2.0",
"nyc": "15.1.0"
"eslint": "3.19.0",
"eslint-config-standard": "10.2.1",
"eslint-plugin-import": "2.7.0",
"eslint-plugin-markdown": "1.0.0-beta.6",
"eslint-plugin-node": "5.1.1",
"eslint-plugin-promise": "3.5.0",
"eslint-plugin-standard": "3.0.1",
"istanbul": "0.4.5",
"mocha": "1.21.5"
},
"files": [
"HISTORY.md",
@@ -34,13 +34,13 @@
"index.js"
],
"engines": {
"node": ">= 0.8"
"node": ">= 0.6"
},
"scripts": {
"bench": "node benchmark/index.js",
"lint": "eslint .",
"test": "mocha --reporter spec --check-leaks --bail test/",
"test-ci": "nyc --reporter=lcov --reporter=text npm test",
"test-cov": "nyc --reporter=html --reporter=text npm test"
"lint": "eslint --plugin markdown --ext js,md .",
"test": "mocha --reporter spec --bail --check-leaks test/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/"
}
}

View File

@@ -1,11 +0,0 @@
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
allow:
- dependency-type: production

View File

@@ -1,47 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<HTMLCodeStyleSettings>
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
<option name="HTML_ENFORCE_QUOTES" value="true" />
</HTMLCodeStyleSettings>
<JSCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</JSCodeStyleSettings>
<TypeScriptCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</TypeScriptCodeStyleSettings>
<VueCodeStyleSettings>
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
</VueCodeStyleSettings>
<codeStyleSettings language="HTML">
<option name="SOFT_MARGINS" value="100" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="SOFT_MARGINS" value="100" />
</codeStyleSettings>
<codeStyleSettings language="TypeScript">
<option name="SOFT_MARGINS" value="100" />
</codeStyleSettings>
<codeStyleSettings language="Vue">
<option name="SOFT_MARGINS" value="100" />
<indentOptions>
<option name="INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/iconv-lite.iml" filepath="$PROJECT_DIR$/.idea/iconv-lite.iml" />
</modules>
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -1,100 +1,50 @@
## 0.6.3 / 2021-05-23
* Fix HKSCS encoding to prefer Big5 codes if both Big5 and HKSCS codes are possible (#264)
## 0.6.2 / 2020-07-08
* Support Uint8Array-s decoding without conversion to Buffers, plus fix an edge case.
## 0.6.1 / 2020-06-28
* Support Uint8Array-s directly when decoding (#246, by @gyzerok)
* Unify package.json version ranges to be strictly semver-compatible (#241)
* Fix minor issue in UTF-32 decoder's endianness detection code.
## 0.6.0 / 2020-06-08
* Updated 'gb18030' encoding to :2005 edition (see https://github.com/whatwg/encoding/issues/22).
* Removed `iconv.extendNodeEncodings()` mechanism. It was deprecated 5 years ago and didn't work
in recent Node versions.
* Reworked Streaming API behavior in browser environments to fix #204. Streaming API will be
excluded by default in browser packs, saving ~100Kb bundle size, unless enabled explicitly using
`iconv.enableStreamingAPI(require('stream'))`.
* Updates to development environment & tests:
* Added ./test/webpack private package to test complex new use cases that need custom environment.
It's tested as a separate job in Travis CI.
* Updated generation code for the new EUC-KR index file format from Encoding Standard.
* Removed Buffer() constructor in tests (#197 by @gabrielschulhof).
## 0.5.2 / 2020-06-08
* Added `iconv.getEncoder()` and `iconv.getDecoder()` methods to typescript definitions (#229).
* Fixed semver version to 6.1.2 to support Node 8.x (by @tanandara).
* Capped iconv version to 2.x as 3.x has dropped support for older Node versions.
* Switched from instanbul to c8 for code coverage.
## 0.5.1 / 2020-01-18
* Added cp720 encoding (#221, by @kr-deps)
* (minor) Changed Changelog.md formatting to use h2.
## 0.5.0 / 2019-06-26
* Added UTF-32 encoding, both little-endian and big-endian variants (UTF-32LE, UTF32-BE). If endianness
is not provided for decoding, it's deduced automatically from the stream using a heuristic similar to
what we use in UTF-16. (great work in #216 by @kshetline)
* Several minor updates to README (#217 by @oldj, plus some more)
* Added Node versions 10 and 12 to Travis test harness.
## 0.4.24 / 2018-08-22
# 0.4.24 / 2018-08-22
* Added MIK encoding (#196, by @Ivan-Kalatchev)
## 0.4.23 / 2018-05-07
# 0.4.23 / 2018-05-07
* Fix deprecation warning in Node v10 due to the last usage of `new Buffer` (#185, by @felixbuenemann)
* Switched from NodeBuffer to Buffer in typings (#155 by @felixfbecker, #186 by @larssn)
## 0.4.22 / 2018-05-05
# 0.4.22 / 2018-05-05
* Use older semver style for dependencies to be compatible with Node version 0.10 (#182, by @dougwilson)
* Fix tests to accomodate fixes in Node v10 (#182, by @dougwilson)
## 0.4.21 / 2018-04-06
# 0.4.21 / 2018-04-06
* Fix encoding canonicalization (#156)
* Fix the paths in the "browser" field in package.json (#174 by @LMLB)
* Removed "contributors" section in package.json - see Git history instead.
## 0.4.20 / 2018-04-06
# 0.4.20 / 2018-04-06
* Updated `new Buffer()` usages with recommended replacements as it's being deprecated in Node v10 (#176, #178 by @ChALkeR)
## 0.4.19 / 2017-09-09
# 0.4.19 / 2017-09-09
* Fixed iso8859-1 codec regression in handling untranslatable characters (#162, caused by #147)
* Re-generated windows1255 codec, because it was updated in iconv project
* Fixed grammar in error message when iconv-lite is loaded with encoding other than utf8
## 0.4.18 / 2017-06-13
# 0.4.18 / 2017-06-13
* Fixed CESU-8 regression in Node v8.
## 0.4.17 / 2017-04-22
# 0.4.17 / 2017-04-22
* Updated typescript definition file to support Angular 2 AoT mode (#153 by @larssn)
## 0.4.16 / 2017-04-22
# 0.4.16 / 2017-04-22
* Added support for React Native (#150)
* Changed iso8859-1 encoding to usine internal 'binary' encoding, as it's the same thing (#147 by @mscdex)
@@ -103,12 +53,12 @@
* Added a warning if iconv-lite is loaded not as utf-8 (see #142)
## 0.4.15 / 2016-11-21
# 0.4.15 / 2016-11-21
* Fixed typescript type definition (#137)
## 0.4.14 / 2016-11-20
# 0.4.14 / 2016-11-20
* Preparation for v1.0
* Added Node v6 and latest Node versions to Travis CI test rig
@@ -118,12 +68,12 @@
* Add ms prefix to dbcs windows encodings (@rokoroku)
## 0.4.13 / 2015-10-01
# 0.4.13 / 2015-10-01
* Fix silly mistake in deprecation notice.
## 0.4.12 / 2015-09-26
# 0.4.12 / 2015-09-26
* Node v4 support:
* Added CESU-8 decoding (#106)
@@ -131,18 +81,18 @@
* Added Travis tests for Node v4 and io.js latest (#105 by @Mithgol)
## 0.4.11 / 2015-07-03
# 0.4.11 / 2015-07-03
* Added CESU-8 encoding.
## 0.4.10 / 2015-05-26
# 0.4.10 / 2015-05-26
* Changed UTF-16 endianness heuristic to take into account any ASCII chars, not
just spaces. This should minimize the importance of "default" endianness.
## 0.4.9 / 2015-05-24
# 0.4.9 / 2015-05-24
* Streamlined BOM handling: strip BOM by default, add BOM when encoding if
addBOM: true. Added docs to Readme.
@@ -154,12 +104,12 @@
* Use strict mode in all files.
## 0.4.8 / 2015-04-14
# 0.4.8 / 2015-04-14
* added alias UNICODE-1-1-UTF-7 for UTF-7 encoding (#94)
## 0.4.7 / 2015-02-05
# 0.4.7 / 2015-02-05
* stop official support of Node.js v0.8. Should still work, but no guarantees.
reason: Packages needed for testing are hard to get on Travis CI.
@@ -167,40 +117,40 @@
props (#89).
## 0.4.6 / 2015-01-12
# 0.4.6 / 2015-01-12
* fix rare aliases of single-byte encodings (thanks @mscdex)
* double the timeout for dbcs tests to make them less flaky on travis
## 0.4.5 / 2014-11-20
# 0.4.5 / 2014-11-20
* fix windows-31j and x-sjis encoding support (@nleush)
* minor fix: undefined variable reference when internal error happens
## 0.4.4 / 2014-07-16
# 0.4.4 / 2014-07-16
* added encodings UTF-7 (RFC2152) and UTF-7-IMAP (RFC3501 Section 5.1.3)
* fixed streaming base64 encoding
## 0.4.3 / 2014-06-14
# 0.4.3 / 2014-06-14
* added encodings UTF-16BE and UTF-16 with BOM
## 0.4.2 / 2014-06-12
# 0.4.2 / 2014-06-12
* don't throw exception if `extendNodeEncodings()` is called more than once
## 0.4.1 / 2014-06-11
# 0.4.1 / 2014-06-11
* codepage 808 added
## 0.4.0 / 2014-06-10
# 0.4.0 / 2014-06-10
* code is rewritten from scratch
* all widespread encodings are supported

View File

@@ -1,40 +1,38 @@
## iconv-lite: Pure JS character encoding conversion
## Pure JS character encoding conversion [![Build Status](https://travis-ci.org/ashtuchkin/iconv-lite.svg?branch=master)](https://travis-ci.org/ashtuchkin/iconv-lite)
* No need for native code compilation. Quick to install, works on Windows and in sandboxed environments like [Cloud9](http://c9.io).
* Doesn't need native code compilation. Works on Windows and in sandboxed environments like [Cloud9](http://c9.io).
* Used in popular projects like [Express.js (body_parser)](https://github.com/expressjs/body-parser),
[Grunt](http://gruntjs.com/), [Nodemailer](http://www.nodemailer.com/), [Yeoman](http://yeoman.io/) and others.
* Faster than [node-iconv](https://github.com/bnoordhuis/node-iconv) (see below for performance comparison).
* Intuitive encode/decode API, including Streaming support.
* In-browser usage via [browserify](https://github.com/substack/node-browserify) or [webpack](https://webpack.js.org/) (~180kb gzip compressed with Buffer shim included).
* Intuitive encode/decode API
* Streaming support for Node v0.10+
* [Deprecated] Can extend Node.js primitives (buffers, streams) to support all iconv-lite encodings.
* In-browser usage via [Browserify](https://github.com/substack/node-browserify) (~180k gzip compressed with Buffer shim included).
* Typescript [type definition file](https://github.com/ashtuchkin/iconv-lite/blob/master/lib/index.d.ts) included.
* React Native is supported (need to install `stream` module to enable Streaming API).
* React Native is supported (need to explicitly `npm install` two more modules: `buffer` and `stream`).
* License: MIT.
[![NPM Stats](https://nodei.co/npm/iconv-lite.png)](https://npmjs.org/package/iconv-lite/)
[![Build Status](https://travis-ci.org/ashtuchkin/iconv-lite.svg?branch=master)](https://travis-ci.org/ashtuchkin/iconv-lite)
[![npm](https://img.shields.io/npm/v/iconv-lite.svg)](https://npmjs.org/package/iconv-lite/)
[![npm downloads](https://img.shields.io/npm/dm/iconv-lite.svg)](https://npmjs.org/package/iconv-lite/)
[![npm bundle size](https://img.shields.io/bundlephobia/min/iconv-lite.svg)](https://npmjs.org/package/iconv-lite/)
[![NPM Stats](https://nodei.co/npm/iconv-lite.png?downloads=true&downloadRank=true)](https://npmjs.org/packages/iconv-lite/)
## Usage
### Basic API
```javascript
var iconv = require('iconv-lite');
// Convert from an encoded buffer to a js string.
// Convert from an encoded buffer to js string.
str = iconv.decode(Buffer.from([0x68, 0x65, 0x6c, 0x6c, 0x6f]), 'win1251');
// Convert from a js string to an encoded buffer.
// Convert from js string to an encoded buffer.
buf = iconv.encode("Sample input string", 'win1251');
// Check if encoding is supported
iconv.encodingExists("us-ascii")
```
### Streaming API
### Streaming API (Node v0.10+)
```javascript
// Decode stream (from binary data stream to js strings)
// Decode stream (from binary stream to js strings)
http.createServer(function(req, res) {
var converterStream = iconv.decodeStream('win1251');
req.pipe(converterStream);
@@ -59,10 +57,44 @@ http.createServer(function(req, res) {
});
```
### [Deprecated] Extend Node.js own encodings
> NOTE: This doesn't work on latest Node versions. See [details](https://github.com/ashtuchkin/iconv-lite/wiki/Node-v4-compatibility).
```javascript
// After this call all Node basic primitives will understand iconv-lite encodings.
iconv.extendNodeEncodings();
// Examples:
buf = new Buffer(str, 'win1251');
buf.write(str, 'gbk');
str = buf.toString('latin1');
assert(Buffer.isEncoding('iso-8859-15'));
Buffer.byteLength(str, 'us-ascii');
http.createServer(function(req, res) {
req.setEncoding('big5');
req.collect(function(err, body) {
console.log(body);
});
});
fs.createReadStream("file.txt", "shift_jis");
// External modules are also supported (if they use Node primitives, which they probably do).
request = require('request');
request({
url: "http://github.com/",
encoding: "cp932"
});
// To remove extensions
iconv.undoExtendNodeEncodings();
```
## Supported encodings
* All node.js native encodings: utf8, ucs2 / utf16-le, ascii, binary, base64, hex.
* Additional unicode encodings: utf16, utf16-be, utf-7, utf-7-imap, utf32, utf32-le, and utf32-be.
* Additional unicode encodings: utf16, utf16-be, utf-7, utf-7-imap.
* All widespread singlebyte encodings: Windows 125x family, ISO-8859 family,
IBM/DOS codepages, Macintosh family, KOI8 family, all others supported by iconv library.
Aliases like 'latin1', 'us-ascii' also supported.
@@ -101,12 +133,6 @@ smart about endianness in the following ways:
overridden with `defaultEncoding: 'utf-16be'` option. Strips BOM unless `stripBOM: false`.
* Encoding: uses UTF-16LE and writes BOM by default. Use `addBOM: false` to override.
## UTF-32 Encodings
This library supports UTF-32LE, UTF-32BE and UTF-32 encodings. Like the UTF-16 encoding above, UTF-32 defaults to UTF-32LE, but uses BOM and 'spaces heuristics' to determine input endianness.
* The default of UTF-32LE can be overridden with the `defaultEncoding: 'utf-32be'` option. Strips BOM unless `stripBOM: false`.
* Encoding: uses UTF-32LE and writes BOM by default. Use `addBOM: false` to override. (`defaultEncoding: 'utf-32be'` can also be used here to change encoding.)
## Other notes
When decoding, be sure to supply a Buffer to decode() method, otherwise [bad things usually happen](https://github.com/ashtuchkin/iconv-lite/wiki/Use-Buffers-when-decoding).

View File

@@ -49,48 +49,6 @@ function DBCSCodec(codecOptions, iconv) {
for (var i = 0; i < mappingTable.length; i++)
this._addDecodeChunk(mappingTable[i]);
// Load & create GB18030 tables when needed.
if (typeof codecOptions.gb18030 === 'function') {
this.gb18030 = codecOptions.gb18030(); // Load GB18030 ranges.
// Add GB18030 common decode nodes.
var commonThirdByteNodeIdx = this.decodeTables.length;
this.decodeTables.push(UNASSIGNED_NODE.slice(0));
var commonFourthByteNodeIdx = this.decodeTables.length;
this.decodeTables.push(UNASSIGNED_NODE.slice(0));
// Fill out the tree
var firstByteNode = this.decodeTables[0];
for (var i = 0x81; i <= 0xFE; i++) {
var secondByteNode = this.decodeTables[NODE_START - firstByteNode[i]];
for (var j = 0x30; j <= 0x39; j++) {
if (secondByteNode[j] === UNASSIGNED) {
secondByteNode[j] = NODE_START - commonThirdByteNodeIdx;
} else if (secondByteNode[j] > NODE_START) {
throw new Error("gb18030 decode tables conflict at byte 2");
}
var thirdByteNode = this.decodeTables[NODE_START - secondByteNode[j]];
for (var k = 0x81; k <= 0xFE; k++) {
if (thirdByteNode[k] === UNASSIGNED) {
thirdByteNode[k] = NODE_START - commonFourthByteNodeIdx;
} else if (thirdByteNode[k] === NODE_START - commonFourthByteNodeIdx) {
continue;
} else if (thirdByteNode[k] > NODE_START) {
throw new Error("gb18030 decode tables conflict at byte 3");
}
var fourthByteNode = this.decodeTables[NODE_START - thirdByteNode[k]];
for (var l = 0x30; l <= 0x39; l++) {
if (fourthByteNode[l] === UNASSIGNED)
fourthByteNode[l] = GB18030_CODE;
}
}
}
}
}
this.defaultCharUnicode = iconv.defaultCharUnicode;
@@ -134,6 +92,30 @@ function DBCSCodec(codecOptions, iconv) {
this.defCharSB = this.encodeTable[0][iconv.defaultCharSingleByte.charCodeAt(0)];
if (this.defCharSB === UNASSIGNED) this.defCharSB = this.encodeTable[0]['?'];
if (this.defCharSB === UNASSIGNED) this.defCharSB = "?".charCodeAt(0);
// Load & create GB18030 tables when needed.
if (typeof codecOptions.gb18030 === 'function') {
this.gb18030 = codecOptions.gb18030(); // Load GB18030 ranges.
// Add GB18030 decode tables.
var thirdByteNodeIdx = this.decodeTables.length;
var thirdByteNode = this.decodeTables[thirdByteNodeIdx] = UNASSIGNED_NODE.slice(0);
var fourthByteNodeIdx = this.decodeTables.length;
var fourthByteNode = this.decodeTables[fourthByteNodeIdx] = UNASSIGNED_NODE.slice(0);
for (var i = 0x81; i <= 0xFE; i++) {
var secondByteNodeIdx = NODE_START - this.decodeTables[0][i];
var secondByteNode = this.decodeTables[secondByteNodeIdx];
for (var j = 0x30; j <= 0x39; j++)
secondByteNode[j] = NODE_START - thirdByteNodeIdx;
}
for (var i = 0x81; i <= 0xFE; i++)
thirdByteNode[i] = NODE_START - fourthByteNodeIdx;
for (var i = 0x30; i <= 0x39; i++)
fourthByteNode[i] = GB18030_CODE
}
}
DBCSCodec.prototype.encoder = DBCSEncoder;
@@ -142,7 +124,7 @@ DBCSCodec.prototype.decoder = DBCSDecoder;
// Decoder helpers
DBCSCodec.prototype._getDecodeTrieNode = function(addr) {
var bytes = [];
for (; addr > 0; addr >>>= 8)
for (; addr > 0; addr >>= 8)
bytes.push(addr & 0xFF);
if (bytes.length == 0)
bytes.push(0);
@@ -267,32 +249,19 @@ DBCSCodec.prototype._setEncodeSequence = function(seq, dbcsCode) {
DBCSCodec.prototype._fillEncodeTable = function(nodeIdx, prefix, skipEncodeChars) {
var node = this.decodeTables[nodeIdx];
var hasValues = false;
var subNodeEmpty = {};
for (var i = 0; i < 0x100; i++) {
var uCode = node[i];
var mbCode = prefix + i;
if (skipEncodeChars[mbCode])
continue;
if (uCode >= 0) {
if (uCode >= 0)
this._setEncodeChar(uCode, mbCode);
hasValues = true;
} else if (uCode <= NODE_START) {
var subNodeIdx = NODE_START - uCode;
if (!subNodeEmpty[subNodeIdx]) { // Skip empty subtrees (they are too large in gb18030).
var newPrefix = (mbCode << 8) >>> 0; // NOTE: '>>> 0' keeps 32-bit num positive.
if (this._fillEncodeTable(subNodeIdx, newPrefix, skipEncodeChars))
hasValues = true;
else
subNodeEmpty[subNodeIdx] = true;
}
} else if (uCode <= SEQ_START) {
else if (uCode <= NODE_START)
this._fillEncodeTable(NODE_START - uCode, mbCode << 8, skipEncodeChars);
else if (uCode <= SEQ_START)
this._setEncodeSequence(this.decodeTableSeq[SEQ_START - uCode], mbCode);
hasValues = true;
}
}
return hasValues;
}
@@ -419,15 +388,10 @@ DBCSEncoder.prototype.write = function(str) {
newBuf[j++] = dbcsCode >> 8; // high byte
newBuf[j++] = dbcsCode & 0xFF; // low byte
}
else if (dbcsCode < 0x1000000) {
else {
newBuf[j++] = dbcsCode >> 16;
newBuf[j++] = (dbcsCode >> 8) & 0xFF;
newBuf[j++] = dbcsCode & 0xFF;
} else {
newBuf[j++] = dbcsCode >>> 24;
newBuf[j++] = (dbcsCode >>> 16) & 0xFF;
newBuf[j++] = (dbcsCode >>> 8) & 0xFF;
newBuf[j++] = dbcsCode & 0xFF;
}
}
@@ -476,7 +440,7 @@ DBCSEncoder.prototype.findIdx = findIdx;
function DBCSDecoder(options, codec) {
// Decoder state
this.nodeIdx = 0;
this.prevBytes = [];
this.prevBuf = Buffer.alloc(0);
// Static data
this.decodeTables = codec.decodeTables;
@@ -488,12 +452,15 @@ function DBCSDecoder(options, codec) {
DBCSDecoder.prototype.write = function(buf) {
var newBuf = Buffer.alloc(buf.length*2),
nodeIdx = this.nodeIdx,
prevBytes = this.prevBytes, prevOffset = this.prevBytes.length,
seqStart = -this.prevBytes.length, // idx of the start of current parsed sequence.
prevBuf = this.prevBuf, prevBufOffset = this.prevBuf.length,
seqStart = -this.prevBuf.length, // idx of the start of current parsed sequence.
uCode;
if (prevBufOffset > 0) // Make prev buf overlap a little to make it easier to slice later.
prevBuf = Buffer.concat([prevBuf, buf.slice(0, 10)]);
for (var i = 0, j = 0; i < buf.length; i++) {
var curByte = (i >= 0) ? buf[i] : prevBytes[i + prevOffset];
var curByte = (i >= 0) ? buf[i] : prevBuf[i + prevBufOffset];
// Lookup in current trie node.
var uCode = this.decodeTables[nodeIdx][curByte];
@@ -503,18 +470,13 @@ DBCSDecoder.prototype.write = function(buf) {
}
else if (uCode === UNASSIGNED) { // Unknown char.
// TODO: Callback with seq.
//var curSeq = (seqStart >= 0) ? buf.slice(seqStart, i+1) : prevBuf.slice(seqStart + prevBufOffset, i+1 + prevBufOffset);
i = seqStart; // Try to parse again, after skipping first byte of the sequence ('i' will be incremented by 'for' cycle).
uCode = this.defaultCharUnicode.charCodeAt(0);
i = seqStart; // Skip one byte ('i' will be incremented by the for loop) and try to parse again.
}
else if (uCode === GB18030_CODE) {
if (i >= 3) {
var ptr = (buf[i-3]-0x81)*12600 + (buf[i-2]-0x30)*1260 + (buf[i-1]-0x81)*10 + (curByte-0x30);
} else {
var ptr = (prevBytes[i-3+prevOffset]-0x81)*12600 +
(((i-2 >= 0) ? buf[i-2] : prevBytes[i-2+prevOffset])-0x30)*1260 +
(((i-1 >= 0) ? buf[i-1] : prevBytes[i-1+prevOffset])-0x81)*10 +
(curByte-0x30);
}
var curSeq = (seqStart >= 0) ? buf.slice(seqStart, i+1) : prevBuf.slice(seqStart + prevBufOffset, i+1 + prevBufOffset);
var ptr = (curSeq[0]-0x81)*12600 + (curSeq[1]-0x30)*1260 + (curSeq[2]-0x81)*10 + (curSeq[3]-0x30);
var idx = findIdx(this.gb18030.gbChars, ptr);
uCode = this.gb18030.uChars[idx] + ptr - this.gb18030.gbChars[idx];
}
@@ -535,13 +497,13 @@ DBCSDecoder.prototype.write = function(buf) {
throw new Error("iconv-lite internal error: invalid decoding table value " + uCode + " at " + nodeIdx + "/" + curByte);
// Write the character to buffer, handling higher planes using surrogate pair.
if (uCode >= 0x10000) {
if (uCode > 0xFFFF) {
uCode -= 0x10000;
var uCodeLead = 0xD800 | (uCode >> 10);
var uCodeLead = 0xD800 + Math.floor(uCode / 0x400);
newBuf[j++] = uCodeLead & 0xFF;
newBuf[j++] = uCodeLead >> 8;
uCode = 0xDC00 | (uCode & 0x3FF);
uCode = 0xDC00 + uCode % 0x400;
}
newBuf[j++] = uCode & 0xFF;
newBuf[j++] = uCode >> 8;
@@ -551,10 +513,7 @@ DBCSDecoder.prototype.write = function(buf) {
}
this.nodeIdx = nodeIdx;
this.prevBytes = (seqStart >= 0)
? Array.prototype.slice.call(buf, seqStart)
: prevBytes.slice(seqStart + prevOffset).concat(Array.prototype.slice.call(buf));
this.prevBuf = (seqStart >= 0) ? buf.slice(seqStart) : prevBuf.slice(seqStart + prevBufOffset);
return newBuf.slice(0, j).toString('ucs2');
}
@@ -562,19 +521,18 @@ DBCSDecoder.prototype.end = function() {
var ret = '';
// Try to parse all remaining chars.
while (this.prevBytes.length > 0) {
while (this.prevBuf.length > 0) {
// Skip 1 character in the buffer.
ret += this.defaultCharUnicode;
var bytesArr = this.prevBytes.slice(1);
var buf = this.prevBuf.slice(1);
// Parse remaining as usual.
this.prevBytes = [];
this.prevBuf = Buffer.alloc(0);
this.nodeIdx = 0;
if (bytesArr.length > 0)
ret += this.write(bytesArr);
if (buf.length > 0)
ret += this.write(buf);
}
this.prevBytes = [];
this.nodeIdx = 0;
return ret;
}
@@ -586,7 +544,7 @@ function findIdx(table, val) {
var l = 0, r = table.length;
while (l < r-1) { // always table[l] <= val < table[r]
var mid = l + ((r-l+1) >> 1);
var mid = l + Math.floor((r-l+1)/2);
if (table[mid] <= val)
l = mid;
else

View File

@@ -167,19 +167,7 @@ module.exports = {
'big5hkscs': {
type: '_dbcs',
table: function() { return require('./tables/cp950.json').concat(require('./tables/big5-added.json')) },
encodeSkipVals: [
// Although Encoding Standard says we should avoid encoding to HKSCS area (See Step 1 of
// https://encoding.spec.whatwg.org/#index-big5-pointer), we still do it to increase compatibility with ICU.
// But if a single unicode point can be encoded both as HKSCS and regular Big5, we prefer the latter.
0x8e69, 0x8e6f, 0x8e7e, 0x8eab, 0x8eb4, 0x8ecd, 0x8ed0, 0x8f57, 0x8f69, 0x8f6e, 0x8fcb, 0x8ffe,
0x906d, 0x907a, 0x90c4, 0x90dc, 0x90f1, 0x91bf, 0x92af, 0x92b0, 0x92b1, 0x92b2, 0x92d1, 0x9447, 0x94ca,
0x95d9, 0x96fc, 0x9975, 0x9b76, 0x9b78, 0x9b7b, 0x9bc6, 0x9bde, 0x9bec, 0x9bf6, 0x9c42, 0x9c53, 0x9c62,
0x9c68, 0x9c6b, 0x9c77, 0x9cbc, 0x9cbd, 0x9cd0, 0x9d57, 0x9d5a, 0x9dc4, 0x9def, 0x9dfb, 0x9ea9, 0x9eef,
0x9efd, 0x9f60, 0x9fcb, 0xa077, 0xa0dc, 0xa0df, 0x8fcc, 0x92c8, 0x9644, 0x96ed,
// Step 2 of https://encoding.spec.whatwg.org/#index-big5-pointer: Use last pointer for U+2550, U+255E, U+2561, U+256A, U+5341, or U+5345
0xa2a4, 0xa2a5, 0xa2a7, 0xa2a6, 0xa2cc, 0xa2ce,
],
encodeSkipVals: [0xa2cc],
},
'cnbig5': 'big5hkscs',

View File

@@ -4,7 +4,6 @@
// We support Browserify by skipping automatic module discovery and requiring modules directly.
var modules = [
require("./internal"),
require("./utf32"),
require("./utf16"),
require("./utf7"),
require("./sbcs-codec"),
@@ -14,7 +13,7 @@ var modules = [
require("./dbcs-data"),
];
// Put all encoding/alias/codec definitions to single object and export it.
// Put all encoding/alias/codec definitions to single object and export it.
for (var i = 0; i < modules.length; i++) {
var module = modules[i];
for (var enc in module)

View File

@@ -53,20 +53,10 @@ if (!StringDecoder.prototype.end) // Node v0.8 doesn't have this method.
function InternalDecoder(options, codec) {
this.decoder = new StringDecoder(codec.enc);
StringDecoder.call(this, codec.enc);
}
InternalDecoder.prototype.write = function(buf) {
if (!Buffer.isBuffer(buf)) {
buf = Buffer.from(buf);
}
return this.decoder.write(buf);
}
InternalDecoder.prototype.end = function() {
return this.decoder.end();
}
InternalDecoder.prototype = StringDecoder.prototype;
//------------------------------------------------------------------------------

View File

@@ -22,11 +22,6 @@ module.exports = {
"chars": "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя└┴┬├─┼╣║╚╔╩╦╠═╬┐░▒▓│┤№§╗╝┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ "
},
"cp720": {
"type": "_sbcs",
"chars": "\x80\x81éâ\x84à\x86çêëèïî\x8d\x8e\x8f\x90\u0651\u0652ô¤ـûùءآأؤ£إئابةتثجحخدذرزسشص«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀ضطظعغفµقكلمنهوىي≡\u064b\u064c\u064d\u064e\u064f\u0650≈°∙·√ⁿ²■\u00a0"
},
// Aliases of generated encodings.
"ascii8bit": "ascii",
"usascii": "ascii",

View File

@@ -27,7 +27,7 @@
["a7c2","",14],
["a7f2","",12],
["a896","",10],
["a8bc","ḿ"],
["a8bc",""],
["a8bf","ǹ"],
["a8c1",""],
["a8ea","",20],
@@ -51,6 +51,5 @@
["fca1","",93],
["fda1","",93],
["fe50","⺁⺄㑳㑇⺈⺋㖞㘚㘎⺌⺗㥮㤘㧏㧟㩳㧐㭎㱮㳠⺧⺪䁖䅟⺮䌷⺳⺶⺷䎱䎬⺻䏝䓖䙡䙌"],
["fe80","䜣䜩䝼䞍⻊䥇䥺䥽䦂䦃䦅䦆䦟䦛䦷䦶䲣䲟䲠䲡䱷䲢䴓",6,"䶮",93],
["8135f437",""]
["fe80","䜣䜩䝼䞍⻊䥇䥺䥽䦂䦃䦅䦆䦟䦛䦷䦶䲣䲟䲠䲡䱷䲢䴓",6,"䶮",93]
]

View File

@@ -61,7 +61,6 @@ Utf16BEDecoder.prototype.write = function(buf) {
}
Utf16BEDecoder.prototype.end = function() {
this.overflowByte = -1;
}
@@ -104,8 +103,8 @@ Utf16Encoder.prototype.end = function() {
function Utf16Decoder(options, codec) {
this.decoder = null;
this.initialBufs = [];
this.initialBufsLen = 0;
this.initialBytes = [];
this.initialBytesLen = 0;
this.options = options || {};
this.iconv = codec.iconv;
@@ -114,22 +113,17 @@ function Utf16Decoder(options, codec) {
Utf16Decoder.prototype.write = function(buf) {
if (!this.decoder) {
// Codec is not chosen yet. Accumulate initial bytes.
this.initialBufs.push(buf);
this.initialBufsLen += buf.length;
this.initialBytes.push(buf);
this.initialBytesLen += buf.length;
if (this.initialBufsLen < 16) // We need more bytes to use space heuristic (see below)
if (this.initialBytesLen < 16) // We need more bytes to use space heuristic (see below)
return '';
// We have enough bytes -> detect endianness.
var encoding = detectEncoding(this.initialBufs, this.options.defaultEncoding);
var buf = Buffer.concat(this.initialBytes),
encoding = detectEncoding(buf, this.options.defaultEncoding);
this.decoder = this.iconv.getDecoder(encoding, this.options);
var resStr = '';
for (var i = 0; i < this.initialBufs.length; i++)
resStr += this.decoder.write(this.initialBufs[i]);
this.initialBufs.length = this.initialBufsLen = 0;
return resStr;
this.initialBytes.length = this.initialBytesLen = 0;
}
return this.decoder.write(buf);
@@ -137,61 +131,47 @@ Utf16Decoder.prototype.write = function(buf) {
Utf16Decoder.prototype.end = function() {
if (!this.decoder) {
var encoding = detectEncoding(this.initialBufs, this.options.defaultEncoding);
var buf = Buffer.concat(this.initialBytes),
encoding = detectEncoding(buf, this.options.defaultEncoding);
this.decoder = this.iconv.getDecoder(encoding, this.options);
var resStr = '';
for (var i = 0; i < this.initialBufs.length; i++)
resStr += this.decoder.write(this.initialBufs[i]);
var res = this.decoder.write(buf),
trail = this.decoder.end();
var trail = this.decoder.end();
if (trail)
resStr += trail;
this.initialBufs.length = this.initialBufsLen = 0;
return resStr;
return trail ? (res + trail) : res;
}
return this.decoder.end();
}
function detectEncoding(bufs, defaultEncoding) {
var b = [];
var charsProcessed = 0;
var asciiCharsLE = 0, asciiCharsBE = 0; // Number of ASCII chars when decoded as LE or BE.
function detectEncoding(buf, defaultEncoding) {
var enc = defaultEncoding || 'utf-16le';
outer_loop:
for (var i = 0; i < bufs.length; i++) {
var buf = bufs[i];
for (var j = 0; j < buf.length; j++) {
b.push(buf[j]);
if (b.length === 2) {
if (charsProcessed === 0) {
// Check BOM first.
if (b[0] === 0xFF && b[1] === 0xFE) return 'utf-16le';
if (b[0] === 0xFE && b[1] === 0xFF) return 'utf-16be';
}
if (buf.length >= 2) {
// Check BOM.
if (buf[0] == 0xFE && buf[1] == 0xFF) // UTF-16BE BOM
enc = 'utf-16be';
else if (buf[0] == 0xFF && buf[1] == 0xFE) // UTF-16LE BOM
enc = 'utf-16le';
else {
// No BOM found. Try to deduce encoding from initial content.
// Most of the time, the content has ASCII chars (U+00**), but the opposite (U+**00) is uncommon.
// So, we count ASCII as if it was LE or BE, and decide from that.
var asciiCharsLE = 0, asciiCharsBE = 0, // Counts of chars in both positions
_len = Math.min(buf.length - (buf.length % 2), 64); // Len is always even.
if (b[0] === 0 && b[1] !== 0) asciiCharsBE++;
if (b[0] !== 0 && b[1] === 0) asciiCharsLE++;
b.length = 0;
charsProcessed++;
if (charsProcessed >= 100) {
break outer_loop;
}
for (var i = 0; i < _len; i += 2) {
if (buf[i] === 0 && buf[i+1] !== 0) asciiCharsBE++;
if (buf[i] !== 0 && buf[i+1] === 0) asciiCharsLE++;
}
if (asciiCharsBE > asciiCharsLE)
enc = 'utf-16be';
else if (asciiCharsBE < asciiCharsLE)
enc = 'utf-16le';
}
}
// Make decisions.
// Most of the time, the content has ASCII chars (U+00**), but the opposite (U+**00) is uncommon.
// So, we count ASCII as if it was LE or BE, and decide from that.
if (asciiCharsBE > asciiCharsLE) return 'utf-16be';
if (asciiCharsBE < asciiCharsLE) return 'utf-16le';
// Couldn't decide (likely all zeros or not enough data).
return defaultEncoding || 'utf-16le';
return enc;
}

View File

@@ -1,319 +0,0 @@
'use strict';
var Buffer = require('safer-buffer').Buffer;
// == UTF32-LE/BE codec. ==========================================================
exports._utf32 = Utf32Codec;
function Utf32Codec(codecOptions, iconv) {
this.iconv = iconv;
this.bomAware = true;
this.isLE = codecOptions.isLE;
}
exports.utf32le = { type: '_utf32', isLE: true };
exports.utf32be = { type: '_utf32', isLE: false };
// Aliases
exports.ucs4le = 'utf32le';
exports.ucs4be = 'utf32be';
Utf32Codec.prototype.encoder = Utf32Encoder;
Utf32Codec.prototype.decoder = Utf32Decoder;
// -- Encoding
function Utf32Encoder(options, codec) {
this.isLE = codec.isLE;
this.highSurrogate = 0;
}
Utf32Encoder.prototype.write = function(str) {
var src = Buffer.from(str, 'ucs2');
var dst = Buffer.alloc(src.length * 2);
var write32 = this.isLE ? dst.writeUInt32LE : dst.writeUInt32BE;
var offset = 0;
for (var i = 0; i < src.length; i += 2) {
var code = src.readUInt16LE(i);
var isHighSurrogate = (0xD800 <= code && code < 0xDC00);
var isLowSurrogate = (0xDC00 <= code && code < 0xE000);
if (this.highSurrogate) {
if (isHighSurrogate || !isLowSurrogate) {
// There shouldn't be two high surrogates in a row, nor a high surrogate which isn't followed by a low
// surrogate. If this happens, keep the pending high surrogate as a stand-alone semi-invalid character
// (technically wrong, but expected by some applications, like Windows file names).
write32.call(dst, this.highSurrogate, offset);
offset += 4;
}
else {
// Create 32-bit value from high and low surrogates;
var codepoint = (((this.highSurrogate - 0xD800) << 10) | (code - 0xDC00)) + 0x10000;
write32.call(dst, codepoint, offset);
offset += 4;
this.highSurrogate = 0;
continue;
}
}
if (isHighSurrogate)
this.highSurrogate = code;
else {
// Even if the current character is a low surrogate, with no previous high surrogate, we'll
// encode it as a semi-invalid stand-alone character for the same reasons expressed above for
// unpaired high surrogates.
write32.call(dst, code, offset);
offset += 4;
this.highSurrogate = 0;
}
}
if (offset < dst.length)
dst = dst.slice(0, offset);
return dst;
};
Utf32Encoder.prototype.end = function() {
// Treat any leftover high surrogate as a semi-valid independent character.
if (!this.highSurrogate)
return;
var buf = Buffer.alloc(4);
if (this.isLE)
buf.writeUInt32LE(this.highSurrogate, 0);
else
buf.writeUInt32BE(this.highSurrogate, 0);
this.highSurrogate = 0;
return buf;
};
// -- Decoding
function Utf32Decoder(options, codec) {
this.isLE = codec.isLE;
this.badChar = codec.iconv.defaultCharUnicode.charCodeAt(0);
this.overflow = [];
}
Utf32Decoder.prototype.write = function(src) {
if (src.length === 0)
return '';
var i = 0;
var codepoint = 0;
var dst = Buffer.alloc(src.length + 4);
var offset = 0;
var isLE = this.isLE;
var overflow = this.overflow;
var badChar = this.badChar;
if (overflow.length > 0) {
for (; i < src.length && overflow.length < 4; i++)
overflow.push(src[i]);
if (overflow.length === 4) {
// NOTE: codepoint is a signed int32 and can be negative.
// NOTE: We copied this block from below to help V8 optimize it (it works with array, not buffer).
if (isLE) {
codepoint = overflow[i] | (overflow[i+1] << 8) | (overflow[i+2] << 16) | (overflow[i+3] << 24);
} else {
codepoint = overflow[i+3] | (overflow[i+2] << 8) | (overflow[i+1] << 16) | (overflow[i] << 24);
}
overflow.length = 0;
offset = _writeCodepoint(dst, offset, codepoint, badChar);
}
}
// Main loop. Should be as optimized as possible.
for (; i < src.length - 3; i += 4) {
// NOTE: codepoint is a signed int32 and can be negative.
if (isLE) {
codepoint = src[i] | (src[i+1] << 8) | (src[i+2] << 16) | (src[i+3] << 24);
} else {
codepoint = src[i+3] | (src[i+2] << 8) | (src[i+1] << 16) | (src[i] << 24);
}
offset = _writeCodepoint(dst, offset, codepoint, badChar);
}
// Keep overflowing bytes.
for (; i < src.length; i++) {
overflow.push(src[i]);
}
return dst.slice(0, offset).toString('ucs2');
};
function _writeCodepoint(dst, offset, codepoint, badChar) {
// NOTE: codepoint is signed int32 and can be negative. We keep it that way to help V8 with optimizations.
if (codepoint < 0 || codepoint > 0x10FFFF) {
// Not a valid Unicode codepoint
codepoint = badChar;
}
// Ephemeral Planes: Write high surrogate.
if (codepoint >= 0x10000) {
codepoint -= 0x10000;
var high = 0xD800 | (codepoint >> 10);
dst[offset++] = high & 0xff;
dst[offset++] = high >> 8;
// Low surrogate is written below.
var codepoint = 0xDC00 | (codepoint & 0x3FF);
}
// Write BMP char or low surrogate.
dst[offset++] = codepoint & 0xff;
dst[offset++] = codepoint >> 8;
return offset;
};
Utf32Decoder.prototype.end = function() {
this.overflow.length = 0;
};
// == UTF-32 Auto codec =============================================================
// Decoder chooses automatically from UTF-32LE and UTF-32BE using BOM and space-based heuristic.
// Defaults to UTF-32LE. http://en.wikipedia.org/wiki/UTF-32
// Encoder/decoder default can be changed: iconv.decode(buf, 'utf32', {defaultEncoding: 'utf-32be'});
// Encoder prepends BOM (which can be overridden with (addBOM: false}).
exports.utf32 = Utf32AutoCodec;
exports.ucs4 = 'utf32';
function Utf32AutoCodec(options, iconv) {
this.iconv = iconv;
}
Utf32AutoCodec.prototype.encoder = Utf32AutoEncoder;
Utf32AutoCodec.prototype.decoder = Utf32AutoDecoder;
// -- Encoding
function Utf32AutoEncoder(options, codec) {
options = options || {};
if (options.addBOM === undefined)
options.addBOM = true;
this.encoder = codec.iconv.getEncoder(options.defaultEncoding || 'utf-32le', options);
}
Utf32AutoEncoder.prototype.write = function(str) {
return this.encoder.write(str);
};
Utf32AutoEncoder.prototype.end = function() {
return this.encoder.end();
};
// -- Decoding
function Utf32AutoDecoder(options, codec) {
this.decoder = null;
this.initialBufs = [];
this.initialBufsLen = 0;
this.options = options || {};
this.iconv = codec.iconv;
}
Utf32AutoDecoder.prototype.write = function(buf) {
if (!this.decoder) {
// Codec is not chosen yet. Accumulate initial bytes.
this.initialBufs.push(buf);
this.initialBufsLen += buf.length;
if (this.initialBufsLen < 32) // We need more bytes to use space heuristic (see below)
return '';
// We have enough bytes -> detect endianness.
var encoding = detectEncoding(this.initialBufs, this.options.defaultEncoding);
this.decoder = this.iconv.getDecoder(encoding, this.options);
var resStr = '';
for (var i = 0; i < this.initialBufs.length; i++)
resStr += this.decoder.write(this.initialBufs[i]);
this.initialBufs.length = this.initialBufsLen = 0;
return resStr;
}
return this.decoder.write(buf);
};
Utf32AutoDecoder.prototype.end = function() {
if (!this.decoder) {
var encoding = detectEncoding(this.initialBufs, this.options.defaultEncoding);
this.decoder = this.iconv.getDecoder(encoding, this.options);
var resStr = '';
for (var i = 0; i < this.initialBufs.length; i++)
resStr += this.decoder.write(this.initialBufs[i]);
var trail = this.decoder.end();
if (trail)
resStr += trail;
this.initialBufs.length = this.initialBufsLen = 0;
return resStr;
}
return this.decoder.end();
};
function detectEncoding(bufs, defaultEncoding) {
var b = [];
var charsProcessed = 0;
var invalidLE = 0, invalidBE = 0; // Number of invalid chars when decoded as LE or BE.
var bmpCharsLE = 0, bmpCharsBE = 0; // Number of BMP chars when decoded as LE or BE.
outer_loop:
for (var i = 0; i < bufs.length; i++) {
var buf = bufs[i];
for (var j = 0; j < buf.length; j++) {
b.push(buf[j]);
if (b.length === 4) {
if (charsProcessed === 0) {
// Check BOM first.
if (b[0] === 0xFF && b[1] === 0xFE && b[2] === 0 && b[3] === 0) {
return 'utf-32le';
}
if (b[0] === 0 && b[1] === 0 && b[2] === 0xFE && b[3] === 0xFF) {
return 'utf-32be';
}
}
if (b[0] !== 0 || b[1] > 0x10) invalidBE++;
if (b[3] !== 0 || b[2] > 0x10) invalidLE++;
if (b[0] === 0 && b[1] === 0 && (b[2] !== 0 || b[3] !== 0)) bmpCharsBE++;
if ((b[0] !== 0 || b[1] !== 0) && b[2] === 0 && b[3] === 0) bmpCharsLE++;
b.length = 0;
charsProcessed++;
if (charsProcessed >= 100) {
break outer_loop;
}
}
}
}
// Make decisions.
if (bmpCharsBE - invalidBE > bmpCharsLE - invalidLE) return 'utf-32be';
if (bmpCharsBE - invalidBE < bmpCharsLE - invalidLE) return 'utf-32le';
// Couldn't decide (likely all zeros or not enough data).
return defaultEncoding || 'utf-32le';
}

View File

@@ -74,7 +74,7 @@ Utf7Decoder.prototype.write = function(buf) {
if (i == lastI && buf[i] == minusChar) {// "+-" -> "+"
res += "+";
} else {
var b64str = base64Accum + this.iconv.decode(buf.slice(lastI, i), "ascii");
var b64str = base64Accum + buf.slice(lastI, i).toString();
res += this.iconv.decode(Buffer.from(b64str, 'base64'), "utf16-be");
}
@@ -91,7 +91,7 @@ Utf7Decoder.prototype.write = function(buf) {
if (!inBase64) {
res += this.iconv.decode(buf.slice(lastI), "ascii"); // Write direct chars.
} else {
var b64str = base64Accum + this.iconv.decode(buf.slice(lastI), "ascii");
var b64str = base64Accum + buf.slice(lastI).toString();
var canBeDecoded = b64str.length - (b64str.length % 8); // Minimal chunk: 2 quads -> 2x3 bytes -> 3 chars.
base64Accum = b64str.slice(canBeDecoded); // The rest will be decoded in future.
@@ -245,7 +245,7 @@ Utf7IMAPDecoder.prototype.write = function(buf) {
if (i == lastI && buf[i] == minusChar) { // "&-" -> "&"
res += "&";
} else {
var b64str = base64Accum + this.iconv.decode(buf.slice(lastI, i), "ascii").replace(/,/g, '/');
var b64str = base64Accum + buf.slice(lastI, i).toString().replace(/,/g, '/');
res += this.iconv.decode(Buffer.from(b64str, 'base64'), "utf16-be");
}
@@ -262,7 +262,7 @@ Utf7IMAPDecoder.prototype.write = function(buf) {
if (!inBase64) {
res += this.iconv.decode(buf.slice(lastI), "ascii"); // Write direct chars.
} else {
var b64str = base64Accum + this.iconv.decode(buf.slice(lastI), "ascii").replace(/,/g, '/');
var b64str = base64Accum + buf.slice(lastI).toString().replace(/,/g, '/');
var canBeDecoded = b64str.length - (b64str.length % 8); // Minimal chunk: 2 quads -> 2x3 bytes -> 3 chars.
base64Accum = b64str.slice(canBeDecoded); // The rest will be decoded in future.

View File

@@ -6,22 +6,15 @@
*--------------------------------------------------------------------------------------------*/
declare module 'iconv-lite' {
// Basic API
export function decode(buffer: Buffer, encoding: string, options?: Options): string;
export function encode(content: string, encoding: string, options?: Options): Buffer;
export function encodingExists(encoding: string): boolean;
// Stream API
export function decodeStream(encoding: string, options?: Options): NodeJS.ReadWriteStream;
export function encodeStream(encoding: string, options?: Options): NodeJS.ReadWriteStream;
// Low-level stream APIs
export function getEncoder(encoding: string, options?: Options): EncoderStream;
export function getDecoder(encoding: string, options?: Options): DecoderStream;
}
export interface Options {
@@ -29,13 +22,3 @@ export interface Options {
addBOM?: boolean;
defaultEncoding?: string;
}
export interface EncoderStream {
write(str: string): Buffer;
end(): Buffer | undefined;
}
export interface DecoderStream {
write(buf: Buffer): string;
end(): string | undefined;
}

View File

@@ -1,5 +1,7 @@
"use strict";
// Some environments don't have global Buffer (e.g. React Native).
// Solution would be installing npm modules "buffer" and "stream" explicitly.
var Buffer = require("safer-buffer").Buffer;
var bomHandling = require("./bom-handling"),
@@ -131,50 +133,21 @@ iconv.getDecoder = function getDecoder(encoding, options) {
return decoder;
}
// Streaming API
// NOTE: Streaming API naturally depends on 'stream' module from Node.js. Unfortunately in browser environments this module can add
// up to 100Kb to the output bundle. To avoid unnecessary code bloat, we don't enable Streaming API in browser by default.
// If you would like to enable it explicitly, please add the following code to your app:
// > iconv.enableStreamingAPI(require('stream'));
iconv.enableStreamingAPI = function enableStreamingAPI(stream_module) {
if (iconv.supportsStreams)
return;
// Dependency-inject stream module to create IconvLite stream classes.
var streams = require("./streams")(stream_module);
// Load extensions in Node. All of them are omitted in Browserify build via 'browser' field in package.json.
var nodeVer = typeof process !== 'undefined' && process.versions && process.versions.node;
if (nodeVer) {
// Not public API yet, but expose the stream classes.
iconv.IconvLiteEncoderStream = streams.IconvLiteEncoderStream;
iconv.IconvLiteDecoderStream = streams.IconvLiteDecoderStream;
// Streaming API.
iconv.encodeStream = function encodeStream(encoding, options) {
return new iconv.IconvLiteEncoderStream(iconv.getEncoder(encoding, options), options);
// Load streaming support in Node v0.10+
var nodeVerArr = nodeVer.split(".").map(Number);
if (nodeVerArr[0] > 0 || nodeVerArr[1] >= 10) {
require("./streams")(iconv);
}
iconv.decodeStream = function decodeStream(encoding, options) {
return new iconv.IconvLiteDecoderStream(iconv.getDecoder(encoding, options), options);
}
iconv.supportsStreams = true;
}
// Enable Streaming API automatically if 'stream' module is available and non-empty (the majority of environments).
var stream_module;
try {
stream_module = require("stream");
} catch (e) {}
if (stream_module && stream_module.Transform) {
iconv.enableStreamingAPI(stream_module);
} else {
// In rare cases where 'stream' module is not available by default, throw a helpful exception.
iconv.encodeStream = iconv.decodeStream = function() {
throw new Error("iconv-lite Streaming API is not enabled. Use iconv.enableStreamingAPI(require('stream')); to enable it.");
};
// Load Node primitive extensions.
require("./extend-node")(iconv);
}
if ("Ā" != "\u0100") {
console.error("iconv-lite warning: js files use non-utf8 encoding. See https://github.com/ashtuchkin/iconv-lite/wiki/Javascript-source-file-encodings for more info.");
console.error("iconv-lite warning: javascript files use encoding different from utf-8. See https://github.com/ashtuchkin/iconv-lite/wiki/Javascript-source-file-encodings for more info.");
}

View File

@@ -1,109 +1,121 @@
"use strict";
var Buffer = require("safer-buffer").Buffer;
var Buffer = require("buffer").Buffer,
Transform = require("stream").Transform;
// NOTE: Due to 'stream' module being pretty large (~100Kb, significant in browser environments),
// we opt to dependency-inject it instead of creating a hard dependency.
module.exports = function(stream_module) {
var Transform = stream_module.Transform;
// == Encoder stream =======================================================
function IconvLiteEncoderStream(conv, options) {
this.conv = conv;
options = options || {};
options.decodeStrings = false; // We accept only strings, so we don't need to decode them.
Transform.call(this, options);
// == Exports ==================================================================
module.exports = function(iconv) {
// Additional Public API.
iconv.encodeStream = function encodeStream(encoding, options) {
return new IconvLiteEncoderStream(iconv.getEncoder(encoding, options), options);
}
IconvLiteEncoderStream.prototype = Object.create(Transform.prototype, {
constructor: { value: IconvLiteEncoderStream }
});
IconvLiteEncoderStream.prototype._transform = function(chunk, encoding, done) {
if (typeof chunk != 'string')
return done(new Error("Iconv encoding stream needs strings as its input."));
try {
var res = this.conv.write(chunk);
if (res && res.length) this.push(res);
done();
}
catch (e) {
done(e);
}
iconv.decodeStream = function decodeStream(encoding, options) {
return new IconvLiteDecoderStream(iconv.getDecoder(encoding, options), options);
}
IconvLiteEncoderStream.prototype._flush = function(done) {
try {
var res = this.conv.end();
if (res && res.length) this.push(res);
done();
}
catch (e) {
done(e);
}
}
IconvLiteEncoderStream.prototype.collect = function(cb) {
var chunks = [];
this.on('error', cb);
this.on('data', function(chunk) { chunks.push(chunk); });
this.on('end', function() {
cb(null, Buffer.concat(chunks));
});
return this;
}
iconv.supportsStreams = true;
// == Decoder stream =======================================================
function IconvLiteDecoderStream(conv, options) {
this.conv = conv;
options = options || {};
options.encoding = this.encoding = 'utf8'; // We output strings.
Transform.call(this, options);
}
IconvLiteDecoderStream.prototype = Object.create(Transform.prototype, {
constructor: { value: IconvLiteDecoderStream }
});
IconvLiteDecoderStream.prototype._transform = function(chunk, encoding, done) {
if (!Buffer.isBuffer(chunk) && !(chunk instanceof Uint8Array))
return done(new Error("Iconv decoding stream needs buffers as its input."));
try {
var res = this.conv.write(chunk);
if (res && res.length) this.push(res, this.encoding);
done();
}
catch (e) {
done(e);
}
}
IconvLiteDecoderStream.prototype._flush = function(done) {
try {
var res = this.conv.end();
if (res && res.length) this.push(res, this.encoding);
done();
}
catch (e) {
done(e);
}
}
IconvLiteDecoderStream.prototype.collect = function(cb) {
var res = '';
this.on('error', cb);
this.on('data', function(chunk) { res += chunk; });
this.on('end', function() {
cb(null, res);
});
return this;
}
return {
IconvLiteEncoderStream: IconvLiteEncoderStream,
IconvLiteDecoderStream: IconvLiteDecoderStream,
};
// Not published yet.
iconv.IconvLiteEncoderStream = IconvLiteEncoderStream;
iconv.IconvLiteDecoderStream = IconvLiteDecoderStream;
iconv._collect = IconvLiteDecoderStream.prototype.collect;
};
// == Encoder stream =======================================================
function IconvLiteEncoderStream(conv, options) {
this.conv = conv;
options = options || {};
options.decodeStrings = false; // We accept only strings, so we don't need to decode them.
Transform.call(this, options);
}
IconvLiteEncoderStream.prototype = Object.create(Transform.prototype, {
constructor: { value: IconvLiteEncoderStream }
});
IconvLiteEncoderStream.prototype._transform = function(chunk, encoding, done) {
if (typeof chunk != 'string')
return done(new Error("Iconv encoding stream needs strings as its input."));
try {
var res = this.conv.write(chunk);
if (res && res.length) this.push(res);
done();
}
catch (e) {
done(e);
}
}
IconvLiteEncoderStream.prototype._flush = function(done) {
try {
var res = this.conv.end();
if (res && res.length) this.push(res);
done();
}
catch (e) {
done(e);
}
}
IconvLiteEncoderStream.prototype.collect = function(cb) {
var chunks = [];
this.on('error', cb);
this.on('data', function(chunk) { chunks.push(chunk); });
this.on('end', function() {
cb(null, Buffer.concat(chunks));
});
return this;
}
// == Decoder stream =======================================================
function IconvLiteDecoderStream(conv, options) {
this.conv = conv;
options = options || {};
options.encoding = this.encoding = 'utf8'; // We output strings.
Transform.call(this, options);
}
IconvLiteDecoderStream.prototype = Object.create(Transform.prototype, {
constructor: { value: IconvLiteDecoderStream }
});
IconvLiteDecoderStream.prototype._transform = function(chunk, encoding, done) {
if (!Buffer.isBuffer(chunk))
return done(new Error("Iconv decoding stream needs buffers as its input."));
try {
var res = this.conv.write(chunk);
if (res && res.length) this.push(res, this.encoding);
done();
}
catch (e) {
done(e);
}
}
IconvLiteDecoderStream.prototype._flush = function(done) {
try {
var res = this.conv.end();
if (res && res.length) this.push(res, this.encoding);
done();
}
catch (e) {
done(e);
}
}
IconvLiteDecoderStream.prototype.collect = function(cb) {
var res = '';
this.on('error', cb);
this.on('data', function(chunk) { res += chunk; });
this.on('end', function() {
cb(null, res);
});
return this;
}

View File

@@ -1,7 +1,7 @@
{
"name": "iconv-lite",
"description": "Convert character encodings in pure javascript.",
"version": "0.6.3",
"version": "0.4.24",
"license": "MIT",
"keywords": [
"iconv",
@@ -22,23 +22,25 @@
"node": ">=0.10.0"
},
"scripts": {
"coverage": "c8 _mocha --grep .",
"coverage": "istanbul cover _mocha -- --grep .",
"coverage-open": "open coverage/lcov-report/index.html",
"test": "mocha --reporter spec --grep ."
},
"browser": {
"stream": false
"./lib/extend-node": false,
"./lib/streams": false
},
"devDependencies": {
"async": "^3.2.0",
"c8": "^7.2.0",
"errto": "^0.2.1",
"iconv": "^2.3.5",
"mocha": "^3.5.3",
"request": "^2.88.2",
"semver": "^6.3.0",
"unorm": "^1.6.0"
"mocha": "^3.1.0",
"request": "~2.87.0",
"unorm": "*",
"errto": "*",
"async": "*",
"istanbul": "*",
"semver": "*",
"iconv": "*"
},
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
"safer-buffer": ">= 2.1.2 < 3"
}
}

19
api/node_modules/is-promise/LICENSE generated vendored
View File

@@ -1,19 +0,0 @@
Copyright (c) 2014 Forbes Lindesay
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,2 +0,0 @@
declare function isPromise<T, S>(obj: PromiseLike<T> | S): obj is PromiseLike<T>;
export default isPromise;

View File

@@ -1,6 +0,0 @@
module.exports = isPromise;
module.exports.default = isPromise;
function isPromise(obj) {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}

View File

@@ -1,3 +0,0 @@
export default function isPromise(obj) {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}

View File

@@ -1,30 +0,0 @@
{
"name": "is-promise",
"version": "4.0.0",
"description": "Test whether an object looks like a promises-a+ promise",
"main": "./index.js",
"scripts": {
"test": "node test"
},
"files": [
"index.js",
"index.mjs",
"index.d.ts"
],
"exports": {
".": [
{
"import": "./index.mjs",
"require": "./index.js",
"default": "./index.js"
},
"./index.js"
]
},
"repository": {
"type": "git",
"url": "https://github.com/then/is-promise.git"
},
"author": "ForbesLindesay",
"license": "MIT"
}

View File

@@ -1,33 +0,0 @@
<a href="https://promisesaplus.com/"><img src="https://promisesaplus.com/assets/logo-small.png" align="right" /></a>
# is-promise
Test whether an object looks like a promises-a+ promise
[![Build Status](https://img.shields.io/travis/then/is-promise/master.svg)](https://travis-ci.org/then/is-promise)
[![Dependency Status](https://img.shields.io/david/then/is-promise.svg)](https://david-dm.org/then/is-promise)
[![NPM version](https://img.shields.io/npm/v/is-promise.svg)](https://www.npmjs.org/package/is-promise)
## Installation
$ npm install is-promise
You can also use it client side via npm.
## API
```typescript
import isPromise from 'is-promise';
isPromise(Promise.resolve());//=>true
isPromise({then:function () {...}});//=>true
isPromise(null);//=>false
isPromise({});//=>false
isPromise({then: true})//=>false
```
## License
MIT

View File

@@ -1,31 +1,3 @@
1.1.0 / 2019-04-24
==================
* Add `test(string)` function
1.0.2 / 2019-04-19
==================
* Fix JSDoc comment for `parse` function
1.0.1 / 2018-10-20
==================
* Remove left over `parameters` property from class
1.0.0 / 2018-10-20
==================
This major release brings the module back to it's RFC 6838 roots. If you want
a module to parse the `Content-Type` or similar HTTP headers, use the
`content-type` module instead.
* Drop support for Node.js below 0.8
* Remove parameter handling, which is outside RFC 6838 scope
* Remove `parse(req)` and `parse(res)` signatures
* perf: enable strict mode
* perf: use a class for object creation
0.3.0 / 2014-09-07
==================

Some files were not shown because too many files have changed in this diff Show More