Compare commits
25
Commits
57e201b8fc
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd44db2213 | ||
|
|
e12936444d | ||
|
|
4ac3d8d53d | ||
|
|
e291ca8438 | ||
|
|
e697057b86 | ||
|
|
7d65ff813f | ||
|
|
51dfbe8cd8 | ||
|
|
e7243f6f26 | ||
|
|
4c2e04e78b | ||
|
|
a2266662ad | ||
|
|
b7e0bd0d92 | ||
|
|
f4fb3c550b | ||
|
|
93505e0e48 | ||
|
|
6ba93b2a92 | ||
|
|
db63a29365 | ||
|
|
bc30f2900b | ||
|
|
18d04e2c4a | ||
|
|
9d361013ba | ||
|
|
5b20adee48 | ||
|
|
4fc5acfebb | ||
|
|
1c176ea621 | ||
|
|
91cc898ec7 | ||
|
|
4e083bee73 | ||
|
|
da64472251 | ||
|
|
115f0f5671 |
No files matched your search
+17
@@ -0,0 +1,17 @@
|
|||||||
|
# SQLite-Datenbank (Laufzeitartefakt in sessions/)
|
||||||
|
sessions/*.db
|
||||||
|
sessions/*.db-wal
|
||||||
|
sessions/*.db-shm
|
||||||
|
|
||||||
|
# macOS-Artefakte
|
||||||
|
.DS_Store
|
||||||
|
sessions/*.txt
|
||||||
|
|
||||||
|
# Update-Dienst (Auftrag, Status, Protokoll — je Rechner)
|
||||||
|
sessions/update/
|
||||||
|
|
||||||
|
# Für den Bau auf dem Pi nicht nötig: Pakete installiert Docker selbst
|
||||||
|
# (npm ci im Dockerfile), Sicherungen und Uploads entstehen je Rechner.
|
||||||
|
node_modules/
|
||||||
|
sessions/backups/
|
||||||
|
sessions/uploads/
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
# AGENTS.md
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Single-page app for tracking engineering projects across weekly sessions
|
|
||||||
("Sitzungen"). Vanilla JS/HTML/CSS frontend + Node.js/Express server. No build
|
|
||||||
step, no bundler, no tests. Data is persisted as semicolon-separated CSVs
|
|
||||||
under `sessions/`.
|
|
||||||
|
|
||||||
Two HTML entrypoints, both served statically by Express:
|
|
||||||
- `Test.html` — the main app (tabs: Projektboard, Auslastung, Projekte,
|
|
||||||
Projektbeteiligte). Served at `/`.
|
|
||||||
- `project-plan.html` — per-project Gantt/TODO planning view, opened in a new
|
|
||||||
tab via `openProjectPlan()` in `js/gantt.js`. Loads the shared helper modules
|
|
||||||
(`globals`, `utils`, `ui`, `date-picker`, `csv`, `server`, `gantt`) plus the
|
|
||||||
plan modules under `js/plan/` — NOT the full main module set.
|
|
||||||
|
|
||||||
All UI strings/comments/CSV content are German. Preserve German in
|
|
||||||
user-facing text.
|
|
||||||
|
|
||||||
## File structure
|
|
||||||
|
|
||||||
```
|
|
||||||
.
|
|
||||||
├── server.js # Express server + CSV API
|
|
||||||
├── package.json # deps: express, cors only
|
|
||||||
├── docker-compose.yml # at repo ROOT; build context ., dockerfile Docker/Dockerfile
|
|
||||||
├── Docker/Dockerfile # node:20-alpine
|
|
||||||
├── .dockerignore
|
|
||||||
├── .vscode/extensions.json
|
|
||||||
├── public/
|
|
||||||
│ ├── Test.html # main app (served at /)
|
|
||||||
│ ├── js/ # shared modules (classic scripts, global fns)
|
|
||||||
│ │ ├── globals.js # Konstanten (LPHS, ROLES, LPH-Farben, DEPARTMENTS) + db/currentDepartment
|
|
||||||
│ │ ├── utils.js # esc, uid, Datum-/Differenz-Helfer, personIdForName, weekSortKey-Quelle
|
|
||||||
│ │ ├── ui.js # LocalStorage-UI-State, Header-Collapse, Theme
|
|
||||||
│ │ ├── date-picker.js # Datums-/Bereichspicker-Overlays
|
|
||||||
│ │ ├── csv.js # CSV-Parser/Serializer + weekSortKey, filenameForWeek
|
|
||||||
│ │ ├── server.js # baseUrl, sendCsvToServer, deleteCsvFromServer, blobToDataUrl
|
|
||||||
│ │ ├── store.js # save/syncAllToServer, switchDepartment, load, seed
|
|
||||||
│ │ ├── weeks.js # Wochen-Auswahl, -Erzeugung, -Klonen, Auto-Lock, -Löschung
|
|
||||||
│ │ ├── gantt.js # GANTT-Diagramm (buildGanttHtml, openProjectPlan), geshared
|
|
||||||
│ │ ├── showmode.js # Show-Mode (Präsentationsmodus)
|
|
||||||
│ │ ├── tabs-auth.js # showTab, confirmPassword (SHA256-Passwortschutz)
|
|
||||||
│ │ ├── pdf.js # PDF-Export (pdfmake, A3 quer)
|
|
||||||
│ │ └── init.js # renderAll/initUiState + Seiteneinstieg (nur Test.html)
|
|
||||||
│ │ ├── board/ # tab-specific: Projektboard
|
|
||||||
│ │ │ └── board.js # Projektboard-Rendering, Status-Editor, Anwesenheit, Zuordnung
|
|
||||||
│ │ ├── people/ # tab-specific: Projektbeteiligte
|
|
||||||
│ │ │ └── people.js # Personen-Tab-Rendering + Drag-and-Drop + Abwesenheiten
|
|
||||||
│ │ ├── projects/ # tab-specific: Projekte
|
|
||||||
│ │ │ └── projects.js # Projekte-Tab-Rendering + Drag-and-Drop
|
|
||||||
│ │ ├── load/ # tab-specific: Auslastung
|
|
||||||
│ │ │ └── load.js # Auslastungs-Tab, LP-Modal, Neues-Projekt-Modal
|
|
||||||
│ ├── project-plan.html # Gantt/TODO planning view
|
|
||||||
│ ├── js/plan/ # planning modules (nur von project-plan.html geladen)
|
|
||||||
│ │ ├── plan-state.js # PlanApp-Zustand, Datenladepath, savePlan, Toasts
|
|
||||||
│ │ ├── plan-build-gantt.js # buildGanttHtml-Erweiterung (TODO- und Meilensteinzeilen)
|
|
||||||
│ │ ├── plan-gantt.js # renderPlanGantt, Abhängigkeitslinien, Zoom, Ressourcen-Load
|
|
||||||
│ │ ├── plan-drag.js # GANTT-Balken-Drag/Resize + TODO-/Meilenstein-Zeilen-Drag
|
|
||||||
│ │ ├── plan-modals.js # TODO-, Meilenstein- und Abwesenheits-Modals
|
|
||||||
│ │ └── plan-schedule.js # Kritischer Pfad, Auto-Schedule, Mail-Link
|
|
||||||
│ ├── styles.css
|
|
||||||
│ └── dein_bild.ico
|
|
||||||
└── sessions/ # data; bind-mounted into Docker container
|
|
||||||
├── ELT/ HLS/ ARCH/ # one folder per department
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install
|
|
||||||
npm start # http://localhost:3003, or PORT=3004 if occupied
|
|
||||||
docker compose up --build # Docker variant (compose file is at repo root)
|
|
||||||
```
|
|
||||||
|
|
||||||
- PORT env var overrides default 3003 in `server.js`.
|
|
||||||
- Frontend must be served over HTTP: `baseUrl()` in `js/server.js` uses
|
|
||||||
`window.location.origin` or falls back to `http://localhost:3003` when opened
|
|
||||||
via `file://` (then only seed/demo data loads).
|
|
||||||
- Check `/health` returns `{ok:true}` after starting.
|
|
||||||
|
|
||||||
## Server API (`server.js`)
|
|
||||||
|
|
||||||
- `GET /` → `Test.html`; `GET /health`.
|
|
||||||
- Deep links per department + tab: `GET /<Dept>/<Tab>` → `Test.html`, e.g.
|
|
||||||
`/HLS/Auslastung` or `/ELT/Projektübersicht`. Tab slugs (`TAB_SLUGS` in
|
|
||||||
`js/globals.js`) are `Projektübersicht|Auslastung|Projekte|Projektbeteiligte`;
|
|
||||||
anything else returns 404. Exception: `GET /<Dept>/Projektplan(/<ProjName>)?`
|
|
||||||
(`PLAN_SLUG`) serves `project-plan.html`, the GANTT view, with
|
|
||||||
`weekLabel|projNr` as query params (the project name is a readable path
|
|
||||||
segment only; identification happens via weekLabel + projNr). The frontend
|
|
||||||
reads/writes these routes via `applyUrlRoute()`/`updateRouteUrl()` in
|
|
||||||
`js/ui.js` and all static assets in the HTML use **absolute** paths
|
|
||||||
(`/js/...`, `/styles.css`) so they resolve on nested routes.
|
|
||||||
- `GET /load-all?department=` → `{sessions:[{filename,content}], people}` with
|
|
||||||
`_people.csv` content in `people`; sessions sorted by filename.
|
|
||||||
- `POST /save-csv {filename, csv, department}` → writes UTF-8 BOM CSV.
|
|
||||||
- `POST /delete-csv {filename, department}`.
|
|
||||||
- Valid departments hard-coded as `ELT|HLS|ARCH` in `server.js`
|
|
||||||
(`VALID_DEPARTMENTS`); anything else silently falls back to `ELT`. Same set in
|
|
||||||
`DEPARTMENTS` in `js/globals.js`.
|
|
||||||
- `safeName()` strictly validates filenames (path traversal protection);
|
|
||||||
static assets get `Cache-Control: no-store`.
|
|
||||||
- JSON body limit is 2 MB.
|
|
||||||
|
|
||||||
## CSV format
|
|
||||||
|
|
||||||
- Delimiter `;`, UTF-8 with BOM. Hand-editing is possible but risky; the parser
|
|
||||||
reconstructs objects from the fixed column layout.
|
|
||||||
- Week files `<Label>.csv` (e.g. `KW_30___2026.csv` — `filenameForWeek()`
|
|
||||||
replaces non-word chars with `_`). Row layout is positional:
|
|
||||||
`WOCHE` (label, date, locked flag) → `PERSONEN` (`name|role|fullName` per
|
|
||||||
column) → header row → `ANWESENHEIT` → one row per project.
|
|
||||||
- `_people.csv` per department: `NAME;Rollen;Voller Name;E-Mail;Abwesenheiten`
|
|
||||||
(absences as JSON). People are global per department; new people are copied
|
|
||||||
into the current week. Historical weeks keep their own person copies, so
|
|
||||||
deleting a global person leaves old sessions unaffected no matter what.
|
|
||||||
- Don't change the column layout without updating both `weekToCsv()` and
|
|
||||||
`parseSessionCsv()` (`js/csv.js`), which must stay inverse of each other.
|
|
||||||
|
|
||||||
## Frontend architecture
|
|
||||||
|
|
||||||
- The frontend is split into small classic non-module `<script>` files under
|
|
||||||
`public/js/`; **all functions are globals on `window`**. Two HTML files include
|
|
||||||
them via `<script src>` tags; load order in the HTML defines dependencies,
|
|
||||||
since there is no bundler:
|
|
||||||
- `Test.html` loads `globals → utils → ui → date-picker → csv → server →
|
|
||||||
store → weeks` (shared modules), then the tab-specific folders `board →
|
|
||||||
people → projects → load`, finally `gantt → showmode → tabs-auth → pdf →
|
|
||||||
init`. See the exact list in `Test.html`.
|
|
||||||
- `project-plan.html` loads only `globals → utils → ui → date-picker → csv →
|
|
||||||
server → gantt` plus the `js/plan/*` modules. It must NOT load modules that
|
|
||||||
touch main-only page elements (`store`, `weeks`, `board`, …, `init`).
|
|
||||||
- Shared state object `window.PlanApp` (`js/plan/plan-state.js`) holds all
|
|
||||||
project-plan page state (week, project, zoom level, drag/`modal`-state),
|
|
||||||
`db`/`currentDepartment` live in `js/globals.js`.
|
|
||||||
- State: `let db = { weeks, currentWeek, globalPeople }` (in-memory only).
|
|
||||||
- Project IDs are regenerated on every CSV load, so projects are addressed
|
|
||||||
cross-page by `department` + week `label` + `projNr` (+ readable projName path
|
|
||||||
segment) via URL params: `openProjectPlan()` builds
|
|
||||||
`/HLS/Projektplan/<ProjName>?weekLabel=…&projNr=…`, `findProjectForPlan`
|
|
||||||
resolves them.
|
|
||||||
- Person IDs are stable across reloads via `personIdForName(name)` →
|
|
||||||
`p_<lowercased alnum>`.
|
|
||||||
- `save()` debounces 500 ms then `syncAllToServer()` writes **all** weeks plus
|
|
||||||
`_people.csv` — not just the changed week. Exception: `js/plan/plan-state.js`
|
|
||||||
`savePlan()` bypasses `save()` and writes only the current week's CSV directly
|
|
||||||
via `sendCsvToServer()`.
|
|
||||||
- Weeks are sorted by calendar week extracted from the label (`KW XX / YYYY`)
|
|
||||||
via `weekSortKey()`, not alphabetically/by filename.
|
|
||||||
- `applyAutoLock()` locks every week except the chronologically last one after
|
|
||||||
load/clone/new-week; `localStorage` UI state is not restored for locked tabs.
|
|
||||||
- `localStorage` keys: `projuebersicht_v1` (department only — tab is never
|
|
||||||
restored from storage; at startup the tab comes from the deep-link URL if
|
|
||||||
present, otherwise `board`), `projuebersicht-theme`, header-collapse key. Do
|
|
||||||
**not** store data in `localStorage`; it is UI state only.
|
|
||||||
- Deep-link URLs (`/<Dept>/<Tab>`) are shared via the address bar:
|
|
||||||
`updateRouteUrl()` rewrites the path via `history.replaceState` on tab and
|
|
||||||
department switches; protected tabs keep the URL but open the password modal.
|
|
||||||
|
|
||||||
## Auth / security quirk
|
|
||||||
|
|
||||||
- The `projects` and `people` tabs, week deletion, and week unlock are gated by
|
|
||||||
a password modal comparing `CryptoJS.SHA256(input)` to
|
|
||||||
`PROTECTED_PASSWORD_HASH` (`js/globals.js`, modal in `js/tabs-auth.js`).
|
|
||||||
Client-side only — not real security. CryptoJS and pdfmake (PDF export) are
|
|
||||||
loaded from `cdnjs.cloudflare.com` in `Test.html`, so those features need
|
|
||||||
internet access and won't work offline. `project-plan.html` loads neither.
|
|
||||||
- User-provided status HTML is sanitized on render via a small whitelist
|
|
||||||
(`sanitizeStatusHtml` in `js/board/board.js`) to mitigate XSS.
|
|
||||||
|
|
||||||
## Docker
|
|
||||||
|
|
||||||
- `docker compose up --build` from the repo root. Compose maps `3003:3003`,
|
|
||||||
bind-mounts `./sessions:/app/sessions`, `restart: unless-stopped`.
|
|
||||||
- Dockerfile: `node:20-alpine`, `npm ci --production`, runs as non-root `node`,
|
|
||||||
HEALTHCHECK on `/health`. `.dockerignore` excludes `Docker/*` (except
|
|
||||||
`Docker/Dockerfile`), `node_modules/`, `.git`, `npm-debug.log`.
|
|
||||||
|
|
||||||
## Conventions & verification
|
|
||||||
|
|
||||||
- Git repo; commits are German (e.g. "PDF Export von A4 auf A3 …"). Remote:
|
|
||||||
`http://192.168.3.187:3000/erik/projektuebersicht.git`.
|
|
||||||
- No linter, formatter, typecheck, or test suite. Manual verification only:
|
|
||||||
start server, open `http://localhost:3003`, exercises the relevant tab,
|
|
||||||
confirm CSV changes in `sessions/`.
|
|
||||||
+13
-7
@@ -1,24 +1,30 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
FROM node:20-alpine
|
# node:22-alpine, weil node:sqlite (SQLite-Persistenz in server.js) erst ab
|
||||||
|
# Node 22.13 (ohne Flag) stabil verfügbar ist.
|
||||||
|
FROM node:22-alpine
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# package.json und package-lock.json zuerst kopieren, damit npm-Installation gecacht wird
|
# package.json und package-lock.json zuerst kopieren, damit npm-Installation gecacht wird
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
RUN npm ci --production
|
RUN npm config set fetch-retries 6 && npm config set fetch-retry-maxtimeout 180000 && npm config set fetch-timeout 600000 && npm ci --omit=dev
|
||||||
|
|
||||||
# Nur die Laufzeit-Dateien kopieren
|
# Nur die Laufzeit-Dateien kopieren
|
||||||
COPY server.js ./
|
COPY server.js pdf-arbeit.js ./
|
||||||
COPY public ./public
|
COPY public ./public
|
||||||
|
|
||||||
# Sessions-Ordner fuer die CSV-Daten sicherstellen
|
# Sessions-Ordner fuer die SQLite-Datenbank sicherstellen
|
||||||
RUN mkdir -p /app/sessions && chown -R node:node /app
|
RUN mkdir -p /app/sessions && chown -R node:node /app
|
||||||
|
|
||||||
USER node
|
USER node
|
||||||
|
|
||||||
EXPOSE 3003
|
EXPOSE 3003
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
# wget (busybox) statt eines eigenen Node-Prozesses je Prüfung: auf dem
|
||||||
CMD node -e "require('http').get('http://localhost:3003/health', r => r.statusCode === 200 ? process.exit(0) : process.exit(1)).on('error', () => process.exit(1))"
|
# Pi Zero kostete jeder Healthcheck sonst ~40 MB und eine Sekunde CPU.
|
||||||
|
HEALTHCHECK --interval=60s --timeout=10s --start-period=60s --retries=3 \
|
||||||
|
CMD wget -qO- http://127.0.0.1:3003/health >/dev/null || exit 1
|
||||||
|
|
||||||
CMD ["node", "server.js"]
|
# --max-old-space-size: Node räumt früher auf, statt bis an die
|
||||||
|
# Container-Grenze zu wachsen (Pi Zero 2, 512 MB). Siehe docker-compose.yml.
|
||||||
|
CMD ["node", "--max-old-space-size=160", "server.js"]
|
||||||
Executable
+236
@@ -0,0 +1,236 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# pi-einrichten.sh — macht Ubuntu Server auf dem Raspberry Pi Zero 2 (512 MB)
|
||||||
|
# stabil für die Projektübersicht. Mehrfach ausführbar (prüft jeden Schritt).
|
||||||
|
#
|
||||||
|
# sudo bash Docker/pi-einrichten.sh # Standard
|
||||||
|
# sudo bash Docker/pi-einrichten.sh --snapd-aus # zusätzlich snapd abschalten
|
||||||
|
#
|
||||||
|
# Was es tut (Einzelheiten in CLAUDE.md, "Betrieb auf dem Pi"):
|
||||||
|
# 1. Hardware-Watchdog: hängt das System, startet der Pi selbst neu
|
||||||
|
# 2. zram: komprimierter Auslagerungsspeicher im RAM (statt SD-Karte)
|
||||||
|
# 3. Speichergrenzen für Container erlauben (cgroup memory)
|
||||||
|
# 4. gpu_mem=16 — kein Bildschirm, mehr RAM für Linux
|
||||||
|
# 5. Unnötige Dienste aus (multipathd, ModemManager, udisks2 …)
|
||||||
|
# 6. earlyoom: greift ein, BEVOR das System im Speichermangel festfriert
|
||||||
|
# 7. Systemprotokoll auf 50 MB begrenzen
|
||||||
|
# 8. Nächtlicher Neustart des Containers (3:30 Uhr)
|
||||||
|
# 9. Prüfung der Stromversorgung (Unterspannung)
|
||||||
|
# 10. WLAN: Energiesparen aus, Wächter verbindet neu und startet den Pi
|
||||||
|
# neu, wenn das Netz 10 Minuten lang weg bleibt
|
||||||
|
set -u
|
||||||
|
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then echo "Bitte mit sudo ausführen."; exit 1; fi
|
||||||
|
|
||||||
|
SNAPD_AUS=0
|
||||||
|
[ "${1:-}" = "--snapd-aus" ] && SNAPD_AUS=1
|
||||||
|
|
||||||
|
NEUSTART_NOETIG=0
|
||||||
|
FIRMWARE=/boot/firmware
|
||||||
|
[ -d "$FIRMWARE" ] || FIRMWARE=/boot
|
||||||
|
ok() { echo " ✓ $*"; }
|
||||||
|
info() { echo " • $*"; }
|
||||||
|
warn() { echo " ! $*"; }
|
||||||
|
|
||||||
|
echo "== Projektübersicht: Pi einrichten =="
|
||||||
|
echo "System: $(. /etc/os-release; echo "$PRETTY_NAME"), $(uname -m), $(free -m | awk '/Mem:/{print $2}') MB RAM"
|
||||||
|
|
||||||
|
# 1. Hardware-Watchdog ------------------------------------------------------
|
||||||
|
echo "[1] Hardware-Watchdog"
|
||||||
|
mkdir -p /etc/systemd/system.conf.d
|
||||||
|
cat > /etc/systemd/system.conf.d/10-watchdog.conf <<'EOF'
|
||||||
|
# Hängt das System, löst der Watchdog des Pi nach 14 s einen Neustart aus
|
||||||
|
# (der bcm2835-Watchdog kann höchstens ~15 s).
|
||||||
|
[Manager]
|
||||||
|
RuntimeWatchdogSec=14s
|
||||||
|
RebootWatchdogSec=2min
|
||||||
|
EOF
|
||||||
|
if [ -e /dev/watchdog ]; then ok "Watchdog aktiv nach Neustart (/dev/watchdog vorhanden)"
|
||||||
|
else warn "/dev/watchdog fehlt — Kernelmodul bcm2835_wdt wird beim Start geladen"; echo bcm2835_wdt > /etc/modules-load.d/watchdog.conf; NEUSTART_NOETIG=1; fi
|
||||||
|
systemctl daemon-reexec
|
||||||
|
|
||||||
|
# 2. zram -------------------------------------------------------------------
|
||||||
|
echo "[2] zram (komprimierter Auslagerungsspeicher)"
|
||||||
|
apt-get update -qq
|
||||||
|
if apt-cache show systemd-zram-generator >/dev/null 2>&1; then
|
||||||
|
apt-get install -y -qq systemd-zram-generator >/dev/null
|
||||||
|
cat > /etc/systemd/zram-generator.conf <<'EOF'
|
||||||
|
[zram0]
|
||||||
|
zram-size = ram / 2
|
||||||
|
compression-algorithm = zstd
|
||||||
|
swap-priority = 100
|
||||||
|
EOF
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl restart systemd-zram-setup@zram0.service 2>/dev/null || true
|
||||||
|
ok "systemd-zram-generator eingerichtet (halber RAM, zstd)"
|
||||||
|
else
|
||||||
|
apt-get install -y -qq zram-tools >/dev/null
|
||||||
|
printf 'ALGO=zstd\nPERCENT=50\nPRIORITY=100\n' > /etc/default/zramswap
|
||||||
|
systemctl restart zramswap 2>/dev/null || true
|
||||||
|
ok "zram-tools eingerichtet (halber RAM, zstd)"
|
||||||
|
fi
|
||||||
|
cat > /etc/sysctl.d/90-zram.conf <<'EOF'
|
||||||
|
# zram ist schnell: früh auslagern, Seiten einzeln holen
|
||||||
|
vm.swappiness=100
|
||||||
|
vm.page-cluster=0
|
||||||
|
vm.vfs_cache_pressure=200
|
||||||
|
EOF
|
||||||
|
sysctl -q --system
|
||||||
|
swapon --show | sed 's/^/ /'
|
||||||
|
|
||||||
|
# 3. Speichergrenzen für Container -------------------------------------------
|
||||||
|
echo "[3] Speichergrenzen für Container (cgroup memory)"
|
||||||
|
if grep -qw memory /sys/fs/cgroup/cgroup.controllers 2>/dev/null; then
|
||||||
|
ok "bereits verfügbar"
|
||||||
|
else
|
||||||
|
CMDLINE="$FIRMWARE/cmdline.txt"
|
||||||
|
if [ -f "$CMDLINE" ] && ! grep -q "cgroup_enable=memory" "$CMDLINE"; then
|
||||||
|
cp "$CMDLINE" "$CMDLINE.vor-projektuebersicht"
|
||||||
|
sed -i '1 s/$/ cgroup_enable=memory cgroup_memory=1/' "$CMDLINE"
|
||||||
|
ok "in $CMDLINE ergänzt (Sicherung: cmdline.txt.vor-projektuebersicht)"
|
||||||
|
NEUSTART_NOETIG=1
|
||||||
|
else
|
||||||
|
warn "nicht verfügbar — $CMDLINE prüfen"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. GPU-Speicher -----------------------------------------------------------
|
||||||
|
echo "[4] GPU-Speicher"
|
||||||
|
CONFIG="$FIRMWARE/config.txt"
|
||||||
|
if [ -f "$CONFIG" ]; then
|
||||||
|
if grep -q "^gpu_mem=" "$CONFIG"; then sed -i 's/^gpu_mem=.*/gpu_mem=16/' "$CONFIG"
|
||||||
|
else printf '\n# Projektübersicht: kein Bildschirm, mehr RAM für Linux\ngpu_mem=16\n' >> "$CONFIG"; fi
|
||||||
|
ok "gpu_mem=16"
|
||||||
|
NEUSTART_NOETIG=1
|
||||||
|
else warn "$CONFIG nicht gefunden"; fi
|
||||||
|
|
||||||
|
# 5. Unnötige Dienste -------------------------------------------------------
|
||||||
|
echo "[5] Unnötige Dienste abschalten"
|
||||||
|
for d in multipathd multipathd.socket ModemManager udisks2 packagekit bluetooth avahi-daemon avahi-daemon.socket; do
|
||||||
|
if systemctl list-unit-files "$d.service" "$d" 2>/dev/null | grep -q "$d"; then
|
||||||
|
systemctl disable --now "$d" >/dev/null 2>&1 && ok "$d aus"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$SNAPD_AUS" = 1 ]; then
|
||||||
|
if command -v snap >/dev/null && snap list docker >/dev/null 2>&1; then
|
||||||
|
warn "Docker ist als Snap installiert — snapd bleibt an"
|
||||||
|
else
|
||||||
|
systemctl disable --now snapd.service snapd.socket snapd.seeded.service >/dev/null 2>&1 && ok "snapd aus"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
info "snapd bleibt an (spart ~40 MB mit --snapd-aus)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 6. earlyoom ---------------------------------------------------------------
|
||||||
|
echo "[6] earlyoom"
|
||||||
|
apt-get install -y -qq earlyoom >/dev/null
|
||||||
|
cat > /etc/default/earlyoom <<'EOF'
|
||||||
|
# Bei unter 5 % freiem RAM UND unter 10 % freiem Swap den größten Prozess
|
||||||
|
# beenden — nie SSH, Docker oder systemd. Der App-Container startet dann neu.
|
||||||
|
# Ohne innere Anführungszeichen: systemd reicht sie sonst wörtlich weiter.
|
||||||
|
EARLYOOM_ARGS="-m 5 -s 10 -r 3600 --avoid ^(sshd|dockerd|containerd|systemd|systemd-journal)$ --prefer ^node$"
|
||||||
|
EOF
|
||||||
|
systemctl enable --now earlyoom >/dev/null 2>&1
|
||||||
|
systemctl restart earlyoom
|
||||||
|
ok "aktiv"
|
||||||
|
|
||||||
|
# 7. Systemprotokoll --------------------------------------------------------
|
||||||
|
echo "[7] Systemprotokoll begrenzen"
|
||||||
|
mkdir -p /etc/systemd/journald.conf.d
|
||||||
|
printf '[Journal]\nSystemMaxUse=50M\n' > /etc/systemd/journald.conf.d/10-groesse.conf
|
||||||
|
systemctl restart systemd-journald
|
||||||
|
ok "höchstens 50 MB"
|
||||||
|
|
||||||
|
# 8. Nächtlicher Neustart ---------------------------------------------------
|
||||||
|
echo "[8] Nächtlicher Neustart des Containers"
|
||||||
|
cat > /etc/cron.d/projektuebersicht <<'EOF'
|
||||||
|
# Projektübersicht: frischer Speicher jede Nacht (gegen schleichende Lecks)
|
||||||
|
30 3 * * * root docker restart projektuebersicht >/dev/null 2>&1
|
||||||
|
EOF
|
||||||
|
systemctl enable docker >/dev/null 2>&1
|
||||||
|
ok "täglich 3:30 Uhr; Docker startet beim Booten"
|
||||||
|
|
||||||
|
# 9. Stromversorgung --------------------------------------------------------
|
||||||
|
echo "[9] Stromversorgung"
|
||||||
|
T=""
|
||||||
|
[ -r /sys/devices/platform/soc/soc:firmware/get_throttled ] && T=$(cat /sys/devices/platform/soc/soc:firmware/get_throttled)
|
||||||
|
command -v vcgencmd >/dev/null && T=$(vcgencmd get_throttled | cut -d= -f2)
|
||||||
|
if [ -z "$T" ]; then info "nicht auslesbar (Paket libraspberrypi-bin installieren)"
|
||||||
|
elif [ "$((T))" -eq 0 ]; then ok "keine Unterspannung seit dem Start ($T)"
|
||||||
|
else warn "Unterspannung/Drosselung erkannt ($T) — besseres Netzteil (5 V / 2,5 A) verwenden!"; fi
|
||||||
|
|
||||||
|
# 10. WLAN ------------------------------------------------------------------
|
||||||
|
# Befund 29.10.: Der Pi "stürzte" nicht ab, sondern verlor das WLAN
|
||||||
|
# (wlan0: Lost carrier) und kam danach nie wieder in das Netz
|
||||||
|
# (ASSOC-REJECT in Schleife) — erreichbar war er erst nach Stecker ziehen.
|
||||||
|
echo "[10] WLAN stabil halten"
|
||||||
|
apt-get install -y -qq iw >/dev/null 2>&1
|
||||||
|
cat > /etc/systemd/system/wlan-energiesparen-aus.service <<'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=WLAN-Energiesparen aus (Pi Zero 2 verliert sonst die Verbindung)
|
||||||
|
After=sys-subsystem-net-devices-wlan0.device
|
||||||
|
Wants=sys-subsystem-net-devices-wlan0.device
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/sbin/iw dev wlan0 set power_save off
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
cat > /usr/local/sbin/wlan-waechter <<'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
# Prüft jede Minute, ob das Gateway erreichbar ist. Ohne Netz: alle 2
|
||||||
|
# Minuten WLAN neu verbinden, nach 10 Minuten den Pi neu starten.
|
||||||
|
ZAEHLER=/run/wlan-waechter.zaehler
|
||||||
|
GW=$(ip route | awk '/^default/{print $3; exit}')
|
||||||
|
if [ -n "$GW" ] && ping -c 2 -W 3 "$GW" >/dev/null 2>&1; then
|
||||||
|
rm -f "$ZAEHLER"; exit 0
|
||||||
|
fi
|
||||||
|
N=$(( $(cat "$ZAEHLER" 2>/dev/null || echo 0) + 1 ))
|
||||||
|
echo "$N" > "$ZAEHLER"
|
||||||
|
logger -t wlan-waechter "kein Netz seit $N Minute(n)"
|
||||||
|
if [ "$N" -ge 10 ]; then
|
||||||
|
logger -t wlan-waechter "Neustart des Pi"; systemctl reboot
|
||||||
|
elif [ $((N % 2)) -eq 0 ]; then
|
||||||
|
logger -t wlan-waechter "WLAN neu verbinden"
|
||||||
|
wpa_cli -i wlan0 reassociate >/dev/null 2>&1
|
||||||
|
if [ "$N" -ge 4 ]; then
|
||||||
|
ip link set wlan0 down; sleep 2; ip link set wlan0 up
|
||||||
|
networkctl reconfigure wlan0 >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
EOF
|
||||||
|
chmod 755 /usr/local/sbin/wlan-waechter
|
||||||
|
cat > /etc/systemd/system/wlan-waechter.service <<'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=WLAN-Wächter der Projektübersicht
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/sbin/wlan-waechter
|
||||||
|
EOF
|
||||||
|
cat > /etc/systemd/system/wlan-waechter.timer <<'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=WLAN-Wächter jede Minute
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnBootSec=3min
|
||||||
|
OnUnitActiveSec=1min
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
|
EOF
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable --now wlan-energiesparen-aus.service >/dev/null 2>&1
|
||||||
|
systemctl enable --now wlan-waechter.timer >/dev/null 2>&1
|
||||||
|
if iw dev wlan0 get power_save 2>/dev/null | grep -q off; then ok "Energiesparen aus"; else warn "Energiesparen ließ sich nicht abschalten"; fi
|
||||||
|
ok "Wächter aktiv (neu verbinden ab 2 min, Neustart nach 10 min ohne Netz)"
|
||||||
|
|
||||||
|
echo
|
||||||
|
if [ "$NEUSTART_NOETIG" = 1 ]; then
|
||||||
|
echo "Fertig. Bitte den Pi jetzt neu starten: sudo reboot"
|
||||||
|
else
|
||||||
|
echo "Fertig."
|
||||||
|
fi
|
||||||
|
echo "Danach prüfen: bash Docker/pi-pruefen.sh"
|
||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# pi-pruefen.sh — zeigt, ob der Pi und die Projektübersicht stabil laufen.
|
||||||
|
# sudo bash Docker/pi-pruefen.sh (ohne sudo fehlen Docker und Strom)
|
||||||
|
echo "== Speicher =="
|
||||||
|
free -m
|
||||||
|
echo
|
||||||
|
echo "== Auslagerung (zram) =="
|
||||||
|
swapon --show 2>/dev/null || echo "keine"
|
||||||
|
echo
|
||||||
|
echo "== Watchdog =="
|
||||||
|
systemctl show -p RuntimeWatchdogUSec 2>/dev/null
|
||||||
|
[ -e /dev/watchdog ] && echo "/dev/watchdog vorhanden" || echo "/dev/watchdog FEHLT"
|
||||||
|
echo
|
||||||
|
echo "== Speichergrenzen für Container =="
|
||||||
|
grep -qw memory /sys/fs/cgroup/cgroup.controllers 2>/dev/null && echo "verfügbar" || echo "NICHT verfügbar (cmdline.txt, siehe pi-einrichten.sh)"
|
||||||
|
echo
|
||||||
|
echo "== Container =="
|
||||||
|
docker ps -a --filter name=projektuebersicht --format 'Status: {{.Status}}'
|
||||||
|
docker inspect projektuebersicht --format 'Neustarts: {{.RestartCount}} Grenze: {{.HostConfig.Memory}} Byte zuletzt gestartet: {{.State.StartedAt}} OOM: {{.State.OOMKilled}}' 2>/dev/null
|
||||||
|
docker stats --no-stream --format 'Verbrauch: {{.MemUsage}} ({{.MemPerc}}) CPU: {{.CPUPerc}}' projektuebersicht 2>/dev/null
|
||||||
|
echo
|
||||||
|
echo "== App =="
|
||||||
|
curl -s --max-time 5 http://localhost:3003/health || wget -qO- http://localhost:3003/health || echo "antwortet NICHT"
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
echo "== Neustarts der App (letzte 10 Meldungen) =="
|
||||||
|
docker logs projektuebersicht 2>&1 | grep -E "Wächter|Unerwarteter Fehler|server running" | tail -10
|
||||||
|
echo
|
||||||
|
echo "== Speichermangel im System (earlyoom / Kernel) =="
|
||||||
|
journalctl -u earlyoom --since "-7 days" --no-pager 2>/dev/null | grep -i "sending\|killing" | tail -5
|
||||||
|
journalctl -k --since "-7 days" --no-pager 2>/dev/null | grep -i "out of memory\|oom-kill" | tail -5
|
||||||
|
echo
|
||||||
|
echo "== Stromversorgung =="
|
||||||
|
if command -v vcgencmd >/dev/null; then vcgencmd get_throttled
|
||||||
|
elif [ -r /sys/devices/platform/soc/soc:firmware/get_throttled ]; then echo "throttled=$(cat /sys/devices/platform/soc/soc:firmware/get_throttled) (0 = gut)"
|
||||||
|
else echo "nicht auslesbar"; fi
|
||||||
|
echo
|
||||||
|
echo "== WLAN =="
|
||||||
|
iw dev wlan0 get power_save 2>/dev/null || echo "nicht auslesbar"
|
||||||
|
systemctl is-active wlan-waechter.timer 2>/dev/null | sed 's/^/Wächter: /'
|
||||||
|
journalctl -t wlan-waechter --since "-7 days" --no-pager 2>/dev/null | tail -5
|
||||||
|
echo
|
||||||
|
echo "== Letzte Systemstarts =="
|
||||||
|
last -x reboot 2>/dev/null | head -5
|
||||||
Executable
+72
@@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# update-ausfuehren.sh — führt einen Update-Auftrag aus der App aus
|
||||||
|
# (Admin → Update, POST /admin/update legt sessions/update/auftrag.json an).
|
||||||
|
# Gestartet vom systemd-Dienst projektuebersicht-update (eingerichtet von
|
||||||
|
# Docker/update-einrichten.sh), läuft als root auf dem Rechner, nicht im
|
||||||
|
# Container: Version vom Git-Server holen, auschecken, Container neu bauen
|
||||||
|
# und starten. Fortschritt in sessions/update/status.json, Protokoll in
|
||||||
|
# sessions/update/update.log, die laufende Version in installiert.json.
|
||||||
|
#
|
||||||
|
# Alles steht in main(): bash liest ein Skript sonst zeilenweise beim
|
||||||
|
# Ausführen — und dieses Skript wird mitten im Lauf durch den Checkout ersetzt.
|
||||||
|
set -u
|
||||||
|
|
||||||
|
main(){
|
||||||
|
local REPO DIR AUFTRAG LOG VERSION EIGNER VORHER COMPOSE
|
||||||
|
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
DIR="$REPO/sessions/update"
|
||||||
|
AUFTRAG="$DIR/auftrag.json"
|
||||||
|
LOG="$DIR/update.log"
|
||||||
|
[ -f "$AUFTRAG" ] || return 0
|
||||||
|
|
||||||
|
VERSION=$(sed -n 's/.*"version":"\(v[0-9]\{1,4\}\.[0-9]\{1,4\}\.[0-9]\{1,4\}\)".*/\1/p' "$AUFTRAG")
|
||||||
|
rm -f "$AUFTRAG"
|
||||||
|
: > "$LOG"
|
||||||
|
EIGNER=$(stat -c %U "$REPO")
|
||||||
|
git_(){ runuser -u "$EIGNER" -- git -C "$REPO" "$@"; }
|
||||||
|
status(){ # $1 Schritt, $2 läuft (true/false), $3 Fehlertext (fest, ohne Anführungszeichen)
|
||||||
|
printf '{"version":"%s","schritt":"%s","laeuft":%s,"fehler":"%s","am":"%s"}\n' \
|
||||||
|
"$VERSION" "$1" "$2" "${3:-}" "$(date -Iseconds)" > "$DIR/status.tmp" && mv "$DIR/status.tmp" "$DIR/status.json"
|
||||||
|
}
|
||||||
|
fehler(){ status "$1" false "$2"; echo "FEHLER: $2" >> "$LOG"; }
|
||||||
|
|
||||||
|
if [ -z "$VERSION" ]; then fehler "Auftrag" "Der Auftrag enthält keine gültige Version."; return 1; fi
|
||||||
|
echo "Update auf $VERSION, $(date)" >> "$LOG"
|
||||||
|
|
||||||
|
status "Version holen" true
|
||||||
|
if ! git_ fetch --tags --force origin >> "$LOG" 2>&1; then fehler "Version holen" "Der Git-Server ist nicht erreichbar."; return 1; fi
|
||||||
|
if ! git_ rev-parse -q --verify "refs/tags/$VERSION^{commit}" >/dev/null; then fehler "Version holen" "Die Version gibt es auf dem Git-Server nicht."; return 1; fi
|
||||||
|
# Lokale Änderungen am Code (sessions/ ausgenommen) nie überschreiben.
|
||||||
|
if [ -n "$(git_ status --porcelain --untracked-files=no -- . ':!sessions')" ]; then
|
||||||
|
git_ status --short -- . ':!sessions' >> "$LOG" 2>&1
|
||||||
|
fehler "Prüfen" "Auf dem Rechner gibt es lokale Änderungen am Code. Bitte von Hand klären."; return 1
|
||||||
|
fi
|
||||||
|
VORHER=$(git_ rev-parse HEAD)
|
||||||
|
if ! git_ checkout -q --detach "$VERSION" >> "$LOG" 2>&1; then fehler "Auschecken" "Auschecken fehlgeschlagen."; return 1; fi
|
||||||
|
|
||||||
|
if docker compose version >/dev/null 2>&1; then COMPOSE="docker compose"; else COMPOSE="docker-compose"; fi
|
||||||
|
status "Container bauen" true
|
||||||
|
# Gebaut wird, während die alte Version weiterläuft; erst danach Neustart.
|
||||||
|
if ! $COMPOSE --project-directory "$REPO" -f "$REPO/docker-compose.yml" build >> "$LOG" 2>&1; then
|
||||||
|
git_ checkout -q --detach "$VORHER" >> "$LOG" 2>&1
|
||||||
|
fehler "Container bauen" "Bauen fehlgeschlagen, die bisherige Version läuft weiter."; return 1
|
||||||
|
fi
|
||||||
|
status "Neustart" true
|
||||||
|
if ! $COMPOSE --project-directory "$REPO" -f "$REPO/docker-compose.yml" up -d >> "$LOG" 2>&1; then
|
||||||
|
fehler "Neustart" "Der Container ließ sich nicht starten."; return 1
|
||||||
|
fi
|
||||||
|
local i gesund=0
|
||||||
|
for i in $(seq 1 60); do
|
||||||
|
if wget -qO- http://127.0.0.1:3003/health >/dev/null 2>&1 || curl -fs http://127.0.0.1:3003/health >/dev/null 2>&1; then gesund=1; break; fi
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
if [ "$gesund" != 1 ]; then
|
||||||
|
$COMPOSE --project-directory "$REPO" -f "$REPO/docker-compose.yml" logs --tail 40 >> "$LOG" 2>&1
|
||||||
|
fehler "Neustart" "Die neue Version antwortet nach 5 Minuten nicht. Protokoll prüfen."; return 1
|
||||||
|
fi
|
||||||
|
printf '{"version":"%s","commit":"%s","am":"%s"}\n' "$VERSION" "$(git_ rev-parse --short HEAD)" "$(date -Iseconds)" > "$DIR/installiert.json"
|
||||||
|
status "Fertig" false
|
||||||
|
echo "Fertig, $(date)" >> "$LOG"
|
||||||
|
}
|
||||||
|
main "$@"
|
||||||
|
exit $?
|
||||||
Executable
+57
@@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# update-einrichten.sh — richtet auf dem Rechner (Pi) den Dienst ein, der
|
||||||
|
# Updates aus der App ausführt (Admin → Update). Einmal ausführen, im
|
||||||
|
# geklonten Repo, mit sudo. Mehrfach ausführbar.
|
||||||
|
#
|
||||||
|
# sudo bash Docker/update-einrichten.sh
|
||||||
|
#
|
||||||
|
# Danach: systemd beobachtet sessions/update/auftrag.json; legt die App dort
|
||||||
|
# einen Auftrag ab, startet Docker/update-ausfuehren.sh. Der Container selbst
|
||||||
|
# bekommt keinen Zugriff auf Docker.
|
||||||
|
set -u
|
||||||
|
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then echo "Bitte mit sudo ausführen."; exit 1; fi
|
||||||
|
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
DIR="$REPO/sessions/update"
|
||||||
|
EIGNER=$(stat -c %U "$REPO")
|
||||||
|
|
||||||
|
if ! runuser -u "$EIGNER" -- git -C "$REPO" remote get-url origin >/dev/null 2>&1; then
|
||||||
|
echo "Kein Git-Repo mit 'origin' in $REPO — die App muss aus einem Klon des Git-Servers laufen."; exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$DIR"
|
||||||
|
# Der Container läuft als Benutzer node (uid 1000) und legt hier den Auftrag ab.
|
||||||
|
chown 1000:1000 "$DIR"
|
||||||
|
|
||||||
|
# Laufende Version vermerken: der nächstliegende Tag.
|
||||||
|
VERSION=$(runuser -u "$EIGNER" -- git -C "$REPO" describe --tags --abbrev=0 2>/dev/null || echo "")
|
||||||
|
COMMIT=$(runuser -u "$EIGNER" -- git -C "$REPO" rev-parse --short HEAD)
|
||||||
|
printf '{"version":"%s","commit":"%s","am":"%s"}\n' "$VERSION" "$COMMIT" "$(date -Iseconds)" > "$DIR/installiert.json"
|
||||||
|
printf '{"eingerichtet":"%s"}\n' "$(date -Iseconds)" > "$DIR/dienst.json"
|
||||||
|
|
||||||
|
cat > /etc/systemd/system/projektuebersicht-update.service <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Projektübersicht: Update aus der App ausführen
|
||||||
|
After=docker.service network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/bin/bash $REPO/Docker/update-ausfuehren.sh
|
||||||
|
TimeoutStartSec=3600
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > /etc/systemd/system/projektuebersicht-update.path <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Projektübersicht: auf Update-Auftrag aus der App warten
|
||||||
|
|
||||||
|
[Path]
|
||||||
|
PathExists=$DIR/auftrag.json
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable --now projektuebersicht-update.path
|
||||||
|
echo " ✓ Update-Dienst eingerichtet (laufende Version: ${VERSION:-unbekannt}, $COMMIT)"
|
||||||
|
echo " • Die App braucht UPDATE_REPO_API in docker-compose.yml; danach einmal: docker compose up -d --build"
|
||||||
@@ -1,148 +1,129 @@
|
|||||||
# Projektübersicht — README
|
# Projektübersicht
|
||||||
|
|
||||||
## Übersicht
|
Werkzeug für ein Ingenieurbüro der technischen Gebäudeausrüstung (TGA), um
|
||||||
Dieses Projekt ist eine lokale Single-Page-Webanwendung zur Verwaltung elektrotechnischer Projekte und Sitzungsübersichten. Die Anwendung besteht aus:
|
Projekte über wöchentliche Sitzungen hinweg zu verfolgen: Leistungsphasen nach
|
||||||
|
HOAI, Termine, Personaleinsatz, die wöchentliche Teambesprechung und die
|
||||||
|
Dokumentenfreigabe. Single-Page-App ohne Build-Schritt: Vanilla JS/HTML/CSS
|
||||||
|
im Frontend, Node.js/Express im Backend, Daten in SQLite.
|
||||||
|
|
||||||
- einem Frontend in `Test.html`, `styles.css` und den JS-Modulen unter `public/js/`
|
## Technologie
|
||||||
- einem einfachen Node.js-Server in `server.js`
|
|
||||||
- einem Datenspeicher in CSV-Dateien unter `sessions/`
|
|
||||||
- einem Docker-Setup unter `Docker/`
|
|
||||||
|
|
||||||
## Datei- und Komponentenübersicht
|
- **Frontend**: klassische, unmodulare `<script>`-Dateien unter `public/js/`
|
||||||
|
— alle Funktionen liegen global auf `window`, kein Bundler, kein Framework.
|
||||||
|
**Eine** HTML-Datei, `public/App.html`, bildet die gesamte Oberfläche ab
|
||||||
|
(Seitenleiste + alle Bereiche inklusive des Projektbereichs eines einzelnen
|
||||||
|
Projekts).
|
||||||
|
- **Backend**: Node.js (≥ 22.13, wegen `node:sqlite`) + Express, alles in
|
||||||
|
einer einzigen `server.js`.
|
||||||
|
- **Datenbank**: SQLite über das eingebaute `node:sqlite`-Modul (WAL-Modus,
|
||||||
|
vollständig relational, keine Einzeldatei je Woche), Datei unter
|
||||||
|
`sessions/projektuebersicht.db`.
|
||||||
|
- Kein Linter, kein Formatter. Tests: `npm test` (Zusammenführen, Rechteprüfung). Sonst läuft die Verifikation manuell:
|
||||||
|
Server starten, Bereich im Browser ausprobieren, SQLite-Datei prüfen.
|
||||||
|
|
||||||
### Wichtige Laufzeit-Dateien
|
## Starten
|
||||||
- `server.js`
|
|
||||||
- Node.js/Express-Server
|
|
||||||
- dient als API für das Frontend
|
|
||||||
- stellt statische Dateien (`Test.html`, die Module unter `js/`, `styles.css`, etc.) bereit
|
|
||||||
- implementiert Endpunkte:
|
|
||||||
- `GET /` → liefert `Test.html`
|
|
||||||
- `GET /load-all` → lädt alle CSV-Dateien aus dem aktiven `sessions/<DEPARTMENT>/`-Ordner inkl. `_people.csv`
|
|
||||||
- `POST /save-csv` → speichert eine CSV-Datei (Dateiname wird validiert)
|
|
||||||
- `POST /delete-csv` → löscht eine CSV-Datei
|
|
||||||
- `GET /health` → Gesundheitscheck
|
|
||||||
- legt das `sessions`-Verzeichnis beim Start an, falls es nicht existiert
|
|
||||||
- beendet sich mit aussagekräftiger Fehlermeldung, wenn der Port belegt ist
|
|
||||||
|
|
||||||
- `package.json`
|
### Lokal (Node.js ≥ 22.13)
|
||||||
- definiert den Node.js-Startbefehl `npm start`
|
|
||||||
- enthält Produktionsabhängigkeiten `express` und `cors`
|
|
||||||
|
|
||||||
- `package-lock.json`
|
|
||||||
- wird für reproduzierbare Docker-Builds verwendet (`npm ci`)
|
|
||||||
|
|
||||||
- `public/js/` (Module)
|
|
||||||
- Die Client-Logik ist in kleine klassische Script-Dateien aufgeteilt (alle Funktionen global auf `window`):
|
|
||||||
`globals.js`, `utils.js`, `ui.js`, `date-picker.js`, `csv.js`, `server.js`, `store.js`, `weeks.js`,
|
|
||||||
`board.js`, `people.js`, `projects.js`, `load.js`, `gantt.js`, `showmode.js`, `tabs-auth.js`, `pdf.js`,
|
|
||||||
`init.js` sowie die Plan-Module unter `js/plan/` (`plan-state`, `plan-build-gantt`, `plan-gantt`,
|
|
||||||
`plan-drag`, `plan-modals`, `plan-schedule`).
|
|
||||||
- rendert das Projektboard, die Projekt- und Personenverwaltung sowie die Auslastungsansicht
|
|
||||||
- lädt Daten über den Server und speichert Änderungen zurück als CSV
|
|
||||||
- verwendet `currentDepartment` zur Trennung von Abteilungen (`ELT`, `HLS`, `ARCH`)
|
|
||||||
- speichert:
|
|
||||||
- `sessions/<DEPT>/<WEEK>.csv`
|
|
||||||
- `sessions/<DEPT>/_people.csv` (globale Personenliste pro Abteilung)
|
|
||||||
- sortiert Sitzungen nach Kalenderwoche, nicht alphabetisch
|
|
||||||
- sperrt historische Sitzungen automatisch (bis auf die chronologisch letzte)
|
|
||||||
- Personen sind global pro Abteilung verwaltet; historische Sitzungen behalten ihre lokalen Personenkopien
|
|
||||||
|
|
||||||
- `Test.html`
|
|
||||||
- die Benutzeroberfläche der Anwendung
|
|
||||||
- lädt `styles.css` und die JS-Module aus `js/` in fester Reihenfolge (siehe `AGENTS.md`)
|
|
||||||
- enthält die Abteilungsauswahl, Sitzungsnavigation, Tab-Navigation und das Passwort-Modal
|
|
||||||
|
|
||||||
- `project-plan.html`
|
|
||||||
- die Planungsansicht (GANTT/TODOs/Meilensteine) pro Projekt
|
|
||||||
- öffnet sich in einem neuen Tab über `openProjectPlan()` (`js/gantt.js`)
|
|
||||||
- lädt nur die gemeinsamen Helfermodule plus `js/plan/*`
|
|
||||||
|
|
||||||
- `styles.css`
|
|
||||||
- Layout und Styling der Oberfläche
|
|
||||||
|
|
||||||
### Docker-bezogene Dateien
|
|
||||||
- `Docker/Dockerfile`
|
|
||||||
- Basis-Image: `node:20-alpine`
|
|
||||||
- kopiert `package.json` und `package-lock.json`, installiert Produktionsabhängigkeiten mit `npm ci`
|
|
||||||
- kopiert den Rest des Projekts in `/app`
|
|
||||||
- legt sicherheitshalber `/app/sessions` an
|
|
||||||
- läuft als nicht-root-User `node`
|
|
||||||
- enthält einen `HEALTHCHECK` auf `/health`
|
|
||||||
- startet `node server.js`
|
|
||||||
|
|
||||||
- `docker-compose.yml`
|
|
||||||
- definiert den Service `projektuebersicht`
|
|
||||||
- baut das Image aus dem Projektstamm mit `Docker/Dockerfile`
|
|
||||||
- mappt Port `3003:3003`
|
|
||||||
- bindet das Host-Verzeichnis `./sessions` in den Container nach `/app/sessions`
|
|
||||||
- setzt `restart: unless-stopped`
|
|
||||||
- definiert einen `healthcheck`
|
|
||||||
|
|
||||||
- `.dockerignore`
|
|
||||||
- schließt unnötige Dateien aus dem Docker-Build aus: `Docker/`, `node_modules/`, `npm-debug.log`, `.git`, `.gitignore`
|
|
||||||
|
|
||||||
### Sonstige Dateien
|
|
||||||
- `dein_bild.ico`
|
|
||||||
- optionales Icon, wird vom Server statisch ausgeliefert, wenn angefragt
|
|
||||||
|
|
||||||
- `AGENTS.md`
|
|
||||||
- Projektnotizen / Dokumentation, aber nicht Teil des Anwendungslaufs
|
|
||||||
|
|
||||||
## Speicherorte und Datenfluss
|
|
||||||
|
|
||||||
### Speicherung
|
|
||||||
- Alle persistierten Daten werden als CSV-Dateien in `sessions/` gespeichert.
|
|
||||||
- Innerhalb von `sessions/` existieren Abteilungsordner:
|
|
||||||
- `sessions/ELT/`
|
|
||||||
- `sessions/HLS/`
|
|
||||||
- `sessions/ARCH/`
|
|
||||||
- Jede Abteilung enthält mindestens:
|
|
||||||
- `_people.csv` → globale Personenliste mit Rollen und vollständigem Namen
|
|
||||||
- `KW_XX___YYYY.csv` oder vergleichbare Sitzungsdateien → Sitzungsdaten mit Projekten, Zuordnungen und Anwesenheit
|
|
||||||
|
|
||||||
### Wo wird der Server erstellt?
|
|
||||||
- Der Server wird in `server.js` erstellt.
|
|
||||||
- `server.js` startet einen Express-Server auf Port `3003` oder dem Wert von `process.env.PORT`.
|
|
||||||
- Das Frontend ruft diesen Server über `window.location.origin` an.
|
|
||||||
|
|
||||||
### Wo werden die Dateien gespeichert?
|
|
||||||
- Das Frontend schreibt über den Server in das Dateisystem.
|
|
||||||
- In `js/store.js` ruft die Anwendung beim Speichern `/save-csv` auf.
|
|
||||||
- Der Server legt CSV-Dateien unter `sessions/<DEPARTMENT>/` ab.
|
|
||||||
- Beispiel:
|
|
||||||
- `sessions/ELT/KW_32___2026.csv`
|
|
||||||
- `sessions/ELT/_people.csv`
|
|
||||||
|
|
||||||
## Betrieb
|
|
||||||
|
|
||||||
### Lokal mit Node.js
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
Danach ist die Anwendung erreichbar unter `http://localhost:3003`.
|
Erreichbar unter `http://localhost:3003`. Anderer Port:
|
||||||
|
|
||||||
Falls der Port belegt ist:
|
|
||||||
```bash
|
```bash
|
||||||
PORT=3004 npm start
|
PORT=3004 npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
### Mit Docker
|
### Mit Docker
|
||||||
Im Projektstamm ausführen:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose -f Docker/docker-compose.yml up --build
|
docker compose up --build
|
||||||
```
|
```
|
||||||
|
`docker-compose.yml` liegt im Projektstamm, baut aus `Docker/Dockerfile`
|
||||||
|
(`node:22-alpine`), mappt Port `3003` und bindet `./sessions` in den
|
||||||
|
Container nach `/app/sessions` — die Datenbank und alle Uploads bleiben damit
|
||||||
|
auf dem Host erhalten. `TZ=Europe/Berlin` ist gesetzt, weil mehrere
|
||||||
|
Datumsvergleiche im Server von der lokalen Zeitzone abhängen.
|
||||||
|
|
||||||
Danach ist die Anwendung erreichbar unter:
|
### Erste Anmeldung
|
||||||
|
Die gesamte Anwendung verlangt eine Anmeldung. Ist die `users`-Tabelle beim
|
||||||
|
ersten Start leer, legt der Server automatisch ein Admin-Konto
|
||||||
|
(`admin`) mit einem zufälligen Passwort an, gibt es einmalig auf der Konsole
|
||||||
|
aus und schreibt es zusätzlich nach `sessions/INITIAL_ADMIN_PASSWORD.txt`
|
||||||
|
(liegt außerhalb von `public/`, wird also nie ausgeliefert). Das Konto muss
|
||||||
|
sein Passwort beim ersten Login ändern.
|
||||||
|
|
||||||
```
|
## Bereiche der Anwendung
|
||||||
http://localhost:3003
|
|
||||||
```
|
|
||||||
|
|
||||||
## Wichtige Hinweise
|
Seitenleiste, in dieser Reihenfolge:
|
||||||
- Der Server benötigt Lese-/Schreibzugriff auf `sessions/`.
|
|
||||||
- Die Docker-Compose-Konfiguration mountet `./sessions` in den Container. Achte darauf, dass der relative Pfad im Kontext korrekt ist, wenn du `docker compose` aus einem anderen Verzeichnis startest.
|
- **Meine Aufgaben** — Dashboard und Startansicht nach dem Login: offene
|
||||||
- `js/store.js` und `js/server.js` arbeiten mit Abteilungsparametern und speichern Daten abteilungsweise in separaten Unterordnern.
|
persönliche Aufgaben, Termine der eigenen Projekte, ausstehende
|
||||||
- Die Passwortfunktion ist nur ein einfacher Client-Mechanismus im Frontend und kein echter Sicherheitsmechanismus.
|
Datei-Freigaben, Abwesenheiten von Kollegen.
|
||||||
- Globale Personen werden in `_people.csv` verwaltet. Wird eine Person global gelöscht, bleibt sie in bereits existierenden Sitzungen sichtbar.
|
- **Projekte** — die eine Projektliste (Board), gruppiert, per Drag-and-Drop
|
||||||
|
sortierbar.
|
||||||
|
- **Wochenbericht** — die Tagesordnung der wöchentlichen Teambesprechung, je
|
||||||
|
Gewerk (ELT/HLS/ARCH) gefiltert: jedes Projekt dieses Gewerks wird
|
||||||
|
angesehen, mit Vergleich zur Vorwoche, Anwesenheitserfassung und PDF-Export.
|
||||||
|
- **Auslastung** — Personalauslastung in tatsächlichen Wochenstunden,
|
||||||
|
aufgeteilt nach Projekten (Lastband).
|
||||||
|
- **Projektbeteiligte** — schreibgeschützte Übersicht der aktiven
|
||||||
|
Benutzerkonten; Teammitglieder pflegt ausschließlich ein Administrator
|
||||||
|
(Admin → Benutzerverwaltung).
|
||||||
|
- **Planung** — ein von einem konkreten Projekt unabhängiger
|
||||||
|
Terminplan-Sandkasten, z. B. um einen Zeitplan vorab durchzurechnen.
|
||||||
|
- **Einstellungen** — das eigene Konto: Stammdaten, E-Mail, Passwort, eigene
|
||||||
|
KI-Zugangsdaten für die Protokoll-Erkennung (siehe unten).
|
||||||
|
- **Admin** *(nur Administratoren)* — Benutzerverwaltung, Honorar-Vorgaben,
|
||||||
|
Auslastungs-Schwellen, Sitzungsverwaltung, Feiertage, Aufgaben-Vorlagen,
|
||||||
|
Betrieb & Sicherheit, Datenbank-Backup/-Import.
|
||||||
|
|
||||||
|
### Projektbereich
|
||||||
|
|
||||||
|
Aus der Projektliste heraus öffnet sich zu einem Projekt ein eigener Bereich
|
||||||
|
mit sieben Abschnitten: Dashboard, Terminplan (GANTT mit Aufgaben und
|
||||||
|
Meilensteinen), Aufgaben, Leistung (erbrachter Leistungswert je Phase),
|
||||||
|
Dateien, Protokolle (Besprechungsprotokolle) und Einstellungen
|
||||||
|
(Projektstammdaten, Honorar, beauftragte Leistungsphasen).
|
||||||
|
|
||||||
|
## Datenhaltung
|
||||||
|
|
||||||
|
- Vollständig relationales SQLite-Schema statt eines JSON-Dokuments je Woche;
|
||||||
|
Projekt- und Personen-IDs bleiben über Neustarts stabil.
|
||||||
|
- Eine Sitzung ("KW XX / YYYY") entspricht einer Kalenderwoche. Der Wechsel
|
||||||
|
in eine neue Sitzung läuft automatisch im Hintergrund (stündliche Prüfung)
|
||||||
|
— es gibt keine "Neue Sitzung"-Schaltfläche mehr.
|
||||||
|
- Der Server legt bei jedem Start automatisch einen Zeitstempel-Snapshot
|
||||||
|
unter `sessions/backups/` an und behält nur die neuesten (Anzahl einstellbar
|
||||||
|
unter Admin → Betrieb & Sicherheit). Ein Admin kann zusätzlich jederzeit
|
||||||
|
ein Backup herunterladen oder eine Sicherung zurückspielen.
|
||||||
|
- Hochgeladene Dateien (Freigabe-Workflow, importierte Protokolle) liegen
|
||||||
|
unter `sessions/uploads/<uuid>.<endung>`, referenziert über die Datenbank.
|
||||||
|
|
||||||
|
## Authentifizierung & Rollen
|
||||||
|
|
||||||
|
Echte serverseitige Sitzungen (Cookie, `scrypt`-gehashte Passwörter) — kein
|
||||||
|
clientseitiger Passwortschutz. Zwei Rollen: `admin` und `member`; nur
|
||||||
|
wirklich destruktive bzw. administrative Aktionen (Benutzerverwaltung,
|
||||||
|
Sitzung löschen/entsperren, DB-Import/-Backup) sind serverseitig auf Admins
|
||||||
|
beschränkt. Zu viele Fehlversuche sperren ein Konto vorübergehend.
|
||||||
|
|
||||||
|
## Besonderheiten
|
||||||
|
|
||||||
|
- **Keine Abteilungstrennung mehr**: Alle Sitzungen, Projekte und Personen
|
||||||
|
liegen in einer gemeinsamen Liste. `ELT`/`HLS`/`ARCH` bezeichnen heute nur
|
||||||
|
noch die Fachrichtung eines Benutzerkontos bzw. die Gewerke eines Projekts.
|
||||||
|
- **Protokoll-Import** kann ein hochgeladenes PDF/Word-Dokument
|
||||||
|
KI-gestützt (eigener OpenAI- oder Claude-Schlüssel je Konto, auch ein
|
||||||
|
lokal laufendes Modell möglich) in Besprechungspunkte umwandeln und fällt
|
||||||
|
ohne Schlüssel oder bei einem Fehler automatisch auf eine
|
||||||
|
Text-Heuristik zurück.
|
||||||
|
- Der PDF-Export lädt `pdfmake` von `cdnjs.cloudflare.com` — benötigt
|
||||||
|
Internetzugang.
|
||||||
|
- Feiertage werden serverseitig berechnet (keine externe Abhängigkeit), die
|
||||||
|
KI-gestützte Protokoll-Erkennung braucht dagegen Internetzugang bzw. ein
|
||||||
|
erreichbares lokales Modell.
|
||||||
|
|
||||||
|
## Weiterführende Dokumentation
|
||||||
|
|
||||||
|
`CLAUDE.md` im Projektstamm enthält die ausführliche technische
|
||||||
|
Dokumentation: Server-API, vollständiges Datenmodell, Architekturentscheidungen
|
||||||
|
und ihre Begründung. Ausgangspunkt für tiefere Änderungen am Code.
|
||||||
+35
-5
@@ -8,10 +8,40 @@ services:
|
|||||||
- "3003:3003"
|
- "3003:3003"
|
||||||
volumes:
|
volumes:
|
||||||
- ./sessions:/app/sessions
|
- ./sessions:/app/sessions
|
||||||
restart: unless-stopped
|
environment:
|
||||||
|
# Ohne TZ läuft der Container auf UTC. Der Server vergleicht Termine
|
||||||
|
# mit dem heutigen Datum (heuteIsoLokal() in server.js) — in deutscher
|
||||||
|
# Sommerzeit gälte damit zwischen 00:00 und 02:00 Ortszeit noch der
|
||||||
|
# Vortag, und Termine von heute erschienen als überfällig.
|
||||||
|
TZ: Europe/Berlin
|
||||||
|
# Speicher-Wächter: darüber startet der Server sich selbst sauber neu,
|
||||||
|
# bevor die Grenze von 320 MB (mem_limit) erreicht ist.
|
||||||
|
SPEICHER_MAX_MB: "220"
|
||||||
|
# PDF-Kindprozess (Protokoll-Import, Plan Checker): bricht darüber ab.
|
||||||
|
PDF_KIND_MAX_MB: "220"
|
||||||
|
PDF_KIND_HEAP_MB: "160"
|
||||||
|
# Hänger-Wächter: reagiert der Server so lange nicht, Neustart.
|
||||||
|
HAENGER_MAX_S: "90"
|
||||||
|
# Admin → Update: Versionen (Tags) vom Git-Server. Ausgeführt wird ein
|
||||||
|
# Update vom Dienst auf dem Rechner (Docker/update-einrichten.sh).
|
||||||
|
UPDATE_REPO_API: "http://192.168.3.187:3000/api/v1/repos/erik/projektuebersicht"
|
||||||
|
restart: always
|
||||||
|
# Betrieb auf dem Pi Zero 2 (512 MB), siehe CLAUDE.md "Betrieb auf dem Pi":
|
||||||
|
# tini als PID 1 — Signale kommen sauber bei Node an (DB wird geschlossen).
|
||||||
|
init: true
|
||||||
|
# Feste Speichergrenze: Läuft die App über, trifft es nur diesen
|
||||||
|
# Container (restart: always holt ihn zurück), nicht SSH oder Docker.
|
||||||
|
mem_limit: 320m
|
||||||
|
memswap_limit: 512m
|
||||||
|
# Die Protokolle wuchsen sonst unbegrenzt auf der SD-Karte.
|
||||||
|
logging:
|
||||||
|
driver: json-file
|
||||||
|
options:
|
||||||
|
max-size: "5m"
|
||||||
|
max-file: "3"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3003/health', r => r.statusCode === 200 ? process.exit(0) : process.exit(1)).on('error', () => process.exit(1))"]
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:3003/health >/dev/null || exit 1"]
|
||||||
interval: 30s
|
interval: 60s
|
||||||
timeout: 5s
|
timeout: 10s
|
||||||
start_period: 10s
|
start_period: 60s
|
||||||
retries: 3
|
retries: 3
|
||||||
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
|||||||
../mime/cli.js
|
|
||||||
-847
@@ -1,847 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "projektuebersicht",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"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==",
|
|
||||||
"license": "MIT",
|
|
||||||
"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==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/body-parser": {
|
|
||||||
"version": "1.20.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz",
|
|
||||||
"integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"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.1",
|
|
||||||
"iconv-lite": "~0.4.24",
|
|
||||||
"on-finished": "~2.4.1",
|
|
||||||
"qs": "~6.15.1",
|
|
||||||
"raw-body": "~2.5.3",
|
|
||||||
"type-is": "~1.6.18",
|
|
||||||
"unpipe": "~1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8",
|
|
||||||
"npm": "1.2.8000 || >= 1.4.16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/bytes": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/call-bind-apply-helpers": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"function-bind": "^1.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/call-bound": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
|
||||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
|
||||||
"get-intrinsic": "^1.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/content-disposition": {
|
|
||||||
"version": "0.5.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
|
||||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"safe-buffer": "5.2.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/content-type": {
|
|
||||||
"version": "1.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
|
||||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cookie": {
|
|
||||||
"version": "0.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
|
||||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cookie-signature": {
|
|
||||||
"version": "1.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
|
|
||||||
"integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/cors": {
|
|
||||||
"version": "2.8.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
|
|
||||||
"integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"object-assign": "^4",
|
|
||||||
"vary": "^1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/express"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/debug": {
|
|
||||||
"version": "2.6.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ms": "2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/depd": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"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==",
|
|
||||||
"license": "MIT",
|
|
||||||
"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",
|
|
||||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"call-bind-apply-helpers": "^1.0.1",
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"gopd": "^1.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ee-first": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/encodeurl": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/es-define-property": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/es-errors": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/es-object-atoms": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"es-errors": "^1.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/escape-html": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/etag": {
|
|
||||||
"version": "1.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
|
||||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/express": {
|
|
||||||
"version": "4.22.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",
|
|
||||||
"integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"accepts": "~1.3.8",
|
|
||||||
"array-flatten": "1.1.1",
|
|
||||||
"body-parser": "~1.20.5",
|
|
||||||
"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.15.1",
|
|
||||||
"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": ">= 0.10.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/express"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/finalhandler": {
|
|
||||||
"version": "1.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
|
|
||||||
"integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"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.2",
|
|
||||||
"unpipe": "~1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/forwarded": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fresh": {
|
|
||||||
"version": "0.5.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
|
||||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/function-bind": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/get-intrinsic": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
|
||||||
"es-define-property": "^1.0.1",
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"es-object-atoms": "^1.1.1",
|
|
||||||
"function-bind": "^1.1.2",
|
|
||||||
"get-proto": "^1.0.1",
|
|
||||||
"gopd": "^1.2.0",
|
|
||||||
"has-symbols": "^1.1.0",
|
|
||||||
"hasown": "^2.0.2",
|
|
||||||
"math-intrinsics": "^1.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/get-proto": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"dunder-proto": "^1.0.1",
|
|
||||||
"es-object-atoms": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/gopd": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/has-symbols": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/hasown": {
|
|
||||||
"version": "2.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
|
|
||||||
"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"function-bind": "^1.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/http-errors": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"depd": "~2.0.0",
|
|
||||||
"inherits": "~2.0.4",
|
|
||||||
"setprototypeof": "~1.2.0",
|
|
||||||
"statuses": "~2.0.2",
|
|
||||||
"toidentifier": "~1.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/express"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/iconv-lite": {
|
|
||||||
"version": "0.4.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
|
||||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"safer-buffer": ">= 2.1.2 < 3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/inherits": {
|
|
||||||
"version": "2.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/ipaddr.js": {
|
|
||||||
"version": "1.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
|
||||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/math-intrinsics": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/media-typer": {
|
|
||||||
"version": "0.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
|
||||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/merge-descriptors": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"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==",
|
|
||||||
"license": "MIT",
|
|
||||||
"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==",
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"mime": "cli.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mime-db": {
|
|
||||||
"version": "1.52.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/mime-types": {
|
|
||||||
"version": "2.1.35",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"mime-db": "1.52.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ms": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/negotiator": {
|
|
||||||
"version": "0.6.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
|
||||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/object-assign": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/object-inspect": {
|
|
||||||
"version": "1.13.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
|
||||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/on-finished": {
|
|
||||||
"version": "2.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
|
||||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ee-first": "1.1.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/parseurl": {
|
|
||||||
"version": "1.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
|
||||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/path-to-regexp": {
|
|
||||||
"version": "0.1.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
|
|
||||||
"integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/proxy-addr": {
|
|
||||||
"version": "2.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
|
||||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"forwarded": "0.2.0",
|
|
||||||
"ipaddr.js": "1.9.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/qs": {
|
|
||||||
"version": "6.15.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
|
|
||||||
"integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"es-define-property": "^1.0.1",
|
|
||||||
"side-channel": "^1.1.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/range-parser": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
|
||||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/raw-body": {
|
|
||||||
"version": "2.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
|
|
||||||
"integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"bytes": "~3.1.2",
|
|
||||||
"http-errors": "~2.0.1",
|
|
||||||
"iconv-lite": "~0.4.24",
|
|
||||||
"unpipe": "~1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/safe-buffer": {
|
|
||||||
"version": "5.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
|
||||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/feross"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "patreon",
|
|
||||||
"url": "https://www.patreon.com/feross"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "consulting",
|
|
||||||
"url": "https://feross.org/support"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/safer-buffer": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/send": {
|
|
||||||
"version": "0.19.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
|
|
||||||
"integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "2.0.0",
|
|
||||||
"destroy": "1.2.0",
|
|
||||||
"encodeurl": "~2.0.0",
|
|
||||||
"escape-html": "~1.0.3",
|
|
||||||
"etag": "~1.8.1",
|
|
||||||
"fresh": "~0.5.2",
|
|
||||||
"http-errors": "~2.0.1",
|
|
||||||
"mime": "1.6.0",
|
|
||||||
"ms": "2.1.3",
|
|
||||||
"on-finished": "~2.4.1",
|
|
||||||
"range-parser": "~1.2.1",
|
|
||||||
"statuses": "~2.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"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==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/serve-static": {
|
|
||||||
"version": "1.16.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
|
|
||||||
"integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"encodeurl": "~2.0.0",
|
|
||||||
"escape-html": "~1.0.3",
|
|
||||||
"parseurl": "~1.3.3",
|
|
||||||
"send": "~0.19.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/setprototypeof": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/side-channel": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"object-inspect": "^1.13.4",
|
|
||||||
"side-channel-list": "^1.0.1",
|
|
||||||
"side-channel-map": "^1.0.1",
|
|
||||||
"side-channel-weakmap": "^1.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/side-channel-list": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"object-inspect": "^1.13.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/side-channel-map": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"call-bound": "^1.0.2",
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"get-intrinsic": "^1.2.5",
|
|
||||||
"object-inspect": "^1.13.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/side-channel-weakmap": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"call-bound": "^1.0.2",
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"get-intrinsic": "^1.2.5",
|
|
||||||
"object-inspect": "^1.13.3",
|
|
||||||
"side-channel-map": "^1.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/statuses": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/toidentifier": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/type-is": {
|
|
||||||
"version": "1.6.18",
|
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
|
||||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"media-typer": "0.3.0",
|
|
||||||
"mime-types": "~2.1.24"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/unpipe": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"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==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vary": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-243
@@ -1,243 +0,0 @@
|
|||||||
1.3.8 / 2022-02-02
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.34
|
|
||||||
- deps: mime-db@~1.51.0
|
|
||||||
* deps: negotiator@0.6.3
|
|
||||||
|
|
||||||
1.3.7 / 2019-04-29
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: negotiator@0.6.2
|
|
||||||
- Fix sorting charset, encoding, and language with extra parameters
|
|
||||||
|
|
||||||
1.3.6 / 2019-04-28
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.24
|
|
||||||
- deps: mime-db@~1.40.0
|
|
||||||
|
|
||||||
1.3.5 / 2018-02-28
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.18
|
|
||||||
- deps: mime-db@~1.33.0
|
|
||||||
|
|
||||||
1.3.4 / 2017-08-22
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.16
|
|
||||||
- deps: mime-db@~1.29.0
|
|
||||||
|
|
||||||
1.3.3 / 2016-05-02
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.11
|
|
||||||
- deps: mime-db@~1.23.0
|
|
||||||
* deps: negotiator@0.6.1
|
|
||||||
- perf: improve `Accept` parsing speed
|
|
||||||
- perf: improve `Accept-Charset` parsing speed
|
|
||||||
- perf: improve `Accept-Encoding` parsing speed
|
|
||||||
- perf: improve `Accept-Language` parsing speed
|
|
||||||
|
|
||||||
1.3.2 / 2016-03-08
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.10
|
|
||||||
- Fix extension of `application/dash+xml`
|
|
||||||
- Update primary extension for `audio/mp4`
|
|
||||||
- deps: mime-db@~1.22.0
|
|
||||||
|
|
||||||
1.3.1 / 2016-01-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.9
|
|
||||||
- deps: mime-db@~1.21.0
|
|
||||||
|
|
||||||
1.3.0 / 2015-09-29
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.7
|
|
||||||
- deps: mime-db@~1.19.0
|
|
||||||
* deps: negotiator@0.6.0
|
|
||||||
- Fix including type extensions in parameters in `Accept` parsing
|
|
||||||
- Fix parsing `Accept` parameters with quoted equals
|
|
||||||
- Fix parsing `Accept` parameters with quoted semicolons
|
|
||||||
- Lazy-load modules from main entry point
|
|
||||||
- perf: delay type concatenation until needed
|
|
||||||
- perf: enable strict mode
|
|
||||||
- perf: hoist regular expressions
|
|
||||||
- perf: remove closures getting spec properties
|
|
||||||
- perf: remove a closure from media type parsing
|
|
||||||
- perf: remove property delete from media type parsing
|
|
||||||
|
|
||||||
1.2.13 / 2015-09-06
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.6
|
|
||||||
- deps: mime-db@~1.18.0
|
|
||||||
|
|
||||||
1.2.12 / 2015-07-30
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.4
|
|
||||||
- deps: mime-db@~1.16.0
|
|
||||||
|
|
||||||
1.2.11 / 2015-07-16
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.3
|
|
||||||
- deps: mime-db@~1.15.0
|
|
||||||
|
|
||||||
1.2.10 / 2015-07-01
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.2
|
|
||||||
- deps: mime-db@~1.14.0
|
|
||||||
|
|
||||||
1.2.9 / 2015-06-08
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.1
|
|
||||||
- perf: fix deopt during mapping
|
|
||||||
|
|
||||||
1.2.8 / 2015-06-07
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.1.0
|
|
||||||
- deps: mime-db@~1.13.0
|
|
||||||
* perf: avoid argument reassignment & argument slice
|
|
||||||
* perf: avoid negotiator recursive construction
|
|
||||||
* perf: enable strict mode
|
|
||||||
* perf: remove unnecessary bitwise operator
|
|
||||||
|
|
||||||
1.2.7 / 2015-05-10
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: negotiator@0.5.3
|
|
||||||
- Fix media type parameter matching to be case-insensitive
|
|
||||||
|
|
||||||
1.2.6 / 2015-05-07
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.0.11
|
|
||||||
- deps: mime-db@~1.9.1
|
|
||||||
* deps: negotiator@0.5.2
|
|
||||||
- Fix comparing media types with quoted values
|
|
||||||
- Fix splitting media types with quoted commas
|
|
||||||
|
|
||||||
1.2.5 / 2015-03-13
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.0.10
|
|
||||||
- deps: mime-db@~1.8.0
|
|
||||||
|
|
||||||
1.2.4 / 2015-02-14
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Support Node.js 0.6
|
|
||||||
* deps: mime-types@~2.0.9
|
|
||||||
- deps: mime-db@~1.7.0
|
|
||||||
* deps: negotiator@0.5.1
|
|
||||||
- Fix preference sorting to be stable for long acceptable lists
|
|
||||||
|
|
||||||
1.2.3 / 2015-01-31
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.0.8
|
|
||||||
- deps: mime-db@~1.6.0
|
|
||||||
|
|
||||||
1.2.2 / 2014-12-30
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.0.7
|
|
||||||
- deps: mime-db@~1.5.0
|
|
||||||
|
|
||||||
1.2.1 / 2014-12-30
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.0.5
|
|
||||||
- deps: mime-db@~1.3.1
|
|
||||||
|
|
||||||
1.2.0 / 2014-12-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: negotiator@0.5.0
|
|
||||||
- Fix list return order when large accepted list
|
|
||||||
- Fix missing identity encoding when q=0 exists
|
|
||||||
- Remove dynamic building of Negotiator class
|
|
||||||
|
|
||||||
1.1.4 / 2014-12-10
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.0.4
|
|
||||||
- deps: mime-db@~1.3.0
|
|
||||||
|
|
||||||
1.1.3 / 2014-11-09
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.0.3
|
|
||||||
- deps: mime-db@~1.2.0
|
|
||||||
|
|
||||||
1.1.2 / 2014-10-14
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: negotiator@0.4.9
|
|
||||||
- Fix error when media type has invalid parameter
|
|
||||||
|
|
||||||
1.1.1 / 2014-09-28
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: mime-types@~2.0.2
|
|
||||||
- deps: mime-db@~1.1.0
|
|
||||||
* deps: negotiator@0.4.8
|
|
||||||
- Fix all negotiations to be case-insensitive
|
|
||||||
- Stable sort preferences of same quality according to client order
|
|
||||||
|
|
||||||
1.1.0 / 2014-09-02
|
|
||||||
==================
|
|
||||||
|
|
||||||
* update `mime-types`
|
|
||||||
|
|
||||||
1.0.7 / 2014-07-04
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix wrong type returned from `type` when match after unknown extension
|
|
||||||
|
|
||||||
1.0.6 / 2014-06-24
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: negotiator@0.4.7
|
|
||||||
|
|
||||||
1.0.5 / 2014-06-20
|
|
||||||
==================
|
|
||||||
|
|
||||||
* fix crash when unknown extension given
|
|
||||||
|
|
||||||
1.0.4 / 2014-06-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* use `mime-types`
|
|
||||||
|
|
||||||
1.0.3 / 2014-06-11
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: negotiator@0.4.6
|
|
||||||
- Order by specificity when quality is the same
|
|
||||||
|
|
||||||
1.0.2 / 2014-05-29
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix interpretation when header not in request
|
|
||||||
* deps: pin negotiator@0.4.5
|
|
||||||
|
|
||||||
1.0.1 / 2014-01-18
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Identity encoding isn't always acceptable
|
|
||||||
* deps: negotiator@~0.4.0
|
|
||||||
|
|
||||||
1.0.0 / 2013-12-27
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Genesis
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
(The MIT License)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Jonathan Ong <me@jongleberry.com>
|
|
||||||
Copyright (c) 2015 Douglas Christopher Wilson <doug@somethingdoug.com>
|
|
||||||
|
|
||||||
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.
|
|
||||||
-140
@@ -1,140 +0,0 @@
|
|||||||
# accepts
|
|
||||||
|
|
||||||
[![NPM Version][npm-version-image]][npm-url]
|
|
||||||
[![NPM Downloads][npm-downloads-image]][npm-url]
|
|
||||||
[![Node.js Version][node-version-image]][node-version-url]
|
|
||||||
[![Build Status][github-actions-ci-image]][github-actions-ci-url]
|
|
||||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
|
||||||
|
|
||||||
Higher level content negotiation based on [negotiator](https://www.npmjs.com/package/negotiator).
|
|
||||||
Extracted from [koa](https://www.npmjs.com/package/koa) for general use.
|
|
||||||
|
|
||||||
In addition to negotiator, it allows:
|
|
||||||
|
|
||||||
- Allows types as an array or arguments list, ie `(['text/html', 'application/json'])`
|
|
||||||
as well as `('text/html', 'application/json')`.
|
|
||||||
- Allows type shorthands such as `json`.
|
|
||||||
- Returns `false` when no types match
|
|
||||||
- Treats non-existent headers as `*`
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
This is a [Node.js](https://nodejs.org/en/) module available through the
|
|
||||||
[npm registry](https://www.npmjs.com/). Installation is done using the
|
|
||||||
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ npm install accepts
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
```js
|
|
||||||
var accepts = require('accepts')
|
|
||||||
```
|
|
||||||
|
|
||||||
### accepts(req)
|
|
||||||
|
|
||||||
Create a new `Accepts` object for the given `req`.
|
|
||||||
|
|
||||||
#### .charset(charsets)
|
|
||||||
|
|
||||||
Return the first accepted charset. If nothing in `charsets` is accepted,
|
|
||||||
then `false` is returned.
|
|
||||||
|
|
||||||
#### .charsets()
|
|
||||||
|
|
||||||
Return the charsets that the request accepts, in the order of the client's
|
|
||||||
preference (most preferred first).
|
|
||||||
|
|
||||||
#### .encoding(encodings)
|
|
||||||
|
|
||||||
Return the first accepted encoding. If nothing in `encodings` is accepted,
|
|
||||||
then `false` is returned.
|
|
||||||
|
|
||||||
#### .encodings()
|
|
||||||
|
|
||||||
Return the encodings that the request accepts, in the order of the client's
|
|
||||||
preference (most preferred first).
|
|
||||||
|
|
||||||
#### .language(languages)
|
|
||||||
|
|
||||||
Return the first accepted language. If nothing in `languages` is accepted,
|
|
||||||
then `false` is returned.
|
|
||||||
|
|
||||||
#### .languages()
|
|
||||||
|
|
||||||
Return the languages that the request accepts, in the order of the client's
|
|
||||||
preference (most preferred first).
|
|
||||||
|
|
||||||
#### .type(types)
|
|
||||||
|
|
||||||
Return the first accepted type (and it is returned as the same text as what
|
|
||||||
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 types is passed to `require('mime-types').lookup`.
|
|
||||||
|
|
||||||
#### .types()
|
|
||||||
|
|
||||||
Return the types that the request accepts, in the order of the client's
|
|
||||||
preference (most preferred first).
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Simple type negotiation
|
|
||||||
|
|
||||||
This simple example shows how to use `accepts` to return a different typed
|
|
||||||
respond body based on what the client wants to accept. The server lists it's
|
|
||||||
preferences in order and will get back the best match between the client and
|
|
||||||
server.
|
|
||||||
|
|
||||||
```js
|
|
||||||
var accepts = require('accepts')
|
|
||||||
var http = require('http')
|
|
||||||
|
|
||||||
function app (req, res) {
|
|
||||||
var accept = accepts(req)
|
|
||||||
|
|
||||||
// the order of this list is significant; should be server preferred order
|
|
||||||
switch (accept.type(['json', 'html'])) {
|
|
||||||
case 'json':
|
|
||||||
res.setHeader('Content-Type', 'application/json')
|
|
||||||
res.write('{"hello":"world!"}')
|
|
||||||
break
|
|
||||||
case 'html':
|
|
||||||
res.setHeader('Content-Type', 'text/html')
|
|
||||||
res.write('<b>hello, world!</b>')
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
// the fallback is text/plain, so no need to specify it above
|
|
||||||
res.setHeader('Content-Type', 'text/plain')
|
|
||||||
res.write('hello, world!')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
res.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
http.createServer(app).listen(3000)
|
|
||||||
```
|
|
||||||
|
|
||||||
You can test this out with the cURL program:
|
|
||||||
```sh
|
|
||||||
curl -I -H'Accept: text/html' http://localhost:3000/
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[MIT](LICENSE)
|
|
||||||
|
|
||||||
[coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/accepts/master
|
|
||||||
[coveralls-url]: https://coveralls.io/r/jshttp/accepts?branch=master
|
|
||||||
[github-actions-ci-image]: https://badgen.net/github/checks/jshttp/accepts/master?label=ci
|
|
||||||
[github-actions-ci-url]: https://github.com/jshttp/accepts/actions/workflows/ci.yml
|
|
||||||
[node-version-image]: https://badgen.net/npm/node/accepts
|
|
||||||
[node-version-url]: https://nodejs.org/en/download
|
|
||||||
[npm-downloads-image]: https://badgen.net/npm/dm/accepts
|
|
||||||
[npm-url]: https://npmjs.org/package/accepts
|
|
||||||
[npm-version-image]: https://badgen.net/npm/v/accepts
|
|
||||||
-238
@@ -1,238 +0,0 @@
|
|||||||
/*!
|
|
||||||
* accepts
|
|
||||||
* Copyright(c) 2014 Jonathan Ong
|
|
||||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
|
||||||
* MIT Licensed
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var Negotiator = require('negotiator')
|
|
||||||
var mime = require('mime-types')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module exports.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = Accepts
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new Accepts object for the given req.
|
|
||||||
*
|
|
||||||
* @param {object} req
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function Accepts (req) {
|
|
||||||
if (!(this instanceof Accepts)) {
|
|
||||||
return new Accepts(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.headers = req.headers
|
|
||||||
this.negotiator = new Negotiator(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the given `type(s)` is acceptable, returning
|
|
||||||
* the best match when true, otherwise `undefined`, in which
|
|
||||||
* case you should respond with 406 "Not Acceptable".
|
|
||||||
*
|
|
||||||
* The `type` value may be a single mime type string
|
|
||||||
* such as "application/json", the extension name
|
|
||||||
* such as "json" or an array `["json", "html", "text/plain"]`. When a list
|
|
||||||
* or array is given the _best_ match, if any is returned.
|
|
||||||
*
|
|
||||||
* Examples:
|
|
||||||
*
|
|
||||||
* // Accept: text/html
|
|
||||||
* this.types('html');
|
|
||||||
* // => "html"
|
|
||||||
*
|
|
||||||
* // Accept: text/*, application/json
|
|
||||||
* this.types('html');
|
|
||||||
* // => "html"
|
|
||||||
* this.types('text/html');
|
|
||||||
* // => "text/html"
|
|
||||||
* this.types('json', 'text');
|
|
||||||
* // => "json"
|
|
||||||
* this.types('application/json');
|
|
||||||
* // => "application/json"
|
|
||||||
*
|
|
||||||
* // Accept: text/*, application/json
|
|
||||||
* this.types('image/png');
|
|
||||||
* this.types('png');
|
|
||||||
* // => undefined
|
|
||||||
*
|
|
||||||
* // Accept: text/*;q=.5, application/json
|
|
||||||
* this.types(['html', 'json']);
|
|
||||||
* this.types('html', 'json');
|
|
||||||
* // => "json"
|
|
||||||
*
|
|
||||||
* @param {String|Array} types...
|
|
||||||
* @return {String|Array|Boolean}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
Accepts.prototype.type =
|
|
||||||
Accepts.prototype.types = function (types_) {
|
|
||||||
var types = types_
|
|
||||||
|
|
||||||
// support flattened arguments
|
|
||||||
if (types && !Array.isArray(types)) {
|
|
||||||
types = new Array(arguments.length)
|
|
||||||
for (var i = 0; i < types.length; i++) {
|
|
||||||
types[i] = arguments[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no types, return all requested types
|
|
||||||
if (!types || types.length === 0) {
|
|
||||||
return this.negotiator.mediaTypes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// no accept header, return first given type
|
|
||||||
if (!this.headers.accept) {
|
|
||||||
return types[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
var mimes = types.map(extToMime)
|
|
||||||
var accepts = this.negotiator.mediaTypes(mimes.filter(validMime))
|
|
||||||
var first = accepts[0]
|
|
||||||
|
|
||||||
return first
|
|
||||||
? types[mimes.indexOf(first)]
|
|
||||||
: false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return accepted encodings or best fit based on `encodings`.
|
|
||||||
*
|
|
||||||
* Given `Accept-Encoding: gzip, deflate`
|
|
||||||
* an array sorted by quality is returned:
|
|
||||||
*
|
|
||||||
* ['gzip', 'deflate']
|
|
||||||
*
|
|
||||||
* @param {String|Array} encodings...
|
|
||||||
* @return {String|Array}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
Accepts.prototype.encoding =
|
|
||||||
Accepts.prototype.encodings = function (encodings_) {
|
|
||||||
var encodings = encodings_
|
|
||||||
|
|
||||||
// support flattened arguments
|
|
||||||
if (encodings && !Array.isArray(encodings)) {
|
|
||||||
encodings = new Array(arguments.length)
|
|
||||||
for (var i = 0; i < encodings.length; i++) {
|
|
||||||
encodings[i] = arguments[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no encodings, return all requested encodings
|
|
||||||
if (!encodings || encodings.length === 0) {
|
|
||||||
return this.negotiator.encodings()
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.negotiator.encodings(encodings)[0] || false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return accepted charsets or best fit based on `charsets`.
|
|
||||||
*
|
|
||||||
* Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5`
|
|
||||||
* an array sorted by quality is returned:
|
|
||||||
*
|
|
||||||
* ['utf-8', 'utf-7', 'iso-8859-1']
|
|
||||||
*
|
|
||||||
* @param {String|Array} charsets...
|
|
||||||
* @return {String|Array}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
Accepts.prototype.charset =
|
|
||||||
Accepts.prototype.charsets = function (charsets_) {
|
|
||||||
var charsets = charsets_
|
|
||||||
|
|
||||||
// support flattened arguments
|
|
||||||
if (charsets && !Array.isArray(charsets)) {
|
|
||||||
charsets = new Array(arguments.length)
|
|
||||||
for (var i = 0; i < charsets.length; i++) {
|
|
||||||
charsets[i] = arguments[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no charsets, return all requested charsets
|
|
||||||
if (!charsets || charsets.length === 0) {
|
|
||||||
return this.negotiator.charsets()
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.negotiator.charsets(charsets)[0] || false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return accepted languages or best fit based on `langs`.
|
|
||||||
*
|
|
||||||
* Given `Accept-Language: en;q=0.8, es, pt`
|
|
||||||
* an array sorted by quality is returned:
|
|
||||||
*
|
|
||||||
* ['es', 'pt', 'en']
|
|
||||||
*
|
|
||||||
* @param {String|Array} langs...
|
|
||||||
* @return {Array|String}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
Accepts.prototype.lang =
|
|
||||||
Accepts.prototype.langs =
|
|
||||||
Accepts.prototype.language =
|
|
||||||
Accepts.prototype.languages = function (languages_) {
|
|
||||||
var languages = languages_
|
|
||||||
|
|
||||||
// support flattened arguments
|
|
||||||
if (languages && !Array.isArray(languages)) {
|
|
||||||
languages = new Array(arguments.length)
|
|
||||||
for (var i = 0; i < languages.length; i++) {
|
|
||||||
languages[i] = arguments[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no languages, return all requested languages
|
|
||||||
if (!languages || languages.length === 0) {
|
|
||||||
return this.negotiator.languages()
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.negotiator.languages(languages)[0] || false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert extnames to mime.
|
|
||||||
*
|
|
||||||
* @param {String} type
|
|
||||||
* @return {String}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function extToMime (type) {
|
|
||||||
return type.indexOf('/') === -1
|
|
||||||
? mime.lookup(type)
|
|
||||||
: type
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if mime is valid.
|
|
||||||
*
|
|
||||||
* @param {String} type
|
|
||||||
* @return {String}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function validMime (type) {
|
|
||||||
return typeof type === 'string'
|
|
||||||
}
|
|
||||||
-47
@@ -1,47 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "accepts",
|
|
||||||
"description": "Higher-level content negotiation",
|
|
||||||
"version": "1.3.8",
|
|
||||||
"contributors": [
|
|
||||||
"Douglas Christopher Wilson <doug@somethingdoug.com>",
|
|
||||||
"Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"repository": "jshttp/accepts",
|
|
||||||
"dependencies": {
|
|
||||||
"mime-types": "~2.1.34",
|
|
||||||
"negotiator": "0.6.3"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"deep-equal": "1.0.1",
|
|
||||||
"eslint": "7.32.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": "4.3.1",
|
|
||||||
"eslint-plugin-standard": "4.1.0",
|
|
||||||
"mocha": "9.2.0",
|
|
||||||
"nyc": "15.1.0"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"LICENSE",
|
|
||||||
"HISTORY.md",
|
|
||||||
"index.js"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"content",
|
|
||||||
"negotiation",
|
|
||||||
"accept",
|
|
||||||
"accepts"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Blake Embrey (hello@blakeembrey.com)
|
|
||||||
|
|
||||||
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.
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
# Array Flatten
|
|
||||||
|
|
||||||
[![NPM version][npm-image]][npm-url]
|
|
||||||
[![NPM downloads][downloads-image]][downloads-url]
|
|
||||||
[![Build status][travis-image]][travis-url]
|
|
||||||
[![Test coverage][coveralls-image]][coveralls-url]
|
|
||||||
|
|
||||||
> Flatten an array of nested arrays into a single flat array. Accepts an optional depth.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```
|
|
||||||
npm install array-flatten --save
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var flatten = require('array-flatten')
|
|
||||||
|
|
||||||
flatten([1, [2, [3, [4, [5], 6], 7], 8], 9])
|
|
||||||
//=> [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
||||||
|
|
||||||
flatten([1, [2, [3, [4, [5], 6], 7], 8], 9], 2)
|
|
||||||
//=> [1, 2, 3, [4, [5], 6], 7, 8, 9]
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
flatten(arguments) //=> [1, 2, 3]
|
|
||||||
})(1, [2, 3])
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT
|
|
||||||
|
|
||||||
[npm-image]: https://img.shields.io/npm/v/array-flatten.svg?style=flat
|
|
||||||
[npm-url]: https://npmjs.org/package/array-flatten
|
|
||||||
[downloads-image]: https://img.shields.io/npm/dm/array-flatten.svg?style=flat
|
|
||||||
[downloads-url]: https://npmjs.org/package/array-flatten
|
|
||||||
[travis-image]: https://img.shields.io/travis/blakeembrey/array-flatten.svg?style=flat
|
|
||||||
[travis-url]: https://travis-ci.org/blakeembrey/array-flatten
|
|
||||||
[coveralls-image]: https://img.shields.io/coveralls/blakeembrey/array-flatten.svg?style=flat
|
|
||||||
[coveralls-url]: https://coveralls.io/r/blakeembrey/array-flatten?branch=master
|
|
||||||
-64
@@ -1,64 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expose `arrayFlatten`.
|
|
||||||
*/
|
|
||||||
module.exports = arrayFlatten
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursive flatten function with depth.
|
|
||||||
*
|
|
||||||
* @param {Array} array
|
|
||||||
* @param {Array} result
|
|
||||||
* @param {Number} depth
|
|
||||||
* @return {Array}
|
|
||||||
*/
|
|
||||||
function flattenWithDepth (array, result, depth) {
|
|
||||||
for (var i = 0; i < array.length; i++) {
|
|
||||||
var value = array[i]
|
|
||||||
|
|
||||||
if (depth > 0 && Array.isArray(value)) {
|
|
||||||
flattenWithDepth(value, result, depth - 1)
|
|
||||||
} else {
|
|
||||||
result.push(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursive flatten function. Omitting depth is slightly faster.
|
|
||||||
*
|
|
||||||
* @param {Array} array
|
|
||||||
* @param {Array} result
|
|
||||||
* @return {Array}
|
|
||||||
*/
|
|
||||||
function flattenForever (array, result) {
|
|
||||||
for (var i = 0; i < array.length; i++) {
|
|
||||||
var value = array[i]
|
|
||||||
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
flattenForever(value, result)
|
|
||||||
} else {
|
|
||||||
result.push(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flatten an array, with the ability to define a depth.
|
|
||||||
*
|
|
||||||
* @param {Array} array
|
|
||||||
* @param {Number} depth
|
|
||||||
* @return {Array}
|
|
||||||
*/
|
|
||||||
function arrayFlatten (array, depth) {
|
|
||||||
if (depth == null) {
|
|
||||||
return flattenForever(array, [])
|
|
||||||
}
|
|
||||||
|
|
||||||
return flattenWithDepth(array, [], depth)
|
|
||||||
}
|
|
||||||
-39
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "array-flatten",
|
|
||||||
"version": "1.1.1",
|
|
||||||
"description": "Flatten an array of nested arrays into a single flat array",
|
|
||||||
"main": "array-flatten.js",
|
|
||||||
"files": [
|
|
||||||
"array-flatten.js",
|
|
||||||
"LICENSE"
|
|
||||||
],
|
|
||||||
"scripts": {
|
|
||||||
"test": "istanbul cover _mocha -- -R spec"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git://github.com/blakeembrey/array-flatten.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"array",
|
|
||||||
"flatten",
|
|
||||||
"arguments",
|
|
||||||
"depth"
|
|
||||||
],
|
|
||||||
"author": {
|
|
||||||
"name": "Blake Embrey",
|
|
||||||
"email": "hello@blakeembrey.com",
|
|
||||||
"url": "http://blakeembrey.me"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/blakeembrey/array-flatten/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/blakeembrey/array-flatten",
|
|
||||||
"devDependencies": {
|
|
||||||
"istanbul": "^0.3.13",
|
|
||||||
"mocha": "^2.2.4",
|
|
||||||
"pre-commit": "^1.0.7",
|
|
||||||
"standard": "^3.7.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-692
@@ -1,692 +0,0 @@
|
|||||||
1.20.6 / 2026-07-09
|
|
||||||
===================
|
|
||||||
* fix: improve `limit` option validation (#698)
|
|
||||||
* Invalid `limit` values (e.g. unparseable strings or `NaN`) now throw instead of being silently ignored, which previously disabled size limit enforcement
|
|
||||||
* `null` and `undefined` fall back to the default 100kb limit
|
|
||||||
|
|
||||||
1.20.5 / 2026-04-24
|
|
||||||
===================
|
|
||||||
* refactor(json): simplify strict mode error string construction
|
|
||||||
* fix: extended urlencoded parsing of arrays with >100 elements (#716)
|
|
||||||
* deps: qs@~6.15.1
|
|
||||||
|
|
||||||
1.20.4 / 2025-12-01
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: qs@~6.14.0
|
|
||||||
* deps: use tilde notation for dependencies
|
|
||||||
* deps: http-errors@~2.0.1
|
|
||||||
* deps: raw-body@~2.5.3
|
|
||||||
|
|
||||||
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
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Fix strict json error message on Node.js 19+
|
|
||||||
* deps: content-type@~1.0.5
|
|
||||||
- perf: skip value escaping when unnecessary
|
|
||||||
* deps: raw-body@2.5.2
|
|
||||||
|
|
||||||
1.20.1 / 2022-10-06
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: qs@6.11.0
|
|
||||||
* perf: remove unnecessary object clone
|
|
||||||
|
|
||||||
1.20.0 / 2022-04-02
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Fix error message for json parse whitespace in `strict`
|
|
||||||
* Fix internal error when inflated body exceeds limit
|
|
||||||
* Prevent loss of async hooks context
|
|
||||||
* Prevent hanging when request already read
|
|
||||||
* deps: depd@2.0.0
|
|
||||||
- Replace internal `eval` usage with `Function` constructor
|
|
||||||
- Use instance methods on `process` to check for listeners
|
|
||||||
* deps: http-errors@2.0.0
|
|
||||||
- deps: depd@2.0.0
|
|
||||||
- deps: statuses@2.0.1
|
|
||||||
* deps: on-finished@2.4.1
|
|
||||||
* deps: qs@6.10.3
|
|
||||||
* deps: raw-body@2.5.1
|
|
||||||
- deps: http-errors@2.0.0
|
|
||||||
|
|
||||||
1.19.2 / 2022-02-15
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: bytes@3.1.2
|
|
||||||
* deps: qs@6.9.7
|
|
||||||
* Fix handling of `__proto__` keys
|
|
||||||
* deps: raw-body@2.4.3
|
|
||||||
- deps: bytes@3.1.2
|
|
||||||
|
|
||||||
1.19.1 / 2021-12-10
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: bytes@3.1.1
|
|
||||||
* deps: http-errors@1.8.1
|
|
||||||
- deps: inherits@2.0.4
|
|
||||||
- deps: toidentifier@1.0.1
|
|
||||||
- deps: setprototypeof@1.2.0
|
|
||||||
* deps: qs@6.9.6
|
|
||||||
* deps: raw-body@2.4.2
|
|
||||||
- deps: bytes@3.1.1
|
|
||||||
- deps: http-errors@1.8.1
|
|
||||||
* deps: safe-buffer@5.2.1
|
|
||||||
* deps: type-is@~1.6.18
|
|
||||||
|
|
||||||
1.19.0 / 2019-04-25
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: bytes@3.1.0
|
|
||||||
- Add petabyte (`pb`) support
|
|
||||||
* deps: http-errors@1.7.2
|
|
||||||
- Set constructor name when possible
|
|
||||||
- deps: setprototypeof@1.1.1
|
|
||||||
- deps: statuses@'>= 1.5.0 < 2'
|
|
||||||
* deps: iconv-lite@0.4.24
|
|
||||||
- Added encoding MIK
|
|
||||||
* deps: qs@6.7.0
|
|
||||||
- Fix parsing array brackets after index
|
|
||||||
* deps: raw-body@2.4.0
|
|
||||||
- deps: bytes@3.1.0
|
|
||||||
- deps: http-errors@1.7.2
|
|
||||||
- deps: iconv-lite@0.4.24
|
|
||||||
* deps: type-is@~1.6.17
|
|
||||||
- deps: mime-types@~2.1.24
|
|
||||||
- perf: prevent internal `throw` on invalid type
|
|
||||||
|
|
||||||
1.18.3 / 2018-05-14
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Fix stack trace for strict json parse error
|
|
||||||
* deps: depd@~1.1.2
|
|
||||||
- perf: remove argument reassignment
|
|
||||||
* deps: http-errors@~1.6.3
|
|
||||||
- deps: depd@~1.1.2
|
|
||||||
- deps: setprototypeof@1.1.0
|
|
||||||
- deps: statuses@'>= 1.3.1 < 2'
|
|
||||||
* deps: iconv-lite@0.4.23
|
|
||||||
- Fix loading encoding with year appended
|
|
||||||
- Fix deprecation warnings on Node.js 10+
|
|
||||||
* deps: qs@6.5.2
|
|
||||||
* deps: raw-body@2.3.3
|
|
||||||
- deps: http-errors@1.6.3
|
|
||||||
- deps: iconv-lite@0.4.23
|
|
||||||
* deps: type-is@~1.6.16
|
|
||||||
- deps: mime-types@~2.1.18
|
|
||||||
|
|
||||||
1.18.2 / 2017-09-22
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: debug@2.6.9
|
|
||||||
* perf: remove argument reassignment
|
|
||||||
|
|
||||||
1.18.1 / 2017-09-12
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: content-type@~1.0.4
|
|
||||||
- perf: remove argument reassignment
|
|
||||||
- perf: skip parameter parsing when no parameters
|
|
||||||
* deps: iconv-lite@0.4.19
|
|
||||||
- Fix ISO-8859-1 regression
|
|
||||||
- Update Windows-1255
|
|
||||||
* deps: qs@6.5.1
|
|
||||||
- Fix parsing & compacting very deep objects
|
|
||||||
* deps: raw-body@2.3.2
|
|
||||||
- deps: iconv-lite@0.4.19
|
|
||||||
|
|
||||||
1.18.0 / 2017-09-08
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Fix JSON strict violation error to match native parse error
|
|
||||||
* Include the `body` property on verify errors
|
|
||||||
* Include the `type` property on all generated errors
|
|
||||||
* Use `http-errors` to set status code on errors
|
|
||||||
* deps: bytes@3.0.0
|
|
||||||
* deps: debug@2.6.8
|
|
||||||
* deps: depd@~1.1.1
|
|
||||||
- Remove unnecessary `Buffer` loading
|
|
||||||
* deps: http-errors@~1.6.2
|
|
||||||
- deps: depd@1.1.1
|
|
||||||
* deps: iconv-lite@0.4.18
|
|
||||||
- Add support for React Native
|
|
||||||
- Add a warning if not loaded as utf-8
|
|
||||||
- Fix CESU-8 decoding in Node.js 8
|
|
||||||
- Improve speed of ISO-8859-1 encoding
|
|
||||||
* deps: qs@6.5.0
|
|
||||||
* deps: raw-body@2.3.1
|
|
||||||
- Use `http-errors` for standard emitted errors
|
|
||||||
- deps: bytes@3.0.0
|
|
||||||
- deps: iconv-lite@0.4.18
|
|
||||||
- perf: skip buffer decoding on overage chunk
|
|
||||||
* perf: prevent internal `throw` when missing charset
|
|
||||||
|
|
||||||
1.17.2 / 2017-05-17
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: debug@2.6.7
|
|
||||||
- Fix `DEBUG_MAX_ARRAY_LENGTH`
|
|
||||||
- deps: ms@2.0.0
|
|
||||||
* deps: type-is@~1.6.15
|
|
||||||
- deps: mime-types@~2.1.15
|
|
||||||
|
|
||||||
1.17.1 / 2017-03-06
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: qs@6.4.0
|
|
||||||
- Fix regression parsing keys starting with `[`
|
|
||||||
|
|
||||||
1.17.0 / 2017-03-01
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: http-errors@~1.6.1
|
|
||||||
- Make `message` property enumerable for `HttpError`s
|
|
||||||
- deps: setprototypeof@1.0.3
|
|
||||||
* deps: qs@6.3.1
|
|
||||||
- Fix compacting nested arrays
|
|
||||||
|
|
||||||
1.16.1 / 2017-02-10
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: debug@2.6.1
|
|
||||||
- Fix deprecation messages in WebStorm and other editors
|
|
||||||
- Undeprecate `DEBUG_FD` set to `1` or `2`
|
|
||||||
|
|
||||||
1.16.0 / 2017-01-17
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: debug@2.6.0
|
|
||||||
- Allow colors in workers
|
|
||||||
- Deprecated `DEBUG_FD` environment variable
|
|
||||||
- Fix error when running under React Native
|
|
||||||
- Use same color for same namespace
|
|
||||||
- deps: ms@0.7.2
|
|
||||||
* deps: http-errors@~1.5.1
|
|
||||||
- deps: inherits@2.0.3
|
|
||||||
- deps: setprototypeof@1.0.2
|
|
||||||
- deps: statuses@'>= 1.3.1 < 2'
|
|
||||||
* deps: iconv-lite@0.4.15
|
|
||||||
- Added encoding MS-31J
|
|
||||||
- Added encoding MS-932
|
|
||||||
- Added encoding MS-936
|
|
||||||
- Added encoding MS-949
|
|
||||||
- Added encoding MS-950
|
|
||||||
- Fix GBK/GB18030 handling of Euro character
|
|
||||||
* deps: qs@6.2.1
|
|
||||||
- Fix array parsing from skipping empty values
|
|
||||||
* deps: raw-body@~2.2.0
|
|
||||||
- deps: iconv-lite@0.4.15
|
|
||||||
* deps: type-is@~1.6.14
|
|
||||||
- deps: mime-types@~2.1.13
|
|
||||||
|
|
||||||
1.15.2 / 2016-06-19
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: bytes@2.4.0
|
|
||||||
* deps: content-type@~1.0.2
|
|
||||||
- perf: enable strict mode
|
|
||||||
* deps: http-errors@~1.5.0
|
|
||||||
- Use `setprototypeof` module to replace `__proto__` setting
|
|
||||||
- deps: statuses@'>= 1.3.0 < 2'
|
|
||||||
- perf: enable strict mode
|
|
||||||
* deps: qs@6.2.0
|
|
||||||
* deps: raw-body@~2.1.7
|
|
||||||
- deps: bytes@2.4.0
|
|
||||||
- perf: remove double-cleanup on happy path
|
|
||||||
* deps: type-is@~1.6.13
|
|
||||||
- deps: mime-types@~2.1.11
|
|
||||||
|
|
||||||
1.15.1 / 2016-05-05
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: bytes@2.3.0
|
|
||||||
- Drop partial bytes on all parsed units
|
|
||||||
- Fix parsing byte string that looks like hex
|
|
||||||
* deps: raw-body@~2.1.6
|
|
||||||
- deps: bytes@2.3.0
|
|
||||||
* deps: type-is@~1.6.12
|
|
||||||
- deps: mime-types@~2.1.10
|
|
||||||
|
|
||||||
1.15.0 / 2016-02-10
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: http-errors@~1.4.0
|
|
||||||
- Add `HttpError` export, for `err instanceof createError.HttpError`
|
|
||||||
- deps: inherits@2.0.1
|
|
||||||
- deps: statuses@'>= 1.2.1 < 2'
|
|
||||||
* deps: qs@6.1.0
|
|
||||||
* deps: type-is@~1.6.11
|
|
||||||
- deps: mime-types@~2.1.9
|
|
||||||
|
|
||||||
1.14.2 / 2015-12-16
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: bytes@2.2.0
|
|
||||||
* deps: iconv-lite@0.4.13
|
|
||||||
* deps: qs@5.2.0
|
|
||||||
* deps: raw-body@~2.1.5
|
|
||||||
- deps: bytes@2.2.0
|
|
||||||
- deps: iconv-lite@0.4.13
|
|
||||||
* deps: type-is@~1.6.10
|
|
||||||
- deps: mime-types@~2.1.8
|
|
||||||
|
|
||||||
1.14.1 / 2015-09-27
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Fix issue where invalid charset results in 400 when `verify` used
|
|
||||||
* deps: iconv-lite@0.4.12
|
|
||||||
- Fix CESU-8 decoding in Node.js 4.x
|
|
||||||
* deps: raw-body@~2.1.4
|
|
||||||
- Fix masking critical errors from `iconv-lite`
|
|
||||||
- deps: iconv-lite@0.4.12
|
|
||||||
* deps: type-is@~1.6.9
|
|
||||||
- deps: mime-types@~2.1.7
|
|
||||||
|
|
||||||
1.14.0 / 2015-09-16
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Fix JSON strict parse error to match syntax errors
|
|
||||||
* Provide static `require` analysis in `urlencoded` parser
|
|
||||||
* deps: depd@~1.1.0
|
|
||||||
- Support web browser loading
|
|
||||||
* deps: qs@5.1.0
|
|
||||||
* deps: raw-body@~2.1.3
|
|
||||||
- Fix sync callback when attaching data listener causes sync read
|
|
||||||
* deps: type-is@~1.6.8
|
|
||||||
- Fix type error when given invalid type to match against
|
|
||||||
- deps: mime-types@~2.1.6
|
|
||||||
|
|
||||||
1.13.3 / 2015-07-31
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: type-is@~1.6.6
|
|
||||||
- deps: mime-types@~2.1.4
|
|
||||||
|
|
||||||
1.13.2 / 2015-07-05
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: iconv-lite@0.4.11
|
|
||||||
* deps: qs@4.0.0
|
|
||||||
- Fix dropping parameters like `hasOwnProperty`
|
|
||||||
- Fix user-visible incompatibilities from 3.1.0
|
|
||||||
- Fix various parsing edge cases
|
|
||||||
* deps: raw-body@~2.1.2
|
|
||||||
- Fix error stack traces to skip `makeError`
|
|
||||||
- deps: iconv-lite@0.4.11
|
|
||||||
* deps: type-is@~1.6.4
|
|
||||||
- deps: mime-types@~2.1.2
|
|
||||||
- perf: enable strict mode
|
|
||||||
- perf: remove argument reassignment
|
|
||||||
|
|
||||||
1.13.1 / 2015-06-16
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: qs@2.4.2
|
|
||||||
- Downgraded from 3.1.0 because of user-visible incompatibilities
|
|
||||||
|
|
||||||
1.13.0 / 2015-06-14
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Add `statusCode` property on `Error`s, in addition to `status`
|
|
||||||
* Change `type` default to `application/json` for JSON parser
|
|
||||||
* Change `type` default to `application/x-www-form-urlencoded` for urlencoded parser
|
|
||||||
* Provide static `require` analysis
|
|
||||||
* Use the `http-errors` module to generate errors
|
|
||||||
* deps: bytes@2.1.0
|
|
||||||
- Slight optimizations
|
|
||||||
* deps: iconv-lite@0.4.10
|
|
||||||
- The encoding UTF-16 without BOM now defaults to UTF-16LE when detection fails
|
|
||||||
- Leading BOM is now removed when decoding
|
|
||||||
* deps: on-finished@~2.3.0
|
|
||||||
- Add defined behavior for HTTP `CONNECT` requests
|
|
||||||
- Add defined behavior for HTTP `Upgrade` requests
|
|
||||||
- deps: ee-first@1.1.1
|
|
||||||
* deps: qs@3.1.0
|
|
||||||
- Fix dropping parameters like `hasOwnProperty`
|
|
||||||
- Fix various parsing edge cases
|
|
||||||
- Parsed object now has `null` prototype
|
|
||||||
* deps: raw-body@~2.1.1
|
|
||||||
- Use `unpipe` module for unpiping requests
|
|
||||||
- deps: iconv-lite@0.4.10
|
|
||||||
* deps: type-is@~1.6.3
|
|
||||||
- deps: mime-types@~2.1.1
|
|
||||||
- perf: reduce try block size
|
|
||||||
- perf: remove bitwise operations
|
|
||||||
* perf: enable strict mode
|
|
||||||
* perf: remove argument reassignment
|
|
||||||
* perf: remove delete call
|
|
||||||
|
|
||||||
1.12.4 / 2015-05-10
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: debug@~2.2.0
|
|
||||||
* deps: qs@2.4.2
|
|
||||||
- Fix allowing parameters like `constructor`
|
|
||||||
* deps: on-finished@~2.2.1
|
|
||||||
* deps: raw-body@~2.0.1
|
|
||||||
- Fix a false-positive when unpiping in Node.js 0.8
|
|
||||||
- deps: bytes@2.0.1
|
|
||||||
* deps: type-is@~1.6.2
|
|
||||||
- deps: mime-types@~2.0.11
|
|
||||||
|
|
||||||
1.12.3 / 2015-04-15
|
|
||||||
===================
|
|
||||||
|
|
||||||
* Slight efficiency improvement when not debugging
|
|
||||||
* deps: depd@~1.0.1
|
|
||||||
* deps: iconv-lite@0.4.8
|
|
||||||
- Add encoding alias UNICODE-1-1-UTF-7
|
|
||||||
* deps: raw-body@1.3.4
|
|
||||||
- Fix hanging callback if request aborts during read
|
|
||||||
- deps: iconv-lite@0.4.8
|
|
||||||
|
|
||||||
1.12.2 / 2015-03-16
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: qs@2.4.1
|
|
||||||
- Fix error when parameter `hasOwnProperty` is present
|
|
||||||
|
|
||||||
1.12.1 / 2015-03-15
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: debug@~2.1.3
|
|
||||||
- Fix high intensity foreground color for bold
|
|
||||||
- deps: ms@0.7.0
|
|
||||||
* deps: type-is@~1.6.1
|
|
||||||
- deps: mime-types@~2.0.10
|
|
||||||
|
|
||||||
1.12.0 / 2015-02-13
|
|
||||||
===================
|
|
||||||
|
|
||||||
* add `debug` messages
|
|
||||||
* accept a function for the `type` option
|
|
||||||
* use `content-type` to parse `Content-Type` headers
|
|
||||||
* deps: iconv-lite@0.4.7
|
|
||||||
- Gracefully support enumerables on `Object.prototype`
|
|
||||||
* deps: raw-body@1.3.3
|
|
||||||
- deps: iconv-lite@0.4.7
|
|
||||||
* deps: type-is@~1.6.0
|
|
||||||
- fix argument reassignment
|
|
||||||
- fix false-positives in `hasBody` `Transfer-Encoding` check
|
|
||||||
- support wildcard for both type and subtype (`*/*`)
|
|
||||||
- deps: mime-types@~2.0.9
|
|
||||||
|
|
||||||
1.11.0 / 2015-01-30
|
|
||||||
===================
|
|
||||||
|
|
||||||
* make internal `extended: true` depth limit infinity
|
|
||||||
* deps: type-is@~1.5.6
|
|
||||||
- deps: mime-types@~2.0.8
|
|
||||||
|
|
||||||
1.10.2 / 2015-01-20
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: iconv-lite@0.4.6
|
|
||||||
- Fix rare aliases of single-byte encodings
|
|
||||||
* deps: raw-body@1.3.2
|
|
||||||
- deps: iconv-lite@0.4.6
|
|
||||||
|
|
||||||
1.10.1 / 2015-01-01
|
|
||||||
===================
|
|
||||||
|
|
||||||
* deps: on-finished@~2.2.0
|
|
||||||
* deps: type-is@~1.5.5
|
|
||||||
- deps: mime-types@~2.0.7
|
|
||||||
|
|
||||||
1.10.0 / 2014-12-02
|
|
||||||
===================
|
|
||||||
|
|
||||||
* make internal `extended: true` array limit dynamic
|
|
||||||
|
|
||||||
1.9.3 / 2014-11-21
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: iconv-lite@0.4.5
|
|
||||||
- Fix Windows-31J and X-SJIS encoding support
|
|
||||||
* deps: qs@2.3.3
|
|
||||||
- Fix `arrayLimit` behavior
|
|
||||||
* deps: raw-body@1.3.1
|
|
||||||
- deps: iconv-lite@0.4.5
|
|
||||||
* deps: type-is@~1.5.3
|
|
||||||
- deps: mime-types@~2.0.3
|
|
||||||
|
|
||||||
1.9.2 / 2014-10-27
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: qs@2.3.2
|
|
||||||
- Fix parsing of mixed objects and values
|
|
||||||
|
|
||||||
1.9.1 / 2014-10-22
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: on-finished@~2.1.1
|
|
||||||
- Fix handling of pipelined requests
|
|
||||||
* deps: qs@2.3.0
|
|
||||||
- Fix parsing of mixed implicit and explicit arrays
|
|
||||||
* deps: type-is@~1.5.2
|
|
||||||
- deps: mime-types@~2.0.2
|
|
||||||
|
|
||||||
1.9.0 / 2014-09-24
|
|
||||||
==================
|
|
||||||
|
|
||||||
* include the charset in "unsupported charset" error message
|
|
||||||
* include the encoding in "unsupported content encoding" error message
|
|
||||||
* deps: depd@~1.0.0
|
|
||||||
|
|
||||||
1.8.4 / 2014-09-23
|
|
||||||
==================
|
|
||||||
|
|
||||||
* fix content encoding to be case-insensitive
|
|
||||||
|
|
||||||
1.8.3 / 2014-09-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: qs@2.2.4
|
|
||||||
- Fix issue with object keys starting with numbers truncated
|
|
||||||
|
|
||||||
1.8.2 / 2014-09-15
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: depd@0.4.5
|
|
||||||
|
|
||||||
1.8.1 / 2014-09-07
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: media-typer@0.3.0
|
|
||||||
* deps: type-is@~1.5.1
|
|
||||||
|
|
||||||
1.8.0 / 2014-09-05
|
|
||||||
==================
|
|
||||||
|
|
||||||
* make empty-body-handling consistent between chunked requests
|
|
||||||
- empty `json` produces `{}`
|
|
||||||
- empty `raw` produces `new Buffer(0)`
|
|
||||||
- empty `text` produces `''`
|
|
||||||
- empty `urlencoded` produces `{}`
|
|
||||||
* deps: qs@2.2.3
|
|
||||||
- Fix issue where first empty value in array is discarded
|
|
||||||
* deps: type-is@~1.5.0
|
|
||||||
- fix `hasbody` to be true for `content-length: 0`
|
|
||||||
|
|
||||||
1.7.0 / 2014-09-01
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add `parameterLimit` option to `urlencoded` parser
|
|
||||||
* change `urlencoded` extended array limit to 100
|
|
||||||
* respond with 413 when over `parameterLimit` in `urlencoded`
|
|
||||||
|
|
||||||
1.6.7 / 2014-08-29
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: qs@2.2.2
|
|
||||||
- Remove unnecessary cloning
|
|
||||||
|
|
||||||
1.6.6 / 2014-08-27
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: qs@2.2.0
|
|
||||||
- Array parsing fix
|
|
||||||
- Performance improvements
|
|
||||||
|
|
||||||
1.6.5 / 2014-08-16
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: on-finished@2.1.0
|
|
||||||
|
|
||||||
1.6.4 / 2014-08-14
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: qs@1.2.2
|
|
||||||
|
|
||||||
1.6.3 / 2014-08-10
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: qs@1.2.1
|
|
||||||
|
|
||||||
1.6.2 / 2014-08-07
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: qs@1.2.0
|
|
||||||
- Fix parsing array of objects
|
|
||||||
|
|
||||||
1.6.1 / 2014-08-06
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: qs@1.1.0
|
|
||||||
- Accept urlencoded square brackets
|
|
||||||
- Accept empty values in implicit array notation
|
|
||||||
|
|
||||||
1.6.0 / 2014-08-05
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: qs@1.0.2
|
|
||||||
- Complete rewrite
|
|
||||||
- Limits array length to 20
|
|
||||||
- Limits object depth to 5
|
|
||||||
- Limits parameters to 1,000
|
|
||||||
|
|
||||||
1.5.2 / 2014-07-27
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: depd@0.4.4
|
|
||||||
- Work-around v8 generating empty stack traces
|
|
||||||
|
|
||||||
1.5.1 / 2014-07-26
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: depd@0.4.3
|
|
||||||
- Fix exception when global `Error.stackTraceLimit` is too low
|
|
||||||
|
|
||||||
1.5.0 / 2014-07-20
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: depd@0.4.2
|
|
||||||
- Add `TRACE_DEPRECATION` environment variable
|
|
||||||
- Remove non-standard grey color from color output
|
|
||||||
- Support `--no-deprecation` argument
|
|
||||||
- Support `--trace-deprecation` argument
|
|
||||||
* deps: iconv-lite@0.4.4
|
|
||||||
- Added encoding UTF-7
|
|
||||||
* deps: raw-body@1.3.0
|
|
||||||
- deps: iconv-lite@0.4.4
|
|
||||||
- Added encoding UTF-7
|
|
||||||
- Fix `Cannot switch to old mode now` error on Node.js 0.10+
|
|
||||||
* deps: type-is@~1.3.2
|
|
||||||
|
|
||||||
1.4.3 / 2014-06-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: type-is@1.3.1
|
|
||||||
- fix global variable leak
|
|
||||||
|
|
||||||
1.4.2 / 2014-06-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: type-is@1.3.0
|
|
||||||
- improve type parsing
|
|
||||||
|
|
||||||
1.4.1 / 2014-06-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* fix urlencoded extended deprecation message
|
|
||||||
|
|
||||||
1.4.0 / 2014-06-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add `text` parser
|
|
||||||
* add `raw` parser
|
|
||||||
* check accepted charset in content-type (accepts utf-8)
|
|
||||||
* check accepted encoding in content-encoding (accepts identity)
|
|
||||||
* deprecate `bodyParser()` middleware; use `.json()` and `.urlencoded()` as needed
|
|
||||||
* deprecate `urlencoded()` without provided `extended` option
|
|
||||||
* lazy-load urlencoded parsers
|
|
||||||
* parsers split into files for reduced mem usage
|
|
||||||
* support gzip and deflate bodies
|
|
||||||
- set `inflate: false` to turn off
|
|
||||||
* deps: raw-body@1.2.2
|
|
||||||
- Support all encodings from `iconv-lite`
|
|
||||||
|
|
||||||
1.3.1 / 2014-06-11
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: type-is@1.2.1
|
|
||||||
- Switch dependency from mime to mime-types@1.0.0
|
|
||||||
|
|
||||||
1.3.0 / 2014-05-31
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add `extended` option to urlencoded parser
|
|
||||||
|
|
||||||
1.2.2 / 2014-05-27
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: raw-body@1.1.6
|
|
||||||
- assert stream encoding on node.js 0.8
|
|
||||||
- assert stream encoding on node.js < 0.10.6
|
|
||||||
- deps: bytes@1
|
|
||||||
|
|
||||||
1.2.1 / 2014-05-26
|
|
||||||
==================
|
|
||||||
|
|
||||||
* invoke `next(err)` after request fully read
|
|
||||||
- prevents hung responses and socket hang ups
|
|
||||||
|
|
||||||
1.2.0 / 2014-05-11
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add `verify` option
|
|
||||||
* deps: type-is@1.2.0
|
|
||||||
- support suffix matching
|
|
||||||
|
|
||||||
1.1.2 / 2014-05-11
|
|
||||||
==================
|
|
||||||
|
|
||||||
* improve json parser speed
|
|
||||||
|
|
||||||
1.1.1 / 2014-05-11
|
|
||||||
==================
|
|
||||||
|
|
||||||
* fix repeated limit parsing with every request
|
|
||||||
|
|
||||||
1.1.0 / 2014-05-10
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add `type` option
|
|
||||||
* deps: pin for safety and consistency
|
|
||||||
|
|
||||||
1.0.2 / 2014-04-14
|
|
||||||
==================
|
|
||||||
|
|
||||||
* use `type-is` module
|
|
||||||
|
|
||||||
1.0.1 / 2014-03-20
|
|
||||||
==================
|
|
||||||
|
|
||||||
* lower default limits to 100kb
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
(The MIT License)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Jonathan Ong <me@jongleberry.com>
|
|
||||||
Copyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com>
|
|
||||||
|
|
||||||
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.
|
|
||||||
-476
@@ -1,476 +0,0 @@
|
|||||||
# body-parser
|
|
||||||
|
|
||||||
[![NPM Version][npm-version-image]][npm-url]
|
|
||||||
[![NPM Downloads][npm-downloads-image]][npm-url]
|
|
||||||
[![Build Status][ci-image]][ci-url]
|
|
||||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
|
||||||
[![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]
|
|
||||||
|
|
||||||
Node.js body parsing middleware.
|
|
||||||
|
|
||||||
Parse incoming request bodies in a middleware before your handlers, available
|
|
||||||
under the `req.body` property.
|
|
||||||
|
|
||||||
**Note** As `req.body`'s shape is based on user-controlled input, all
|
|
||||||
properties and values in this object are untrusted and should be validated
|
|
||||||
before trusting. For example, `req.body.foo.toString()` may fail in multiple
|
|
||||||
ways, for example the `foo` property may not be there or may not be a string,
|
|
||||||
and `toString` may not be a function and instead a string or other user input.
|
|
||||||
|
|
||||||
[Learn about the anatomy of an HTTP transaction in Node.js](https://nodejs.org/en/docs/guides/anatomy-of-an-http-transaction/).
|
|
||||||
|
|
||||||
_This does not handle multipart bodies_, due to their complex and typically
|
|
||||||
large nature. For multipart bodies, you may be interested in the following
|
|
||||||
modules:
|
|
||||||
|
|
||||||
* [busboy](https://www.npmjs.org/package/busboy#readme) and
|
|
||||||
[connect-busboy](https://www.npmjs.org/package/connect-busboy#readme)
|
|
||||||
* [multiparty](https://www.npmjs.org/package/multiparty#readme) and
|
|
||||||
[connect-multiparty](https://www.npmjs.org/package/connect-multiparty#readme)
|
|
||||||
* [formidable](https://www.npmjs.org/package/formidable#readme)
|
|
||||||
* [multer](https://www.npmjs.org/package/multer#readme)
|
|
||||||
|
|
||||||
This module provides the following parsers:
|
|
||||||
|
|
||||||
* [JSON body parser](#bodyparserjsonoptions)
|
|
||||||
* [Raw body parser](#bodyparserrawoptions)
|
|
||||||
* [Text body parser](#bodyparsertextoptions)
|
|
||||||
* [URL-encoded form body parser](#bodyparserurlencodedoptions)
|
|
||||||
|
|
||||||
Other body parsers you might be interested in:
|
|
||||||
|
|
||||||
- [body](https://www.npmjs.org/package/body#readme)
|
|
||||||
- [co-body](https://www.npmjs.org/package/co-body#readme)
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ npm install body-parser
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
```js
|
|
||||||
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, 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).
|
|
||||||
|
|
||||||
### bodyParser.json([options])
|
|
||||||
|
|
||||||
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` and
|
|
||||||
`deflate` encodings.
|
|
||||||
|
|
||||||
A new `body` object containing the parsed data is populated on the `request`
|
|
||||||
object after the middleware (i.e. `req.body`).
|
|
||||||
|
|
||||||
#### Options
|
|
||||||
|
|
||||||
The `json` function takes an optional `options` object that may contain any of
|
|
||||||
the following keys:
|
|
||||||
|
|
||||||
##### inflate
|
|
||||||
|
|
||||||
When set to `true`, then deflated (compressed) bodies will be inflated; when
|
|
||||||
`false`, deflated bodies are rejected. Defaults to `true`.
|
|
||||||
|
|
||||||
##### limit
|
|
||||||
|
|
||||||
Controls the maximum request body size. If this is a number, then the value
|
|
||||||
specifies the number of bytes; if it is a string, the value is passed to the
|
|
||||||
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
|
||||||
to `'100kb'`.
|
|
||||||
|
|
||||||
##### reviver
|
|
||||||
|
|
||||||
The `reviver` option is passed directly to `JSON.parse` as the second
|
|
||||||
argument. You can find more information on this argument
|
|
||||||
[in the MDN documentation about JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Example.3A_Using_the_reviver_parameter).
|
|
||||||
|
|
||||||
##### strict
|
|
||||||
|
|
||||||
When set to `true`, will only accept arrays and objects; when `false` will
|
|
||||||
accept anything `JSON.parse` accepts. Defaults to `true`.
|
|
||||||
|
|
||||||
##### type
|
|
||||||
|
|
||||||
The `type` option is used to determine what media type the middleware will
|
|
||||||
parse. This option can be a string, array of strings, or a function. If not a
|
|
||||||
function, `type` option is passed directly to the
|
|
||||||
[type-is](https://www.npmjs.org/package/type-is#readme) library and this can
|
|
||||||
be an extension name (like `json`), a mime type (like `application/json`), or
|
|
||||||
a mime type with a wildcard (like `*/*` or `*/json`). If a function, the `type`
|
|
||||||
option is called as `fn(req)` and the request is parsed if it returns a truthy
|
|
||||||
value. Defaults to `application/json`.
|
|
||||||
|
|
||||||
##### verify
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
### bodyParser.raw([options])
|
|
||||||
|
|
||||||
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` 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
|
|
||||||
of the body.
|
|
||||||
|
|
||||||
#### Options
|
|
||||||
|
|
||||||
The `raw` function takes an optional `options` object that may contain any of
|
|
||||||
the following keys:
|
|
||||||
|
|
||||||
##### inflate
|
|
||||||
|
|
||||||
When set to `true`, then deflated (compressed) bodies will be inflated; when
|
|
||||||
`false`, deflated bodies are rejected. Defaults to `true`.
|
|
||||||
|
|
||||||
##### limit
|
|
||||||
|
|
||||||
Controls the maximum request body size. If this is a number, then the value
|
|
||||||
specifies the number of bytes; if it is a string, the value is passed to the
|
|
||||||
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
|
||||||
to `'100kb'`.
|
|
||||||
|
|
||||||
##### type
|
|
||||||
|
|
||||||
The `type` option is used to determine what media type the middleware will
|
|
||||||
parse. This option can be a string, array of strings, or a function.
|
|
||||||
If not a function, `type` option is passed directly to the
|
|
||||||
[type-is](https://www.npmjs.org/package/type-is#readme) library and this
|
|
||||||
can be an extension name (like `bin`), a mime type (like
|
|
||||||
`application/octet-stream`), or a mime type with a wildcard (like `*/*` or
|
|
||||||
`application/*`). If a function, the `type` option is called as `fn(req)`
|
|
||||||
and the request is parsed if it returns a truthy value. Defaults to
|
|
||||||
`application/octet-stream`.
|
|
||||||
|
|
||||||
##### verify
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
### bodyParser.text([options])
|
|
||||||
|
|
||||||
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` 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
|
|
||||||
body.
|
|
||||||
|
|
||||||
#### Options
|
|
||||||
|
|
||||||
The `text` function takes an optional `options` object that may contain any of
|
|
||||||
the following keys:
|
|
||||||
|
|
||||||
##### defaultCharset
|
|
||||||
|
|
||||||
Specify the default character set for the text content if the charset is not
|
|
||||||
specified in the `Content-Type` header of the request. Defaults to `utf-8`.
|
|
||||||
|
|
||||||
##### inflate
|
|
||||||
|
|
||||||
When set to `true`, then deflated (compressed) bodies will be inflated; when
|
|
||||||
`false`, deflated bodies are rejected. Defaults to `true`.
|
|
||||||
|
|
||||||
##### limit
|
|
||||||
|
|
||||||
Controls the maximum request body size. If this is a number, then the value
|
|
||||||
specifies the number of bytes; if it is a string, the value is passed to the
|
|
||||||
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
|
||||||
to `'100kb'`.
|
|
||||||
|
|
||||||
##### type
|
|
||||||
|
|
||||||
The `type` option is used to determine what media type the middleware will
|
|
||||||
parse. This option can be a string, array of strings, or a function. If not
|
|
||||||
a function, `type` option is passed directly to the
|
|
||||||
[type-is](https://www.npmjs.org/package/type-is#readme) library and this can
|
|
||||||
be an extension name (like `txt`), a mime type (like `text/plain`), or a mime
|
|
||||||
type with a wildcard (like `*/*` or `text/*`). If a function, the `type`
|
|
||||||
option is called as `fn(req)` and the request is parsed if it returns a
|
|
||||||
truthy value. Defaults to `text/plain`.
|
|
||||||
|
|
||||||
##### verify
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
### bodyParser.urlencoded([options])
|
|
||||||
|
|
||||||
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` 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
|
|
||||||
key-value pairs, where the value can be a string or array (when `extended` is
|
|
||||||
`false`), or any type (when `extended` is `true`).
|
|
||||||
|
|
||||||
#### Options
|
|
||||||
|
|
||||||
The `urlencoded` function takes an optional `options` object that may contain
|
|
||||||
any of the following keys:
|
|
||||||
|
|
||||||
##### extended
|
|
||||||
|
|
||||||
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 `true`, but using the default has been deprecated. Please
|
|
||||||
research into the difference between `qs` and `querystring` and choose the
|
|
||||||
appropriate setting.
|
|
||||||
|
|
||||||
##### inflate
|
|
||||||
|
|
||||||
When set to `true`, then deflated (compressed) bodies will be inflated; when
|
|
||||||
`false`, deflated bodies are rejected. Defaults to `true`.
|
|
||||||
|
|
||||||
##### limit
|
|
||||||
|
|
||||||
Controls the maximum request body size. If this is a number, then the value
|
|
||||||
specifies the number of bytes; if it is a string, the value is passed to the
|
|
||||||
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
|
||||||
to `'100kb'`.
|
|
||||||
|
|
||||||
##### parameterLimit
|
|
||||||
|
|
||||||
The `parameterLimit` option controls the maximum number of parameters that
|
|
||||||
are allowed in the URL-encoded data. If a request contains more parameters
|
|
||||||
than this value, a 413 will be returned to the client. Defaults to `1000`.
|
|
||||||
|
|
||||||
##### type
|
|
||||||
|
|
||||||
The `type` option is used to determine what media type the middleware will
|
|
||||||
parse. This option can be a string, array of strings, or a function. If not
|
|
||||||
a function, `type` option is passed directly to the
|
|
||||||
[type-is](https://www.npmjs.org/package/type-is#readme) library and this can
|
|
||||||
be an extension name (like `urlencoded`), a mime type (like
|
|
||||||
`application/x-www-form-urlencoded`), or a mime type with a wildcard (like
|
|
||||||
`*/x-www-form-urlencoded`). If a function, the `type` option is called as
|
|
||||||
`fn(req)` and the request is parsed if it returns a truthy value. Defaults
|
|
||||||
to `application/x-www-form-urlencoded`.
|
|
||||||
|
|
||||||
##### verify
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
#### 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.
|
|
||||||
|
|
||||||
## Errors
|
|
||||||
|
|
||||||
The middlewares provided by this module create errors using the
|
|
||||||
[`http-errors` module](https://www.npmjs.com/package/http-errors). The errors
|
|
||||||
will typically have a `status`/`statusCode` property that contains the suggested
|
|
||||||
HTTP response code, an `expose` property to determine if the `message` property
|
|
||||||
should be displayed to the client, a `type` property to determine the type of
|
|
||||||
error without matching against the `message`, and a `body` property containing
|
|
||||||
the read body, if available.
|
|
||||||
|
|
||||||
The following are the common errors created, though any error can come through
|
|
||||||
for various reasons.
|
|
||||||
|
|
||||||
### content encoding unsupported
|
|
||||||
|
|
||||||
This error will occur when the request had a `Content-Encoding` header that
|
|
||||||
contained an encoding but the "inflation" option was set to `false`. The
|
|
||||||
`status` property is set to `415`, the `type` property is set to
|
|
||||||
`'encoding.unsupported'`, and the `charset` property will be set to the
|
|
||||||
encoding that is unsupported.
|
|
||||||
|
|
||||||
### entity parse failed
|
|
||||||
|
|
||||||
This error will occur when the request contained an entity that could not be
|
|
||||||
parsed by the middleware. The `status` property is set to `400`, the `type`
|
|
||||||
property is set to `'entity.parse.failed'`, and the `body` property is set to
|
|
||||||
the entity value that failed parsing.
|
|
||||||
|
|
||||||
### entity verify failed
|
|
||||||
|
|
||||||
This error will occur when the request contained an entity that could not be
|
|
||||||
failed verification by the defined `verify` option. The `status` property is
|
|
||||||
set to `403`, the `type` property is set to `'entity.verify.failed'`, and the
|
|
||||||
`body` property is set to the entity value that failed verification.
|
|
||||||
|
|
||||||
### request aborted
|
|
||||||
|
|
||||||
This error will occur when the request is aborted by the client before reading
|
|
||||||
the body has finished. The `received` property will be set to the number of
|
|
||||||
bytes received before the request was aborted and the `expected` property is
|
|
||||||
set to the number of expected bytes. The `status` property is set to `400`
|
|
||||||
and `type` property is set to `'request.aborted'`.
|
|
||||||
|
|
||||||
### request entity too large
|
|
||||||
|
|
||||||
This error will occur when the request body's size is larger than the "limit"
|
|
||||||
option. The `limit` property will be set to the byte limit and the `length`
|
|
||||||
property will be set to the request body's length. The `status` property is
|
|
||||||
set to `413` and the `type` property is set to `'entity.too.large'`.
|
|
||||||
|
|
||||||
### request size did not match content length
|
|
||||||
|
|
||||||
This error will occur when the request's length did not match the length from
|
|
||||||
the `Content-Length` header. This typically occurs when the request is malformed,
|
|
||||||
typically when the `Content-Length` header was calculated based on characters
|
|
||||||
instead of bytes. The `status` property is set to `400` and the `type` property
|
|
||||||
is set to `'request.size.invalid'`.
|
|
||||||
|
|
||||||
### stream encoding should not be set
|
|
||||||
|
|
||||||
This error will occur when something called the `req.setEncoding` method prior
|
|
||||||
to this middleware. This module operates directly on bytes only and you cannot
|
|
||||||
call `req.setEncoding` when using this module. The `status` property is set to
|
|
||||||
`500` and the `type` property is set to `'stream.encoding.set'`.
|
|
||||||
|
|
||||||
### stream is not readable
|
|
||||||
|
|
||||||
This error will occur when the request is no longer readable when this middleware
|
|
||||||
attempts to read it. This typically means something other than a middleware from
|
|
||||||
this module read the request body already and the middleware was also configured to
|
|
||||||
read the same request. The `status` property is set to `500` and the `type`
|
|
||||||
property is set to `'stream.not.readable'`.
|
|
||||||
|
|
||||||
### too many parameters
|
|
||||||
|
|
||||||
This error will occur when the content of the request exceeds the configured
|
|
||||||
`parameterLimit` for the `urlencoded` parser. The `status` property is set to
|
|
||||||
`413` and the `type` property is set to `'parameters.too.many'`.
|
|
||||||
|
|
||||||
### unsupported charset "BOGUS"
|
|
||||||
|
|
||||||
This error will occur when the request had a charset parameter in the
|
|
||||||
`Content-Type` header, but the `iconv-lite` module does not support it OR the
|
|
||||||
parser does not support it. The charset is contained in the message as well
|
|
||||||
as in the `charset` property. The `status` property is set to `415`, the
|
|
||||||
`type` property is set to `'charset.unsupported'`, and the `charset` property
|
|
||||||
is set to the charset that is unsupported.
|
|
||||||
|
|
||||||
### unsupported content encoding "bogus"
|
|
||||||
|
|
||||||
This error will occur when the request had a `Content-Encoding` header that
|
|
||||||
contained an unsupported encoding. The encoding is contained in the message
|
|
||||||
as well as in the `encoding` property. The `status` property is set to `415`,
|
|
||||||
the `type` property is set to `'encoding.unsupported'`, and the `encoding`
|
|
||||||
property is set to the encoding that is unsupported.
|
|
||||||
|
|
||||||
### The input exceeded the depth
|
|
||||||
|
|
||||||
This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Express/Connect top-level generic
|
|
||||||
|
|
||||||
This example demonstrates adding a generic JSON and URL-encoded parser as a
|
|
||||||
top-level middleware, which will parse the bodies of all incoming requests.
|
|
||||||
This is the simplest setup.
|
|
||||||
|
|
||||||
```js
|
|
||||||
var express = require('express')
|
|
||||||
var bodyParser = require('body-parser')
|
|
||||||
|
|
||||||
var app = express()
|
|
||||||
|
|
||||||
// parse application/x-www-form-urlencoded
|
|
||||||
app.use(bodyParser.urlencoded({ extended: false }))
|
|
||||||
|
|
||||||
// parse application/json
|
|
||||||
app.use(bodyParser.json())
|
|
||||||
|
|
||||||
app.use(function (req, res) {
|
|
||||||
res.setHeader('Content-Type', 'text/plain')
|
|
||||||
res.write('you posted:\n')
|
|
||||||
res.end(JSON.stringify(req.body, null, 2))
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Express route-specific
|
|
||||||
|
|
||||||
This example demonstrates adding body parsers specifically to the routes that
|
|
||||||
need them. In general, this is the most recommended way to use body-parser with
|
|
||||||
Express.
|
|
||||||
|
|
||||||
```js
|
|
||||||
var express = require('express')
|
|
||||||
var bodyParser = require('body-parser')
|
|
||||||
|
|
||||||
var app = express()
|
|
||||||
|
|
||||||
// create application/json parser
|
|
||||||
var jsonParser = bodyParser.json()
|
|
||||||
|
|
||||||
// create application/x-www-form-urlencoded parser
|
|
||||||
var urlencodedParser = bodyParser.urlencoded({ extended: false })
|
|
||||||
|
|
||||||
// POST /login gets urlencoded bodies
|
|
||||||
app.post('/login', urlencodedParser, function (req, res) {
|
|
||||||
res.send('welcome, ' + req.body.username)
|
|
||||||
})
|
|
||||||
|
|
||||||
// POST /api/users gets JSON bodies
|
|
||||||
app.post('/api/users', jsonParser, function (req, res) {
|
|
||||||
// create user in req.body
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Change accepted type for parsers
|
|
||||||
|
|
||||||
All the parsers accept a `type` option which allows you to change the
|
|
||||||
`Content-Type` that the middleware will parse.
|
|
||||||
|
|
||||||
```js
|
|
||||||
var express = require('express')
|
|
||||||
var bodyParser = require('body-parser')
|
|
||||||
|
|
||||||
var app = express()
|
|
||||||
|
|
||||||
// parse various different custom JSON types as JSON
|
|
||||||
app.use(bodyParser.json({ type: 'application/*+json' }))
|
|
||||||
|
|
||||||
// parse some custom thing into a Buffer
|
|
||||||
app.use(bodyParser.raw({ type: 'application/vnd.custom-type' }))
|
|
||||||
|
|
||||||
// parse an HTML body into a string
|
|
||||||
app.use(bodyParser.text({ type: 'text/html' }))
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[MIT](LICENSE)
|
|
||||||
|
|
||||||
[ci-image]: https://badgen.net/github/checks/expressjs/body-parser/master?label=ci
|
|
||||||
[ci-url]: https://github.com/expressjs/body-parser/actions/workflows/ci.yml
|
|
||||||
[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/body-parser/master
|
|
||||||
[coveralls-url]: https://coveralls.io/r/expressjs/body-parser?branch=master
|
|
||||||
[node-version-image]: https://badgen.net/npm/node/body-parser
|
|
||||||
[node-version-url]: https://nodejs.org/en/download
|
|
||||||
[npm-downloads-image]: https://badgen.net/npm/dm/body-parser
|
|
||||||
[npm-url]: https://npmjs.org/package/body-parser
|
|
||||||
[npm-version-image]: https://badgen.net/npm/v/body-parser
|
|
||||||
[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/body-parser/badge
|
|
||||||
[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/body-parser
|
|
||||||
-156
@@ -1,156 +0,0 @@
|
|||||||
/*!
|
|
||||||
* body-parser
|
|
||||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
|
||||||
* MIT Licensed
|
|
||||||
*/
|
|
||||||
|
|
||||||
'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}
|
|
||||||
* @property {function} json
|
|
||||||
* @property {function} raw
|
|
||||||
* @property {function} text
|
|
||||||
* @property {function} urlencoded
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module exports.
|
|
||||||
* @type {Parsers}
|
|
||||||
*/
|
|
||||||
|
|
||||||
exports = module.exports = deprecate.function(bodyParser,
|
|
||||||
'bodyParser: use individual json/urlencoded middlewares')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSON parser.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
Object.defineProperty(exports, 'json', {
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
get: createParserGetter('json')
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Raw parser.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
Object.defineProperty(exports, 'raw', {
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
get: createParserGetter('raw')
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Text parser.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
Object.defineProperty(exports, 'text', {
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
get: createParserGetter('text')
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* URL-encoded parser.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
Object.defineProperty(exports, 'urlencoded', {
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
get: createParserGetter('urlencoded')
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a middleware to parse json and urlencoded bodies.
|
|
||||||
*
|
|
||||||
* @param {object} [options]
|
|
||||||
* @return {function}
|
|
||||||
* @deprecated
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
-205
@@ -1,205 +0,0 @@
|
|||||||
/*!
|
|
||||||
* body-parser
|
|
||||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
|
||||||
* MIT Licensed
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
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 unpipe = require('unpipe')
|
|
||||||
var zlib = require('zlib')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module exports.
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = read
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a request into a buffer and parse.
|
|
||||||
*
|
|
||||||
* @param {object} req
|
|
||||||
* @param {object} res
|
|
||||||
* @param {function} next
|
|
||||||
* @param {function} parse
|
|
||||||
* @param {function} debug
|
|
||||||
* @param {object} options
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function read (req, res, next, parse, debug, options) {
|
|
||||||
var length
|
|
||||||
var opts = options
|
|
||||||
var stream
|
|
||||||
|
|
||||||
// flag as parsed
|
|
||||||
req._body = true
|
|
||||||
|
|
||||||
// read options
|
|
||||||
var encoding = opts.encoding !== null
|
|
||||||
? opts.encoding
|
|
||||||
: null
|
|
||||||
var verify = opts.verify
|
|
||||||
|
|
||||||
try {
|
|
||||||
// get the content stream
|
|
||||||
stream = contentstream(req, debug, opts.inflate)
|
|
||||||
length = stream.length
|
|
||||||
stream.length = undefined
|
|
||||||
} catch (err) {
|
|
||||||
return next(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set raw-body options
|
|
||||||
opts.length = length
|
|
||||||
opts.encoding = verify
|
|
||||||
? null
|
|
||||||
: encoding
|
|
||||||
|
|
||||||
// assert charset is supported
|
|
||||||
if (opts.encoding === null && encoding !== null && !iconv.encodingExists(encoding)) {
|
|
||||||
return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
|
|
||||||
charset: encoding.toLowerCase(),
|
|
||||||
type: 'charset.unsupported'
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// read body
|
|
||||||
debug('read body')
|
|
||||||
getBody(stream, opts, function (error, body) {
|
|
||||||
if (error) {
|
|
||||||
var _error
|
|
||||||
|
|
||||||
if (error.type === 'encoding.unsupported') {
|
|
||||||
// echo back charset
|
|
||||||
_error = createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
|
|
||||||
charset: encoding.toLowerCase(),
|
|
||||||
type: 'charset.unsupported'
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// set status code on error
|
|
||||||
_error = createError(400, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpipe from stream and destroy
|
|
||||||
if (stream !== req) {
|
|
||||||
unpipe(req)
|
|
||||||
destroy(stream, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// read off entire request
|
|
||||||
dump(req, function onfinished () {
|
|
||||||
next(createError(400, _error))
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify
|
|
||||||
if (verify) {
|
|
||||||
try {
|
|
||||||
debug('verify body')
|
|
||||||
verify(req, res, body, encoding)
|
|
||||||
} catch (err) {
|
|
||||||
next(createError(403, err, {
|
|
||||||
body: body,
|
|
||||||
type: err.type || 'entity.verify.failed'
|
|
||||||
}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse
|
|
||||||
var str = body
|
|
||||||
try {
|
|
||||||
debug('parse body')
|
|
||||||
str = typeof body !== 'string' && encoding !== null
|
|
||||||
? iconv.decode(body, encoding)
|
|
||||||
: body
|
|
||||||
req.body = parse(str)
|
|
||||||
} catch (err) {
|
|
||||||
next(createError(400, err, {
|
|
||||||
body: str,
|
|
||||||
type: err.type || 'entity.parse.failed'
|
|
||||||
}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the content stream of the request.
|
|
||||||
*
|
|
||||||
* @param {object} req
|
|
||||||
* @param {function} debug
|
|
||||||
* @param {boolean} [inflate=true]
|
|
||||||
* @return {object}
|
|
||||||
* @api private
|
|
||||||
*/
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
if (inflate === false && encoding !== 'identity') {
|
|
||||||
throw createError(415, 'content encoding unsupported', {
|
|
||||||
encoding: encoding,
|
|
||||||
type: 'encoding.unsupported'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (encoding) {
|
|
||||||
case 'deflate':
|
|
||||||
stream = zlib.createInflate()
|
|
||||||
debug('inflate body')
|
|
||||||
req.pipe(stream)
|
|
||||||
break
|
|
||||||
case 'gzip':
|
|
||||||
stream = zlib.createGunzip()
|
|
||||||
debug('gunzip body')
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dump the contents of a request.
|
|
||||||
*
|
|
||||||
* @param {object} req
|
|
||||||
* @param {function} callback
|
|
||||||
* @api private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function dump (req, callback) {
|
|
||||||
if (onFinished.isFinished(req)) {
|
|
||||||
callback(null)
|
|
||||||
} else {
|
|
||||||
onFinished(req, callback)
|
|
||||||
req.resume()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-247
@@ -1,247 +0,0 @@
|
|||||||
/*!
|
|
||||||
* body-parser
|
|
||||||
* Copyright(c) 2014 Jonathan Ong
|
|
||||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
|
||||||
* MIT Licensed
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var bytes = require('bytes')
|
|
||||||
var contentType = require('content-type')
|
|
||||||
var createError = require('http-errors')
|
|
||||||
var debug = require('debug')('body-parser:json')
|
|
||||||
var read = require('../read')
|
|
||||||
var typeis = require('type-is')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module exports.
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = json
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match the first non-space in a string.
|
|
||||||
*
|
|
||||||
* Allowed whitespace is defined in RFC 7159:
|
|
||||||
*
|
|
||||||
* ws = *(
|
|
||||||
* %x20 / ; Space
|
|
||||||
* %x09 / ; Horizontal tab
|
|
||||||
* %x0A / ; Line feed or New line
|
|
||||||
* %x0D ) ; Carriage return
|
|
||||||
*/
|
|
||||||
|
|
||||||
var FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex
|
|
||||||
|
|
||||||
var JSON_SYNTAX_CHAR = '#'
|
|
||||||
var JSON_SYNTAX_REGEXP = /#+/g
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a middleware to parse JSON bodies.
|
|
||||||
*
|
|
||||||
* @param {object} [options]
|
|
||||||
* @return {function}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function json (options) {
|
|
||||||
var opts = options || {}
|
|
||||||
|
|
||||||
var limit = typeof opts.limit === 'undefined' || opts.limit === null
|
|
||||||
? 102400 // 100kb default
|
|
||||||
: bytes.parse(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 (limit === null) {
|
|
||||||
throw new TypeError('option limit "' + String(opts.limit) + '" is invalid')
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
// special-case empty json body, as it's a common client-side mistake
|
|
||||||
// TODO: maybe make this configurable or part of "strict" option
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strict) {
|
|
||||||
var first = firstchar(body)
|
|
||||||
|
|
||||||
if (first !== '{' && first !== '[') {
|
|
||||||
debug('strict violation')
|
|
||||||
throw createStrictSyntaxError(body, first)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
debug('parse json')
|
|
||||||
return JSON.parse(body, reviver)
|
|
||||||
} catch (e) {
|
|
||||||
throw normalizeJsonSyntaxError(e, {
|
|
||||||
message: e.message,
|
|
||||||
stack: e.stack
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return function jsonParser (req, res, next) {
|
|
||||||
if (req._body) {
|
|
||||||
debug('body already parsed')
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.body = req.body || {}
|
|
||||||
|
|
||||||
// skip requests without bodies
|
|
||||||
if (!typeis.hasBody(req)) {
|
|
||||||
debug('skip empty body')
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
debug('content-type %j', req.headers['content-type'])
|
|
||||||
|
|
||||||
// determine if request should be parsed
|
|
||||||
if (!shouldParse(req)) {
|
|
||||||
debug('skip parsing')
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert charset per RFC 7159 sec 8.1
|
|
||||||
var charset = getCharset(req) || 'utf-8'
|
|
||||||
if (charset.slice(0, 4) !== 'utf-') {
|
|
||||||
debug('invalid charset')
|
|
||||||
next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
|
|
||||||
charset: charset,
|
|
||||||
type: 'charset.unsupported'
|
|
||||||
}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// read
|
|
||||||
read(req, res, next, parse, debug, {
|
|
||||||
encoding: charset,
|
|
||||||
inflate: inflate,
|
|
||||||
limit: limit,
|
|
||||||
verify: verify
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create strict violation syntax error matching native error.
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* @param {string} char
|
|
||||||
* @return {Error}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function createStrictSyntaxError (str, char) {
|
|
||||||
var index = str.indexOf(char)
|
|
||||||
var partial = ''
|
|
||||||
|
|
||||||
if (index !== -1) {
|
|
||||||
partial = str.substring(0, index) + new Array(str.length - index + 1).join(JSON_SYNTAX_CHAR)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
JSON.parse(partial); /* istanbul ignore next */ throw new SyntaxError('strict violation')
|
|
||||||
} catch (e) {
|
|
||||||
return normalizeJsonSyntaxError(e, {
|
|
||||||
message: e.message.replace(JSON_SYNTAX_REGEXP, function (placeholder) {
|
|
||||||
return str.substring(index, index + placeholder.length)
|
|
||||||
}),
|
|
||||||
stack: e.stack
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the first non-whitespace character in a string.
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* @return {function}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function firstchar (str) {
|
|
||||||
var match = FIRST_CHAR_REGEXP.exec(str)
|
|
||||||
|
|
||||||
return match
|
|
||||||
? match[1]
|
|
||||||
: 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.
|
|
||||||
*
|
|
||||||
* @param {SyntaxError} error
|
|
||||||
* @param {object} obj
|
|
||||||
* @return {SyntaxError}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function normalizeJsonSyntaxError (error, obj) {
|
|
||||||
var keys = Object.getOwnPropertyNames(error)
|
|
||||||
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
var key = keys[i]
|
|
||||||
if (key !== 'stack' && key !== 'message') {
|
|
||||||
delete error[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace stack before message for Node.js 0.10 and below
|
|
||||||
error.stack = obj.stack.replace(error.message, obj.message)
|
|
||||||
error.message = obj.message
|
|
||||||
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the simple type checker.
|
|
||||||
*
|
|
||||||
* @param {string} type
|
|
||||||
* @return {function}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function typeChecker (type) {
|
|
||||||
return function checkType (req) {
|
|
||||||
return Boolean(typeis(req, type))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-105
@@ -1,105 +0,0 @@
|
|||||||
/*!
|
|
||||||
* body-parser
|
|
||||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
|
||||||
* MIT Licensed
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var bytes = require('bytes')
|
|
||||||
var debug = require('debug')('body-parser:raw')
|
|
||||||
var read = require('../read')
|
|
||||||
var typeis = require('type-is')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module exports.
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = raw
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a middleware to parse raw bodies.
|
|
||||||
*
|
|
||||||
* @param {object} [options]
|
|
||||||
* @return {function}
|
|
||||||
* @api public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function raw (options) {
|
|
||||||
var opts = options || {}
|
|
||||||
|
|
||||||
var inflate = opts.inflate !== false
|
|
||||||
var limit = typeof opts.limit === 'undefined' || opts.limit === null
|
|
||||||
? 102400 // 100kb default
|
|
||||||
: bytes.parse(opts.limit)
|
|
||||||
var type = opts.type || 'application/octet-stream'
|
|
||||||
var verify = opts.verify || false
|
|
||||||
|
|
||||||
if (limit === null) {
|
|
||||||
throw new TypeError('option limit "' + String(opts.limit) + '" is invalid')
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (req._body) {
|
|
||||||
debug('body already parsed')
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.body = req.body || {}
|
|
||||||
|
|
||||||
// skip requests without bodies
|
|
||||||
if (!typeis.hasBody(req)) {
|
|
||||||
debug('skip empty body')
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
debug('content-type %j', req.headers['content-type'])
|
|
||||||
|
|
||||||
// determine if request should be parsed
|
|
||||||
if (!shouldParse(req)) {
|
|
||||||
debug('skip parsing')
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// read
|
|
||||||
read(req, res, next, parse, debug, {
|
|
||||||
encoding: null,
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-125
@@ -1,125 +0,0 @@
|
|||||||
/*!
|
|
||||||
* body-parser
|
|
||||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
|
||||||
* MIT Licensed
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var bytes = require('bytes')
|
|
||||||
var contentType = require('content-type')
|
|
||||||
var debug = require('debug')('body-parser:text')
|
|
||||||
var read = require('../read')
|
|
||||||
var typeis = require('type-is')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module exports.
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = text
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a middleware to parse text bodies.
|
|
||||||
*
|
|
||||||
* @param {object} [options]
|
|
||||||
* @return {function}
|
|
||||||
* @api public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function text (options) {
|
|
||||||
var opts = options || {}
|
|
||||||
|
|
||||||
var defaultCharset = opts.defaultCharset || 'utf-8'
|
|
||||||
var inflate = opts.inflate !== false
|
|
||||||
var limit = typeof opts.limit === 'undefined' || opts.limit === null
|
|
||||||
? 102400 // 100kb default
|
|
||||||
: bytes.parse(opts.limit)
|
|
||||||
var type = opts.type || 'text/plain'
|
|
||||||
var verify = opts.verify || false
|
|
||||||
|
|
||||||
if (limit === null) {
|
|
||||||
throw new TypeError('option limit "' + String(opts.limit) + '" is invalid')
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (req._body) {
|
|
||||||
debug('body already parsed')
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.body = req.body || {}
|
|
||||||
|
|
||||||
// skip requests without bodies
|
|
||||||
if (!typeis.hasBody(req)) {
|
|
||||||
debug('skip empty body')
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
debug('content-type %j', req.headers['content-type'])
|
|
||||||
|
|
||||||
// determine if request should be parsed
|
|
||||||
if (!shouldParse(req)) {
|
|
||||||
debug('skip parsing')
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// get charset
|
|
||||||
var charset = getCharset(req) || defaultCharset
|
|
||||||
|
|
||||||
// read
|
|
||||||
read(req, res, next, parse, debug, {
|
|
||||||
encoding: charset,
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-303
@@ -1,303 +0,0 @@
|
|||||||
/*!
|
|
||||||
* body-parser
|
|
||||||
* Copyright(c) 2014 Jonathan Ong
|
|
||||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
|
||||||
* MIT Licensed
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var bytes = require('bytes')
|
|
||||||
var contentType = require('content-type')
|
|
||||||
var createError = require('http-errors')
|
|
||||||
var debug = require('debug')('body-parser:urlencoded')
|
|
||||||
var deprecate = require('depd')('body-parser')
|
|
||||||
var read = require('../read')
|
|
||||||
var typeis = require('type-is')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module exports.
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = urlencoded
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cache of parser modules.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var parsers = Object.create(null)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a middleware to parse urlencoded bodies.
|
|
||||||
*
|
|
||||||
* @param {object} [options]
|
|
||||||
* @return {function}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function urlencoded (options) {
|
|
||||||
var opts = options || {}
|
|
||||||
|
|
||||||
// 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 === 'undefined' || opts.limit === null
|
|
||||||
? 102400 // 100kb default
|
|
||||||
: bytes.parse(opts.limit)
|
|
||||||
var type = opts.type || 'application/x-www-form-urlencoded'
|
|
||||||
var verify = opts.verify || false
|
|
||||||
|
|
||||||
if (limit === null) {
|
|
||||||
throw new TypeError('option limit "' + String(opts.limit) + '" is invalid')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verify !== false && typeof verify !== 'function') {
|
|
||||||
throw new TypeError('option verify must be function')
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the appropriate query parser
|
|
||||||
var queryparse = extended
|
|
||||||
? extendedparser(opts)
|
|
||||||
: simpleparser(opts)
|
|
||||||
|
|
||||||
// create the appropriate type checking function
|
|
||||||
var shouldParse = typeof type !== 'function'
|
|
||||||
? typeChecker(type)
|
|
||||||
: type
|
|
||||||
|
|
||||||
function parse (body) {
|
|
||||||
return body.length
|
|
||||||
? queryparse(body)
|
|
||||||
: {}
|
|
||||||
}
|
|
||||||
|
|
||||||
return function urlencodedParser (req, res, next) {
|
|
||||||
if (req._body) {
|
|
||||||
debug('body already parsed')
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.body = req.body || {}
|
|
||||||
|
|
||||||
// skip requests without bodies
|
|
||||||
if (!typeis.hasBody(req)) {
|
|
||||||
debug('skip empty body')
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
debug('content-type %j', req.headers['content-type'])
|
|
||||||
|
|
||||||
// determine if request should be parsed
|
|
||||||
if (!shouldParse(req)) {
|
|
||||||
debug('skip parsing')
|
|
||||||
next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert charset
|
|
||||||
var charset = getCharset(req) || 'utf-8'
|
|
||||||
if (charset !== 'utf-8') {
|
|
||||||
debug('invalid charset')
|
|
||||||
next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
|
|
||||||
charset: charset,
|
|
||||||
type: 'charset.unsupported'
|
|
||||||
}))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// read
|
|
||||||
read(req, res, next, parse, debug, {
|
|
||||||
debug: debug,
|
|
||||||
encoding: charset,
|
|
||||||
inflate: inflate,
|
|
||||||
limit: limit,
|
|
||||||
verify: verify
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the extended query parser.
|
|
||||||
*
|
|
||||||
* @param {object} options
|
|
||||||
*/
|
|
||||||
|
|
||||||
function extendedparser (options) {
|
|
||||||
var parameterLimit = options.parameterLimit !== undefined
|
|
||||||
? options.parameterLimit
|
|
||||||
: 1000
|
|
||||||
var depth = options.depth !== undefined ? options.depth : 32
|
|
||||||
var parse = parser('qs')
|
|
||||||
|
|
||||||
if (isNaN(parameterLimit) || parameterLimit < 1) {
|
|
||||||
throw new TypeError('option parameterLimit must be a positive number')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNaN(depth) || depth < 0) {
|
|
||||||
throw new TypeError('option depth must be a zero or 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'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var arrayLimit = Math.max(100, paramCount)
|
|
||||||
|
|
||||||
debug('parse extended urlencoding')
|
|
||||||
try {
|
|
||||||
return parse(body, {
|
|
||||||
allowPrototypes: true,
|
|
||||||
arrayLimit: arrayLimit,
|
|
||||||
depth: depth,
|
|
||||||
strictDepth: true,
|
|
||||||
parameterLimit: parameterLimit
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof RangeError) {
|
|
||||||
throw createError(400, 'The input exceeded the depth', {
|
|
||||||
type: 'querystring.parse.rangeError'
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*
|
|
||||||
* @param {string} body
|
|
||||||
* @param {number} limit
|
|
||||||
* @api private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function parameterCount (body, limit) {
|
|
||||||
var count = 0
|
|
||||||
var index = -1
|
|
||||||
|
|
||||||
do {
|
|
||||||
count++
|
|
||||||
if (count > limit) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
index = body.indexOf('&', index + 1)
|
|
||||||
} while (index !== -1)
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-55
@@ -1,55 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "body-parser",
|
|
||||||
"description": "Node.js body parsing middleware",
|
|
||||||
"version": "1.20.6",
|
|
||||||
"contributors": [
|
|
||||||
"Douglas Christopher Wilson <doug@somethingdoug.com>",
|
|
||||||
"Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"repository": "expressjs/body-parser",
|
|
||||||
"dependencies": {
|
|
||||||
"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.1",
|
|
||||||
"iconv-lite": "~0.4.24",
|
|
||||||
"on-finished": "~2.4.1",
|
|
||||||
"qs": "~6.15.1",
|
|
||||||
"raw-body": "~2.5.3",
|
|
||||||
"type-is": "~1.6.18",
|
|
||||||
"unpipe": "~1.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"eslint": "8.34.0",
|
|
||||||
"eslint-config-standard": "14.1.1",
|
|
||||||
"eslint-plugin-import": "2.27.5",
|
|
||||||
"eslint-plugin-markdown": "3.0.0",
|
|
||||||
"eslint-plugin-node": "11.1.0",
|
|
||||||
"eslint-plugin-promise": "6.1.1",
|
|
||||||
"eslint-plugin-standard": "4.1.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",
|
|
||||||
"index.js"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8",
|
|
||||||
"npm": "1.2.8000 || >= 1.4.16"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"lint": "eslint .",
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-97
@@ -1,97 +0,0 @@
|
|||||||
3.1.2 / 2022-01-27
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix return value for un-parsable strings
|
|
||||||
|
|
||||||
3.1.1 / 2021-11-15
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix "thousandsSeparator" incorrecting formatting fractional part
|
|
||||||
|
|
||||||
3.1.0 / 2019-01-22
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Add petabyte (`pb`) support
|
|
||||||
|
|
||||||
3.0.0 / 2017-08-31
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Change "kB" to "KB" in format output
|
|
||||||
* Remove support for Node.js 0.6
|
|
||||||
* Remove support for ComponentJS
|
|
||||||
|
|
||||||
2.5.0 / 2017-03-24
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Add option "unit"
|
|
||||||
|
|
||||||
2.4.0 / 2016-06-01
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Add option "unitSeparator"
|
|
||||||
|
|
||||||
2.3.0 / 2016-02-15
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Drop partial bytes on all parsed units
|
|
||||||
* Fix non-finite numbers to `.format` to return `null`
|
|
||||||
* Fix parsing byte string that looks like hex
|
|
||||||
* perf: hoist regular expressions
|
|
||||||
|
|
||||||
2.2.0 / 2015-11-13
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add option "decimalPlaces"
|
|
||||||
* add option "fixedDecimals"
|
|
||||||
|
|
||||||
2.1.0 / 2015-05-21
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add `.format` export
|
|
||||||
* add `.parse` export
|
|
||||||
|
|
||||||
2.0.2 / 2015-05-20
|
|
||||||
==================
|
|
||||||
|
|
||||||
* remove map recreation
|
|
||||||
* remove unnecessary object construction
|
|
||||||
|
|
||||||
2.0.1 / 2015-05-07
|
|
||||||
==================
|
|
||||||
|
|
||||||
* fix browserify require
|
|
||||||
* remove node.extend dependency
|
|
||||||
|
|
||||||
2.0.0 / 2015-04-12
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add option "case"
|
|
||||||
* add option "thousandsSeparator"
|
|
||||||
* return "null" on invalid parse input
|
|
||||||
* support proper round-trip: bytes(bytes(num)) === num
|
|
||||||
* units no longer case sensitive when parsing
|
|
||||||
|
|
||||||
1.0.0 / 2014-05-05
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add negative support. fixes #6
|
|
||||||
|
|
||||||
0.3.0 / 2014-03-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* added terabyte support
|
|
||||||
|
|
||||||
0.2.1 / 2013-04-01
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add .component
|
|
||||||
|
|
||||||
0.2.0 / 2012-10-28
|
|
||||||
==================
|
|
||||||
|
|
||||||
* bytes(200).should.eql('200b')
|
|
||||||
|
|
||||||
0.1.0 / 2012-07-04
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add bytes to string conversion [yields]
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
(The MIT License)
|
|
||||||
|
|
||||||
Copyright (c) 2012-2014 TJ Holowaychuk <tj@vision-media.ca>
|
|
||||||
Copyright (c) 2015 Jed Watson <jed.watson@me.com>
|
|
||||||
|
|
||||||
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.
|
|
||||||
-152
@@ -1,152 +0,0 @@
|
|||||||
# Bytes utility
|
|
||||||
|
|
||||||
[![NPM Version][npm-image]][npm-url]
|
|
||||||
[![NPM Downloads][downloads-image]][downloads-url]
|
|
||||||
[![Build Status][ci-image]][ci-url]
|
|
||||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
|
||||||
|
|
||||||
Utility to parse a string bytes (ex: `1TB`) to bytes (`1099511627776`) and vice-versa.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
This is a [Node.js](https://nodejs.org/en/) module available through the
|
|
||||||
[npm registry](https://www.npmjs.com/). Installation is done using the
|
|
||||||
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ npm install bytes
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```js
|
|
||||||
var bytes = require('bytes');
|
|
||||||
```
|
|
||||||
|
|
||||||
#### bytes(number|string value, [options]): number|string|null
|
|
||||||
|
|
||||||
Default export function. Delegates to either `bytes.format` or `bytes.parse` based on the type of `value`.
|
|
||||||
|
|
||||||
**Arguments**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
|---------|----------|--------------------|
|
|
||||||
| value | `number`|`string` | Number value to format or string value to parse |
|
|
||||||
| options | `Object` | Conversion options for `format` |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
|---------|------------------|-------------------------------------------------|
|
|
||||||
| results | `string`|`number`|`null` | Return null upon error. Numeric value in bytes, or string value otherwise. |
|
|
||||||
|
|
||||||
**Example**
|
|
||||||
|
|
||||||
```js
|
|
||||||
bytes(1024);
|
|
||||||
// output: '1KB'
|
|
||||||
|
|
||||||
bytes('1KB');
|
|
||||||
// output: 1024
|
|
||||||
```
|
|
||||||
|
|
||||||
#### bytes.format(number value, [options]): string|null
|
|
||||||
|
|
||||||
Format the given value in bytes into a string. If the value is negative, it is kept as such. If it is a float, it is
|
|
||||||
rounded.
|
|
||||||
|
|
||||||
**Arguments**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
|---------|----------|--------------------|
|
|
||||||
| value | `number` | Value in bytes |
|
|
||||||
| options | `Object` | Conversion options |
|
|
||||||
|
|
||||||
**Options**
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
|-------------------|--------|-----------------------------------------------------------------------------------------|
|
|
||||||
| decimalPlaces | `number`|`null` | Maximum number of decimal places to include in output. Default value to `2`. |
|
|
||||||
| fixedDecimals | `boolean`|`null` | Whether to always display the maximum number of decimal places. Default value to `false` |
|
|
||||||
| thousandsSeparator | `string`|`null` | Example of values: `' '`, `','` and `'.'`... Default value to `''`. |
|
|
||||||
| unit | `string`|`null` | The unit in which the result will be returned (B/KB/MB/GB/TB). Default value to `''` (which means auto detect). |
|
|
||||||
| unitSeparator | `string`|`null` | Separator to use between number and unit. Default value to `''`. |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
|---------|------------------|-------------------------------------------------|
|
|
||||||
| results | `string`|`null` | Return null upon error. String value otherwise. |
|
|
||||||
|
|
||||||
**Example**
|
|
||||||
|
|
||||||
```js
|
|
||||||
bytes.format(1024);
|
|
||||||
// output: '1KB'
|
|
||||||
|
|
||||||
bytes.format(1000);
|
|
||||||
// output: '1000B'
|
|
||||||
|
|
||||||
bytes.format(1000, {thousandsSeparator: ' '});
|
|
||||||
// output: '1 000B'
|
|
||||||
|
|
||||||
bytes.format(1024 * 1.7, {decimalPlaces: 0});
|
|
||||||
// output: '2KB'
|
|
||||||
|
|
||||||
bytes.format(1024, {unitSeparator: ' '});
|
|
||||||
// output: '1 KB'
|
|
||||||
```
|
|
||||||
|
|
||||||
#### bytes.parse(string|number value): number|null
|
|
||||||
|
|
||||||
Parse the string value into an integer in bytes. If no unit is given, or `value`
|
|
||||||
is a number, it is assumed the value is in bytes.
|
|
||||||
|
|
||||||
Supported units and abbreviations are as follows and are case-insensitive:
|
|
||||||
|
|
||||||
* `b` for bytes
|
|
||||||
* `kb` for kilobytes
|
|
||||||
* `mb` for megabytes
|
|
||||||
* `gb` for gigabytes
|
|
||||||
* `tb` for terabytes
|
|
||||||
* `pb` for petabytes
|
|
||||||
|
|
||||||
The units are in powers of two, not ten. This means 1kb = 1024b according to this parser.
|
|
||||||
|
|
||||||
**Arguments**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
|---------------|--------|--------------------|
|
|
||||||
| value | `string`|`number` | String to parse, or number in bytes. |
|
|
||||||
|
|
||||||
**Returns**
|
|
||||||
|
|
||||||
| Name | Type | Description |
|
|
||||||
|---------|-------------|-------------------------|
|
|
||||||
| results | `number`|`null` | Return null upon error. Value in bytes otherwise. |
|
|
||||||
|
|
||||||
**Example**
|
|
||||||
|
|
||||||
```js
|
|
||||||
bytes.parse('1KB');
|
|
||||||
// output: 1024
|
|
||||||
|
|
||||||
bytes.parse('1024');
|
|
||||||
// output: 1024
|
|
||||||
|
|
||||||
bytes.parse(1024);
|
|
||||||
// output: 1024
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[MIT](LICENSE)
|
|
||||||
|
|
||||||
[ci-image]: https://badgen.net/github/checks/visionmedia/bytes.js/master?label=ci
|
|
||||||
[ci-url]: https://github.com/visionmedia/bytes.js/actions?query=workflow%3Aci
|
|
||||||
[coveralls-image]: https://badgen.net/coveralls/c/github/visionmedia/bytes.js/master
|
|
||||||
[coveralls-url]: https://coveralls.io/r/visionmedia/bytes.js?branch=master
|
|
||||||
[downloads-image]: https://badgen.net/npm/dm/bytes
|
|
||||||
[downloads-url]: https://npmjs.org/package/bytes
|
|
||||||
[npm-image]: https://badgen.net/npm/v/bytes
|
|
||||||
[npm-url]: https://npmjs.org/package/bytes
|
|
||||||
-170
@@ -1,170 +0,0 @@
|
|||||||
/*!
|
|
||||||
* bytes
|
|
||||||
* Copyright(c) 2012-2014 TJ Holowaychuk
|
|
||||||
* Copyright(c) 2015 Jed Watson
|
|
||||||
* MIT Licensed
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module exports.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = bytes;
|
|
||||||
module.exports.format = format;
|
|
||||||
module.exports.parse = parse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module variables.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var formatThousandsRegExp = /\B(?=(\d{3})+(?!\d))/g;
|
|
||||||
|
|
||||||
var formatDecimalsRegExp = /(?:\.0*|(\.[^0]+)0+)$/;
|
|
||||||
|
|
||||||
var map = {
|
|
||||||
b: 1,
|
|
||||||
kb: 1 << 10,
|
|
||||||
mb: 1 << 20,
|
|
||||||
gb: 1 << 30,
|
|
||||||
tb: Math.pow(1024, 4),
|
|
||||||
pb: Math.pow(1024, 5),
|
|
||||||
};
|
|
||||||
|
|
||||||
var parseRegExp = /^((-|\+)?(\d+(?:\.\d+)?)) *(kb|mb|gb|tb|pb)$/i;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the given value in bytes into a string or parse to string to an integer in bytes.
|
|
||||||
*
|
|
||||||
* @param {string|number} value
|
|
||||||
* @param {{
|
|
||||||
* case: [string],
|
|
||||||
* decimalPlaces: [number]
|
|
||||||
* fixedDecimals: [boolean]
|
|
||||||
* thousandsSeparator: [string]
|
|
||||||
* unitSeparator: [string]
|
|
||||||
* }} [options] bytes options.
|
|
||||||
*
|
|
||||||
* @returns {string|number|null}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function bytes(value, options) {
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
return parse(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof value === 'number') {
|
|
||||||
return format(value, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format the given value in bytes into a string.
|
|
||||||
*
|
|
||||||
* If the value is negative, it is kept as such. If it is a float,
|
|
||||||
* it is rounded.
|
|
||||||
*
|
|
||||||
* @param {number} value
|
|
||||||
* @param {object} [options]
|
|
||||||
* @param {number} [options.decimalPlaces=2]
|
|
||||||
* @param {number} [options.fixedDecimals=false]
|
|
||||||
* @param {string} [options.thousandsSeparator=]
|
|
||||||
* @param {string} [options.unit=]
|
|
||||||
* @param {string} [options.unitSeparator=]
|
|
||||||
*
|
|
||||||
* @returns {string|null}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function format(value, options) {
|
|
||||||
if (!Number.isFinite(value)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var mag = Math.abs(value);
|
|
||||||
var thousandsSeparator = (options && options.thousandsSeparator) || '';
|
|
||||||
var unitSeparator = (options && options.unitSeparator) || '';
|
|
||||||
var decimalPlaces = (options && options.decimalPlaces !== undefined) ? options.decimalPlaces : 2;
|
|
||||||
var fixedDecimals = Boolean(options && options.fixedDecimals);
|
|
||||||
var unit = (options && options.unit) || '';
|
|
||||||
|
|
||||||
if (!unit || !map[unit.toLowerCase()]) {
|
|
||||||
if (mag >= map.pb) {
|
|
||||||
unit = 'PB';
|
|
||||||
} else if (mag >= map.tb) {
|
|
||||||
unit = 'TB';
|
|
||||||
} else if (mag >= map.gb) {
|
|
||||||
unit = 'GB';
|
|
||||||
} else if (mag >= map.mb) {
|
|
||||||
unit = 'MB';
|
|
||||||
} else if (mag >= map.kb) {
|
|
||||||
unit = 'KB';
|
|
||||||
} else {
|
|
||||||
unit = 'B';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var val = value / map[unit.toLowerCase()];
|
|
||||||
var str = val.toFixed(decimalPlaces);
|
|
||||||
|
|
||||||
if (!fixedDecimals) {
|
|
||||||
str = str.replace(formatDecimalsRegExp, '$1');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thousandsSeparator) {
|
|
||||||
str = str.split('.').map(function (s, i) {
|
|
||||||
return i === 0
|
|
||||||
? s.replace(formatThousandsRegExp, thousandsSeparator)
|
|
||||||
: s
|
|
||||||
}).join('.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return str + unitSeparator + unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the string value into an integer in bytes.
|
|
||||||
*
|
|
||||||
* If no unit is given, it is assumed the value is in bytes.
|
|
||||||
*
|
|
||||||
* @param {number|string} val
|
|
||||||
*
|
|
||||||
* @returns {number|null}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function parse(val) {
|
|
||||||
if (typeof val === 'number' && !isNaN(val)) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof val !== 'string') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test if the string passed is valid
|
|
||||||
var results = parseRegExp.exec(val);
|
|
||||||
var floatValue;
|
|
||||||
var unit = 'b';
|
|
||||||
|
|
||||||
if (!results) {
|
|
||||||
// Nothing could be extracted from the given string
|
|
||||||
floatValue = parseInt(val, 10);
|
|
||||||
unit = 'b'
|
|
||||||
} else {
|
|
||||||
// Retrieve the value and the unit
|
|
||||||
floatValue = parseFloat(results[1]);
|
|
||||||
unit = results[4].toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNaN(floatValue)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.floor(map[unit] * floatValue);
|
|
||||||
}
|
|
||||||
-42
@@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "bytes",
|
|
||||||
"description": "Utility to parse a string bytes to bytes and vice-versa",
|
|
||||||
"version": "3.1.2",
|
|
||||||
"author": "TJ Holowaychuk <tj@vision-media.ca> (http://tjholowaychuk.com)",
|
|
||||||
"contributors": [
|
|
||||||
"Jed Watson <jed.watson@me.com>",
|
|
||||||
"Théo FIDRY <theo.fidry@gmail.com>"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"keywords": [
|
|
||||||
"byte",
|
|
||||||
"bytes",
|
|
||||||
"utility",
|
|
||||||
"parse",
|
|
||||||
"parser",
|
|
||||||
"convert",
|
|
||||||
"converter"
|
|
||||||
],
|
|
||||||
"repository": "visionmedia/bytes.js",
|
|
||||||
"devDependencies": {
|
|
||||||
"eslint": "7.32.0",
|
|
||||||
"eslint-plugin-markdown": "2.2.1",
|
|
||||||
"mocha": "9.2.0",
|
|
||||||
"nyc": "15.1.0"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"History.md",
|
|
||||||
"LICENSE",
|
|
||||||
"Readme.md",
|
|
||||||
"index.js"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"lint": "eslint .",
|
|
||||||
"test": "mocha --check-leaks --reporter spec",
|
|
||||||
"test-ci": "nyc --reporter=lcov --reporter=text npm test",
|
|
||||||
"test-cov": "nyc --reporter=html --reporter=text npm test"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BIN
Binary file not shown.
-17
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"root": true,
|
|
||||||
|
|
||||||
"extends": "@ljharb",
|
|
||||||
|
|
||||||
"rules": {
|
|
||||||
"func-name-matching": 0,
|
|
||||||
"id-length": 0,
|
|
||||||
"new-cap": [2, {
|
|
||||||
"capIsNewExceptions": [
|
|
||||||
"GetIntrinsic",
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
"no-extra-parens": 0,
|
|
||||||
"no-magic-numbers": 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: [ljharb]
|
|
||||||
patreon: # Replace with a single Patreon username
|
|
||||||
open_collective: # Replace with a single Open Collective username
|
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
|
||||||
tidelift: npm/call-bind-apply-helpers
|
|
||||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
||||||
liberapay: # Replace with a single Liberapay username
|
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
|
||||||
otechie: # Replace with a single Otechie username
|
|
||||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
||||||
-9
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"all": true,
|
|
||||||
"check-coverage": false,
|
|
||||||
"reporter": ["text-summary", "text", "html", "json"],
|
|
||||||
"exclude": [
|
|
||||||
"coverage",
|
|
||||||
"test"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
-30
@@ -1,30 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [v1.0.2](https://github.com/ljharb/call-bind-apply-helpers/compare/v1.0.1...v1.0.2) - 2025-02-12
|
|
||||||
|
|
||||||
### Commits
|
|
||||||
|
|
||||||
- [types] improve inferred types [`e6f9586`](https://github.com/ljharb/call-bind-apply-helpers/commit/e6f95860a3c72879cb861a858cdfb8138fbedec1)
|
|
||||||
- [Dev Deps] update `@arethetypeswrong/cli`, `@ljharb/tsconfig`, `@types/tape`, `es-value-fixtures`, `for-each`, `has-strict-mode`, `object-inspect` [`e43d540`](https://github.com/ljharb/call-bind-apply-helpers/commit/e43d5409f97543bfbb11f345d47d8ce4e066d8c1)
|
|
||||||
|
|
||||||
## [v1.0.1](https://github.com/ljharb/call-bind-apply-helpers/compare/v1.0.0...v1.0.1) - 2024-12-08
|
|
||||||
|
|
||||||
### Commits
|
|
||||||
|
|
||||||
- [types] `reflectApply`: fix types [`4efc396`](https://github.com/ljharb/call-bind-apply-helpers/commit/4efc3965351a4f02cc55e836fa391d3d11ef2ef8)
|
|
||||||
- [Fix] `reflectApply`: oops, Reflect is not a function [`83cc739`](https://github.com/ljharb/call-bind-apply-helpers/commit/83cc7395de6b79b7730bdf092f1436f0b1263c75)
|
|
||||||
- [Dev Deps] update `@arethetypeswrong/cli` [`80bd5d3`](https://github.com/ljharb/call-bind-apply-helpers/commit/80bd5d3ae58b4f6b6995ce439dd5a1bcb178a940)
|
|
||||||
|
|
||||||
## v1.0.0 - 2024-12-05
|
|
||||||
|
|
||||||
### Commits
|
|
||||||
|
|
||||||
- Initial implementation, tests, readme [`7879629`](https://github.com/ljharb/call-bind-apply-helpers/commit/78796290f9b7430c9934d6f33d94ae9bc89fce04)
|
|
||||||
- Initial commit [`3f1dc16`](https://github.com/ljharb/call-bind-apply-helpers/commit/3f1dc164afc43285631b114a5f9dd9137b2b952f)
|
|
||||||
- npm init [`081df04`](https://github.com/ljharb/call-bind-apply-helpers/commit/081df048c312fcee400922026f6e97281200a603)
|
|
||||||
- Only apps should have lockfiles [`5b9ca0f`](https://github.com/ljharb/call-bind-apply-helpers/commit/5b9ca0fe8101ebfaf309c549caac4e0a017ed930)
|
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2024 Jordan Harband
|
|
||||||
|
|
||||||
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.
|
|
||||||
-62
@@ -1,62 +0,0 @@
|
|||||||
# call-bind-apply-helpers <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
|
|
||||||
|
|
||||||
[![github actions][actions-image]][actions-url]
|
|
||||||
[![coverage][codecov-image]][codecov-url]
|
|
||||||
[![dependency status][deps-svg]][deps-url]
|
|
||||||
[![dev dependency status][dev-deps-svg]][dev-deps-url]
|
|
||||||
[![License][license-image]][license-url]
|
|
||||||
[![Downloads][downloads-image]][downloads-url]
|
|
||||||
|
|
||||||
[![npm badge][npm-badge-png]][package-url]
|
|
||||||
|
|
||||||
Helper functions around Function call/apply/bind, for use in `call-bind`.
|
|
||||||
|
|
||||||
The only packages that should likely ever use this package directly are `call-bind` and `get-intrinsic`.
|
|
||||||
Please use `call-bind` unless you have a very good reason not to.
|
|
||||||
|
|
||||||
## Getting started
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install --save call-bind-apply-helpers
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage/Examples
|
|
||||||
|
|
||||||
```js
|
|
||||||
const assert = require('assert');
|
|
||||||
const callBindBasic = require('call-bind-apply-helpers');
|
|
||||||
|
|
||||||
function f(a, b) {
|
|
||||||
assert.equal(this, 1);
|
|
||||||
assert.equal(a, 2);
|
|
||||||
assert.equal(b, 3);
|
|
||||||
assert.equal(arguments.length, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fBound = callBindBasic([f, 1]);
|
|
||||||
|
|
||||||
delete Function.prototype.call;
|
|
||||||
delete Function.prototype.bind;
|
|
||||||
|
|
||||||
fBound(2, 3);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Clone the repo, `npm install`, and run `npm test`
|
|
||||||
|
|
||||||
[package-url]: https://npmjs.org/package/call-bind-apply-helpers
|
|
||||||
[npm-version-svg]: https://versionbadg.es/ljharb/call-bind-apply-helpers.svg
|
|
||||||
[deps-svg]: https://david-dm.org/ljharb/call-bind-apply-helpers.svg
|
|
||||||
[deps-url]: https://david-dm.org/ljharb/call-bind-apply-helpers
|
|
||||||
[dev-deps-svg]: https://david-dm.org/ljharb/call-bind-apply-helpers/dev-status.svg
|
|
||||||
[dev-deps-url]: https://david-dm.org/ljharb/call-bind-apply-helpers#info=devDependencies
|
|
||||||
[npm-badge-png]: https://nodei.co/npm/call-bind-apply-helpers.png?downloads=true&stars=true
|
|
||||||
[license-image]: https://img.shields.io/npm/l/call-bind-apply-helpers.svg
|
|
||||||
[license-url]: LICENSE
|
|
||||||
[downloads-image]: https://img.shields.io/npm/dm/call-bind-apply-helpers.svg
|
|
||||||
[downloads-url]: https://npm-stat.com/charts.html?package=call-bind-apply-helpers
|
|
||||||
[codecov-image]: https://codecov.io/gh/ljharb/call-bind-apply-helpers/branch/main/graphs/badge.svg
|
|
||||||
[codecov-url]: https://app.codecov.io/gh/ljharb/call-bind-apply-helpers/
|
|
||||||
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/call-bind-apply-helpers
|
|
||||||
[actions-url]: https://github.com/ljharb/call-bind-apply-helpers/actions
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export = Reflect.apply;
|
|
||||||
-10
@@ -1,10 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var bind = require('function-bind');
|
|
||||||
|
|
||||||
var $apply = require('./functionApply');
|
|
||||||
var $call = require('./functionCall');
|
|
||||||
var $reflectApply = require('./reflectApply');
|
|
||||||
|
|
||||||
/** @type {import('./actualApply')} */
|
|
||||||
module.exports = $reflectApply || bind.call($call, $apply);
|
|
||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
import actualApply from './actualApply';
|
|
||||||
|
|
||||||
type TupleSplitHead<T extends any[], N extends number> = T['length'] extends N
|
|
||||||
? T
|
|
||||||
: T extends [...infer R, any]
|
|
||||||
? TupleSplitHead<R, N>
|
|
||||||
: never
|
|
||||||
|
|
||||||
type TupleSplitTail<T, N extends number, O extends any[] = []> = O['length'] extends N
|
|
||||||
? T
|
|
||||||
: T extends [infer F, ...infer R]
|
|
||||||
? TupleSplitTail<[...R], N, [...O, F]>
|
|
||||||
: never
|
|
||||||
|
|
||||||
type TupleSplit<T extends any[], N extends number> = [TupleSplitHead<T, N>, TupleSplitTail<T, N>]
|
|
||||||
|
|
||||||
declare function applyBind(...args: TupleSplit<Parameters<typeof actualApply>, 2>[1]): ReturnType<typeof actualApply>;
|
|
||||||
|
|
||||||
export = applyBind;
|
|
||||||
-10
@@ -1,10 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var bind = require('function-bind');
|
|
||||||
var $apply = require('./functionApply');
|
|
||||||
var actualApply = require('./actualApply');
|
|
||||||
|
|
||||||
/** @type {import('./applyBind')} */
|
|
||||||
module.exports = function applyBind() {
|
|
||||||
return actualApply(bind, $apply, arguments);
|
|
||||||
};
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export = Function.prototype.apply;
|
|
||||||
-4
@@ -1,4 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
/** @type {import('./functionApply')} */
|
|
||||||
module.exports = Function.prototype.apply;
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export = Function.prototype.call;
|
|
||||||
-4
@@ -1,4 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
/** @type {import('./functionCall')} */
|
|
||||||
module.exports = Function.prototype.call;
|
|
||||||
-64
@@ -1,64 +0,0 @@
|
|||||||
type RemoveFromTuple<
|
|
||||||
Tuple extends readonly unknown[],
|
|
||||||
RemoveCount extends number,
|
|
||||||
Index extends 1[] = []
|
|
||||||
> = Index["length"] extends RemoveCount
|
|
||||||
? Tuple
|
|
||||||
: Tuple extends [infer First, ...infer Rest]
|
|
||||||
? RemoveFromTuple<Rest, RemoveCount, [...Index, 1]>
|
|
||||||
: Tuple;
|
|
||||||
|
|
||||||
type ConcatTuples<
|
|
||||||
Prefix extends readonly unknown[],
|
|
||||||
Suffix extends readonly unknown[]
|
|
||||||
> = [...Prefix, ...Suffix];
|
|
||||||
|
|
||||||
type ExtractFunctionParams<T> = T extends (this: infer TThis, ...args: infer P extends readonly unknown[]) => infer R
|
|
||||||
? { thisArg: TThis; params: P; returnType: R }
|
|
||||||
: never;
|
|
||||||
|
|
||||||
type BindFunction<
|
|
||||||
T extends (this: any, ...args: any[]) => any,
|
|
||||||
TThis,
|
|
||||||
TBoundArgs extends readonly unknown[],
|
|
||||||
ReceiverBound extends boolean
|
|
||||||
> = ExtractFunctionParams<T> extends {
|
|
||||||
thisArg: infer OrigThis;
|
|
||||||
params: infer P extends readonly unknown[];
|
|
||||||
returnType: infer R;
|
|
||||||
}
|
|
||||||
? ReceiverBound extends true
|
|
||||||
? (...args: RemoveFromTuple<P, Extract<TBoundArgs["length"], number>>) => R extends [OrigThis, ...infer Rest]
|
|
||||||
? [TThis, ...Rest] // Replace `this` with `thisArg`
|
|
||||||
: R
|
|
||||||
: <U, RemainingArgs extends RemoveFromTuple<P, Extract<TBoundArgs["length"], number>>>(
|
|
||||||
thisArg: U,
|
|
||||||
...args: RemainingArgs
|
|
||||||
) => R extends [OrigThis, ...infer Rest]
|
|
||||||
? [U, ...ConcatTuples<TBoundArgs, Rest>] // Preserve bound args in return type
|
|
||||||
: R
|
|
||||||
: never;
|
|
||||||
|
|
||||||
declare function callBind<
|
|
||||||
const T extends (this: any, ...args: any[]) => any,
|
|
||||||
Extracted extends ExtractFunctionParams<T>,
|
|
||||||
const TBoundArgs extends Partial<Extracted["params"]> & readonly unknown[],
|
|
||||||
const TThis extends Extracted["thisArg"]
|
|
||||||
>(
|
|
||||||
args: [fn: T, thisArg: TThis, ...boundArgs: TBoundArgs]
|
|
||||||
): BindFunction<T, TThis, TBoundArgs, true>;
|
|
||||||
|
|
||||||
declare function callBind<
|
|
||||||
const T extends (this: any, ...args: any[]) => any,
|
|
||||||
Extracted extends ExtractFunctionParams<T>,
|
|
||||||
const TBoundArgs extends Partial<Extracted["params"]> & readonly unknown[]
|
|
||||||
>(
|
|
||||||
args: [fn: T, ...boundArgs: TBoundArgs]
|
|
||||||
): BindFunction<T, Extracted["thisArg"], TBoundArgs, false>;
|
|
||||||
|
|
||||||
declare function callBind<const TArgs extends readonly unknown[]>(
|
|
||||||
args: [fn: Exclude<TArgs[0], Function>, ...rest: TArgs]
|
|
||||||
): never;
|
|
||||||
|
|
||||||
// export as namespace callBind;
|
|
||||||
export = callBind;
|
|
||||||
-15
@@ -1,15 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var bind = require('function-bind');
|
|
||||||
var $TypeError = require('es-errors/type');
|
|
||||||
|
|
||||||
var $call = require('./functionCall');
|
|
||||||
var $actualApply = require('./actualApply');
|
|
||||||
|
|
||||||
/** @type {(args: [Function, thisArg?: unknown, ...args: unknown[]]) => Function} TODO FIXME, find a way to use import('.') */
|
|
||||||
module.exports = function callBindBasic(args) {
|
|
||||||
if (args.length < 1 || typeof args[0] !== 'function') {
|
|
||||||
throw new $TypeError('a function is required');
|
|
||||||
}
|
|
||||||
return $actualApply(bind, $call, args);
|
|
||||||
};
|
|
||||||
-85
@@ -1,85 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "call-bind-apply-helpers",
|
|
||||||
"version": "1.0.2",
|
|
||||||
"description": "Helper functions around Function call/apply/bind, for use in `call-bind`",
|
|
||||||
"main": "index.js",
|
|
||||||
"exports": {
|
|
||||||
".": "./index.js",
|
|
||||||
"./actualApply": "./actualApply.js",
|
|
||||||
"./applyBind": "./applyBind.js",
|
|
||||||
"./functionApply": "./functionApply.js",
|
|
||||||
"./functionCall": "./functionCall.js",
|
|
||||||
"./reflectApply": "./reflectApply.js",
|
|
||||||
"./package.json": "./package.json"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"prepack": "npmignore --auto --commentLines=auto",
|
|
||||||
"prepublish": "not-in-publish || npm run prepublishOnly",
|
|
||||||
"prepublishOnly": "safe-publish-latest",
|
|
||||||
"prelint": "evalmd README.md",
|
|
||||||
"lint": "eslint --ext=.js,.mjs .",
|
|
||||||
"postlint": "tsc -p . && attw -P",
|
|
||||||
"pretest": "npm run lint",
|
|
||||||
"tests-only": "nyc tape 'test/**/*.js'",
|
|
||||||
"test": "npm run tests-only",
|
|
||||||
"posttest": "npx npm@'>=10.2' audit --production",
|
|
||||||
"version": "auto-changelog && git add CHANGELOG.md",
|
|
||||||
"postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/ljharb/call-bind-apply-helpers.git"
|
|
||||||
},
|
|
||||||
"author": "Jordan Harband <ljharb@gmail.com>",
|
|
||||||
"license": "MIT",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/ljharb/call-bind-apply-helpers/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/ljharb/call-bind-apply-helpers#readme",
|
|
||||||
"dependencies": {
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"function-bind": "^1.1.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@arethetypeswrong/cli": "^0.17.3",
|
|
||||||
"@ljharb/eslint-config": "^21.1.1",
|
|
||||||
"@ljharb/tsconfig": "^0.2.3",
|
|
||||||
"@types/for-each": "^0.3.3",
|
|
||||||
"@types/function-bind": "^1.1.10",
|
|
||||||
"@types/object-inspect": "^1.13.0",
|
|
||||||
"@types/tape": "^5.8.1",
|
|
||||||
"auto-changelog": "^2.5.0",
|
|
||||||
"encoding": "^0.1.13",
|
|
||||||
"es-value-fixtures": "^1.7.1",
|
|
||||||
"eslint": "=8.8.0",
|
|
||||||
"evalmd": "^0.0.19",
|
|
||||||
"for-each": "^0.3.5",
|
|
||||||
"has-strict-mode": "^1.1.0",
|
|
||||||
"in-publish": "^2.0.1",
|
|
||||||
"npmignore": "^0.3.1",
|
|
||||||
"nyc": "^10.3.2",
|
|
||||||
"object-inspect": "^1.13.4",
|
|
||||||
"safe-publish-latest": "^2.0.0",
|
|
||||||
"tape": "^5.9.0",
|
|
||||||
"typescript": "next"
|
|
||||||
},
|
|
||||||
"testling": {
|
|
||||||
"files": "test/index.js"
|
|
||||||
},
|
|
||||||
"auto-changelog": {
|
|
||||||
"output": "CHANGELOG.md",
|
|
||||||
"template": "keepachangelog",
|
|
||||||
"unreleased": false,
|
|
||||||
"commitLimit": false,
|
|
||||||
"backfillLimit": false,
|
|
||||||
"hideCredit": true
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
|
||||||
"ignore": [
|
|
||||||
".github/workflows"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
declare const reflectApply: false | typeof Reflect.apply;
|
|
||||||
|
|
||||||
export = reflectApply;
|
|
||||||
-4
@@ -1,4 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
/** @type {import('./reflectApply')} */
|
|
||||||
module.exports = typeof Reflect !== 'undefined' && Reflect && Reflect.apply;
|
|
||||||
-63
@@ -1,63 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var callBind = require('../');
|
|
||||||
var hasStrictMode = require('has-strict-mode')();
|
|
||||||
var forEach = require('for-each');
|
|
||||||
var inspect = require('object-inspect');
|
|
||||||
var v = require('es-value-fixtures');
|
|
||||||
|
|
||||||
var test = require('tape');
|
|
||||||
|
|
||||||
test('callBindBasic', function (t) {
|
|
||||||
forEach(v.nonFunctions, function (nonFunction) {
|
|
||||||
t['throws'](
|
|
||||||
// @ts-expect-error
|
|
||||||
function () { callBind([nonFunction]); },
|
|
||||||
TypeError,
|
|
||||||
inspect(nonFunction) + ' is not a function'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
var sentinel = { sentinel: true };
|
|
||||||
/** @type {<T, A extends number, B extends number>(this: T, a: A, b: B) => [T | undefined, A, B]} */
|
|
||||||
var func = function (a, b) {
|
|
||||||
// eslint-disable-next-line no-invalid-this
|
|
||||||
return [!hasStrictMode && this === global ? undefined : this, a, b];
|
|
||||||
};
|
|
||||||
t.equal(func.length, 2, 'original function length is 2');
|
|
||||||
|
|
||||||
/** type {(thisArg: unknown, a: number, b: number) => [unknown, number, number]} */
|
|
||||||
var bound = callBind([func]);
|
|
||||||
/** type {((a: number, b: number) => [typeof sentinel, typeof a, typeof b])} */
|
|
||||||
var boundR = callBind([func, sentinel]);
|
|
||||||
/** type {((b: number) => [typeof sentinel, number, typeof b])} */
|
|
||||||
var boundArg = callBind([func, sentinel, /** @type {const} */ (1)]);
|
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
t.deepEqual(bound(), [undefined, undefined, undefined], 'bound func with no args');
|
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
t.deepEqual(func(), [undefined, undefined, undefined], 'unbound func with too few args');
|
|
||||||
// @ts-expect-error
|
|
||||||
t.deepEqual(bound(1, 2), [hasStrictMode ? 1 : Object(1), 2, undefined], 'bound func too few args');
|
|
||||||
// @ts-expect-error
|
|
||||||
t.deepEqual(boundR(), [sentinel, undefined, undefined], 'bound func with receiver, with too few args');
|
|
||||||
// @ts-expect-error
|
|
||||||
t.deepEqual(boundArg(), [sentinel, 1, undefined], 'bound func with receiver and arg, with too few args');
|
|
||||||
|
|
||||||
t.deepEqual(func(1, 2), [undefined, 1, 2], 'unbound func with right args');
|
|
||||||
t.deepEqual(bound(1, 2, 3), [hasStrictMode ? 1 : Object(1), 2, 3], 'bound func with right args');
|
|
||||||
t.deepEqual(boundR(1, 2), [sentinel, 1, 2], 'bound func with receiver, with right args');
|
|
||||||
t.deepEqual(boundArg(2), [sentinel, 1, 2], 'bound func with receiver and arg, with right arg');
|
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
t.deepEqual(func(1, 2, 3), [undefined, 1, 2], 'unbound func with too many args');
|
|
||||||
// @ts-expect-error
|
|
||||||
t.deepEqual(bound(1, 2, 3, 4), [hasStrictMode ? 1 : Object(1), 2, 3], 'bound func with too many args');
|
|
||||||
// @ts-expect-error
|
|
||||||
t.deepEqual(boundR(1, 2, 3), [sentinel, 1, 2], 'bound func with receiver, with too many args');
|
|
||||||
// @ts-expect-error
|
|
||||||
t.deepEqual(boundArg(2, 3), [sentinel, 1, 2], 'bound func with receiver and arg, with too many args');
|
|
||||||
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
-9
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "@ljharb/tsconfig",
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es2021",
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"coverage",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
BIN
Binary file not shown.
-13
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"root": true,
|
|
||||||
|
|
||||||
"extends": "@ljharb",
|
|
||||||
|
|
||||||
"rules": {
|
|
||||||
"new-cap": [2, {
|
|
||||||
"capIsNewExceptions": [
|
|
||||||
"GetIntrinsic",
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: [ljharb]
|
|
||||||
patreon: # Replace with a single Patreon username
|
|
||||||
open_collective: # Replace with a single Open Collective username
|
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
|
||||||
tidelift: npm/call-bound
|
|
||||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
||||||
liberapay: # Replace with a single Liberapay username
|
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
|
||||||
otechie: # Replace with a single Otechie username
|
|
||||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
||||||
-9
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"all": true,
|
|
||||||
"check-coverage": false,
|
|
||||||
"reporter": ["text-summary", "text", "html", "json"],
|
|
||||||
"exclude": [
|
|
||||||
"coverage",
|
|
||||||
"test"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
-42
@@ -1,42 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [v1.0.4](https://github.com/ljharb/call-bound/compare/v1.0.3...v1.0.4) - 2025-03-03
|
|
||||||
|
|
||||||
### Commits
|
|
||||||
|
|
||||||
- [types] improve types [`e648922`](https://github.com/ljharb/call-bound/commit/e6489222a9e54f350fbf952ceabe51fd8b6027ff)
|
|
||||||
- [Dev Deps] update `@arethetypeswrong/cli`, `@ljharb/tsconfig`, `@types/tape`, `es-value-fixtures`, `for-each`, `has-strict-mode`, `object-inspect` [`a42a5eb`](https://github.com/ljharb/call-bound/commit/a42a5ebe6c1b54fcdc7997c7dc64fdca9e936719)
|
|
||||||
- [Deps] update `call-bind-apply-helpers`, `get-intrinsic` [`f529eac`](https://github.com/ljharb/call-bound/commit/f529eac132404c17156bbc23ab2297a25d0f20b8)
|
|
||||||
|
|
||||||
## [v1.0.3](https://github.com/ljharb/call-bound/compare/v1.0.2...v1.0.3) - 2024-12-15
|
|
||||||
|
|
||||||
### Commits
|
|
||||||
|
|
||||||
- [Refactor] use `call-bind-apply-helpers` instead of `call-bind` [`5e0b134`](https://github.com/ljharb/call-bound/commit/5e0b13496df14fb7d05dae9412f088da8d3f75be)
|
|
||||||
- [Deps] update `get-intrinsic` [`41fc967`](https://github.com/ljharb/call-bound/commit/41fc96732a22c7b7e8f381f93ccc54bb6293be2e)
|
|
||||||
- [readme] fix example [`79a0137`](https://github.com/ljharb/call-bound/commit/79a0137723f7c6d09c9c05452bbf8d5efb5d6e49)
|
|
||||||
- [meta] add `sideEffects` flag [`08b07be`](https://github.com/ljharb/call-bound/commit/08b07be7f1c03f67dc6f3cdaf0906259771859f7)
|
|
||||||
|
|
||||||
## [v1.0.2](https://github.com/ljharb/call-bound/compare/v1.0.1...v1.0.2) - 2024-12-10
|
|
||||||
|
|
||||||
### Commits
|
|
||||||
|
|
||||||
- [Dev Deps] update `@arethetypeswrong/cli`, `@ljharb/tsconfig`, `gopd` [`e6a5ffe`](https://github.com/ljharb/call-bound/commit/e6a5ffe849368fe4f74dfd6cdeca1b9baa39e8d5)
|
|
||||||
- [Deps] update `call-bind`, `get-intrinsic` [`2aeb5b5`](https://github.com/ljharb/call-bound/commit/2aeb5b521dc2b2683d1345c753ea1161de2d1c14)
|
|
||||||
- [types] improve return type [`1a0c9fe`](https://github.com/ljharb/call-bound/commit/1a0c9fe3114471e7ca1f57d104e2efe713bb4871)
|
|
||||||
|
|
||||||
## v1.0.1 - 2024-12-05
|
|
||||||
|
|
||||||
### Commits
|
|
||||||
|
|
||||||
- Initial implementation, tests, readme, types [`6d94121`](https://github.com/ljharb/call-bound/commit/6d94121a9243602e506334069f7a03189fe3363d)
|
|
||||||
- Initial commit [`0eae867`](https://github.com/ljharb/call-bound/commit/0eae867334ea025c33e6e91cdecfc9df96680cf9)
|
|
||||||
- npm init [`71b2479`](https://github.com/ljharb/call-bound/commit/71b2479c6723e0b7d91a6b663613067e98b7b275)
|
|
||||||
- Only apps should have lockfiles [`c3754a9`](https://github.com/ljharb/call-bound/commit/c3754a949b7f9132b47e2d18c1729889736741eb)
|
|
||||||
- [actions] skip `npm ls` in node < 10 [`74275a5`](https://github.com/ljharb/call-bound/commit/74275a5186b8caf6309b6b97472bdcb0df4683a8)
|
|
||||||
- [Dev Deps] add missing peer dep [`1354de8`](https://github.com/ljharb/call-bound/commit/1354de8679413e4ae9c523d85f76fa7a5e032d97)
|
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2024 Jordan Harband
|
|
||||||
|
|
||||||
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.
|
|
||||||
-53
@@ -1,53 +0,0 @@
|
|||||||
# call-bound <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
|
|
||||||
|
|
||||||
[![github actions][actions-image]][actions-url]
|
|
||||||
[![coverage][codecov-image]][codecov-url]
|
|
||||||
[![dependency status][deps-svg]][deps-url]
|
|
||||||
[![dev dependency status][dev-deps-svg]][dev-deps-url]
|
|
||||||
[![License][license-image]][license-url]
|
|
||||||
[![Downloads][downloads-image]][downloads-url]
|
|
||||||
|
|
||||||
[![npm badge][npm-badge-png]][package-url]
|
|
||||||
|
|
||||||
Robust call-bound JavaScript intrinsics, using `call-bind` and `get-intrinsic`.
|
|
||||||
|
|
||||||
## Getting started
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install --save call-bound
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage/Examples
|
|
||||||
|
|
||||||
```js
|
|
||||||
const assert = require('assert');
|
|
||||||
const callBound = require('call-bound');
|
|
||||||
|
|
||||||
const slice = callBound('Array.prototype.slice');
|
|
||||||
|
|
||||||
delete Function.prototype.call;
|
|
||||||
delete Function.prototype.bind;
|
|
||||||
delete Array.prototype.slice;
|
|
||||||
|
|
||||||
assert.deepEqual(slice([1, 2, 3, 4], 1, -1), [2, 3]);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Clone the repo, `npm install`, and run `npm test`
|
|
||||||
|
|
||||||
[package-url]: https://npmjs.org/package/call-bound
|
|
||||||
[npm-version-svg]: https://versionbadg.es/ljharb/call-bound.svg
|
|
||||||
[deps-svg]: https://david-dm.org/ljharb/call-bound.svg
|
|
||||||
[deps-url]: https://david-dm.org/ljharb/call-bound
|
|
||||||
[dev-deps-svg]: https://david-dm.org/ljharb/call-bound/dev-status.svg
|
|
||||||
[dev-deps-url]: https://david-dm.org/ljharb/call-bound#info=devDependencies
|
|
||||||
[npm-badge-png]: https://nodei.co/npm/call-bound.png?downloads=true&stars=true
|
|
||||||
[license-image]: https://img.shields.io/npm/l/call-bound.svg
|
|
||||||
[license-url]: LICENSE
|
|
||||||
[downloads-image]: https://img.shields.io/npm/dm/call-bound.svg
|
|
||||||
[downloads-url]: https://npm-stat.com/charts.html?package=call-bound
|
|
||||||
[codecov-image]: https://codecov.io/gh/ljharb/call-bound/branch/main/graphs/badge.svg
|
|
||||||
[codecov-url]: https://app.codecov.io/gh/ljharb/call-bound/
|
|
||||||
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/call-bound
|
|
||||||
[actions-url]: https://github.com/ljharb/call-bound/actions
|
|
||||||
-94
@@ -1,94 +0,0 @@
|
|||||||
type Intrinsic = typeof globalThis;
|
|
||||||
|
|
||||||
type IntrinsicName = keyof Intrinsic | `%${keyof Intrinsic}%`;
|
|
||||||
|
|
||||||
type IntrinsicPath = IntrinsicName | `${StripPercents<IntrinsicName>}.${string}` | `%${StripPercents<IntrinsicName>}.${string}%`;
|
|
||||||
|
|
||||||
type AllowMissing = boolean;
|
|
||||||
|
|
||||||
type StripPercents<T extends string> = T extends `%${infer U}%` ? U : T;
|
|
||||||
|
|
||||||
type BindMethodPrecise<F> =
|
|
||||||
F extends (this: infer This, ...args: infer Args) => infer R
|
|
||||||
? (obj: This, ...args: Args) => R
|
|
||||||
: F extends {
|
|
||||||
(this: infer This1, ...args: infer Args1): infer R1;
|
|
||||||
(this: infer This2, ...args: infer Args2): infer R2
|
|
||||||
}
|
|
||||||
? {
|
|
||||||
(obj: This1, ...args: Args1): R1;
|
|
||||||
(obj: This2, ...args: Args2): R2
|
|
||||||
}
|
|
||||||
: never
|
|
||||||
|
|
||||||
// Extract method type from a prototype
|
|
||||||
type GetPrototypeMethod<T extends keyof typeof globalThis, M extends string> =
|
|
||||||
(typeof globalThis)[T] extends { prototype: any }
|
|
||||||
? M extends keyof (typeof globalThis)[T]['prototype']
|
|
||||||
? (typeof globalThis)[T]['prototype'][M]
|
|
||||||
: never
|
|
||||||
: never
|
|
||||||
|
|
||||||
// Get static property/method
|
|
||||||
type GetStaticMember<T extends keyof typeof globalThis, P extends string> =
|
|
||||||
P extends keyof (typeof globalThis)[T] ? (typeof globalThis)[T][P] : never
|
|
||||||
|
|
||||||
// Type that maps string path to actual bound function or value with better precision
|
|
||||||
type BoundIntrinsic<S extends string> =
|
|
||||||
S extends `${infer Obj}.prototype.${infer Method}`
|
|
||||||
? Obj extends keyof typeof globalThis
|
|
||||||
? BindMethodPrecise<GetPrototypeMethod<Obj, Method & string>>
|
|
||||||
: unknown
|
|
||||||
: S extends `${infer Obj}.${infer Prop}`
|
|
||||||
? Obj extends keyof typeof globalThis
|
|
||||||
? GetStaticMember<Obj, Prop & string>
|
|
||||||
: unknown
|
|
||||||
: unknown
|
|
||||||
|
|
||||||
declare function arraySlice<T>(array: readonly T[], start?: number, end?: number): T[];
|
|
||||||
declare function arraySlice<T>(array: ArrayLike<T>, start?: number, end?: number): T[];
|
|
||||||
declare function arraySlice<T>(array: IArguments, start?: number, end?: number): T[];
|
|
||||||
|
|
||||||
// Special cases for methods that need explicit typing
|
|
||||||
interface SpecialCases {
|
|
||||||
'%Object.prototype.isPrototypeOf%': (thisArg: {}, obj: unknown) => boolean;
|
|
||||||
'%String.prototype.replace%': {
|
|
||||||
(str: string, searchValue: string | RegExp, replaceValue: string): string;
|
|
||||||
(str: string, searchValue: string | RegExp, replacer: (substring: string, ...args: any[]) => string): string
|
|
||||||
};
|
|
||||||
'%Object.prototype.toString%': (obj: {}) => string;
|
|
||||||
'%Object.prototype.hasOwnProperty%': (obj: {}, v: PropertyKey) => boolean;
|
|
||||||
'%Array.prototype.slice%': typeof arraySlice;
|
|
||||||
'%Array.prototype.map%': <T, U>(array: readonly T[], callbackfn: (value: T, index: number, array: readonly T[]) => U, thisArg?: any) => U[];
|
|
||||||
'%Array.prototype.filter%': <T>(array: readonly T[], predicate: (value: T, index: number, array: readonly T[]) => unknown, thisArg?: any) => T[];
|
|
||||||
'%Array.prototype.indexOf%': <T>(array: readonly T[], searchElement: T, fromIndex?: number) => number;
|
|
||||||
'%Function.prototype.apply%': <T, A extends any[], R>(fn: (...args: A) => R, thisArg: any, args: A) => R;
|
|
||||||
'%Function.prototype.call%': <T, A extends any[], R>(fn: (...args: A) => R, thisArg: any, ...args: A) => R;
|
|
||||||
'%Function.prototype.bind%': <T, A extends any[], R>(fn: (...args: A) => R, thisArg: any, ...args: A) => (...remainingArgs: A) => R;
|
|
||||||
'%Promise.prototype.then%': {
|
|
||||||
<T, R>(promise: Promise<T>, onfulfilled: (value: T) => R | PromiseLike<R>): Promise<R>;
|
|
||||||
<T, R>(promise: Promise<T>, onfulfilled: ((value: T) => R | PromiseLike<R>) | undefined | null, onrejected: (reason: any) => R | PromiseLike<R>): Promise<R>;
|
|
||||||
};
|
|
||||||
'%RegExp.prototype.test%': (regexp: RegExp, str: string) => boolean;
|
|
||||||
'%RegExp.prototype.exec%': (regexp: RegExp, str: string) => RegExpExecArray | null;
|
|
||||||
'%Error.prototype.toString%': (error: Error) => string;
|
|
||||||
'%TypeError.prototype.toString%': (error: TypeError) => string;
|
|
||||||
'%String.prototype.split%': (
|
|
||||||
obj: unknown,
|
|
||||||
splitter: string | RegExp | {
|
|
||||||
[Symbol.split](string: string, limit?: number): string[];
|
|
||||||
},
|
|
||||||
limit?: number | undefined
|
|
||||||
) => string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a bound function for a prototype method, or a value for a static property.
|
|
||||||
*
|
|
||||||
* @param name - The name of the intrinsic (e.g. 'Array.prototype.slice')
|
|
||||||
* @param {AllowMissing} [allowMissing] - Whether to allow missing intrinsics (default: false)
|
|
||||||
*/
|
|
||||||
declare function callBound<K extends keyof SpecialCases | StripPercents<keyof SpecialCases>, S extends IntrinsicPath>(name: K, allowMissing?: AllowMissing): SpecialCases[`%${StripPercents<K>}%`];
|
|
||||||
declare function callBound<K extends keyof SpecialCases | StripPercents<keyof SpecialCases>, S extends IntrinsicPath>(name: S, allowMissing?: AllowMissing): BoundIntrinsic<S>;
|
|
||||||
|
|
||||||
export = callBound;
|
|
||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var GetIntrinsic = require('get-intrinsic');
|
|
||||||
|
|
||||||
var callBindBasic = require('call-bind-apply-helpers');
|
|
||||||
|
|
||||||
/** @type {(thisArg: string, searchString: string, position?: number) => number} */
|
|
||||||
var $indexOf = callBindBasic([GetIntrinsic('%String.prototype.indexOf%')]);
|
|
||||||
|
|
||||||
/** @type {import('.')} */
|
|
||||||
module.exports = function callBoundIntrinsic(name, allowMissing) {
|
|
||||||
/* eslint no-extra-parens: 0 */
|
|
||||||
|
|
||||||
var intrinsic = /** @type {(this: unknown, ...args: unknown[]) => unknown} */ (GetIntrinsic(name, !!allowMissing));
|
|
||||||
if (typeof intrinsic === 'function' && $indexOf(name, '.prototype.') > -1) {
|
|
||||||
return callBindBasic(/** @type {const} */ ([intrinsic]));
|
|
||||||
}
|
|
||||||
return intrinsic;
|
|
||||||
};
|
|
||||||
-99
@@ -1,99 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "call-bound",
|
|
||||||
"version": "1.0.4",
|
|
||||||
"description": "Robust call-bound JavaScript intrinsics, using `call-bind` and `get-intrinsic`.",
|
|
||||||
"main": "index.js",
|
|
||||||
"exports": {
|
|
||||||
".": "./index.js",
|
|
||||||
"./package.json": "./package.json"
|
|
||||||
},
|
|
||||||
"sideEffects": false,
|
|
||||||
"scripts": {
|
|
||||||
"prepack": "npmignore --auto --commentLines=auto",
|
|
||||||
"prepublish": "not-in-publish || npm run prepublishOnly",
|
|
||||||
"prepublishOnly": "safe-publish-latest",
|
|
||||||
"prelint": "evalmd README.md",
|
|
||||||
"lint": "eslint --ext=.js,.mjs .",
|
|
||||||
"postlint": "tsc -p . && attw -P",
|
|
||||||
"pretest": "npm run lint",
|
|
||||||
"tests-only": "nyc tape 'test/**/*.js'",
|
|
||||||
"test": "npm run tests-only",
|
|
||||||
"posttest": "npx npm@'>=10.2' audit --production",
|
|
||||||
"version": "auto-changelog && git add CHANGELOG.md",
|
|
||||||
"postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/ljharb/call-bound.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"javascript",
|
|
||||||
"ecmascript",
|
|
||||||
"es",
|
|
||||||
"js",
|
|
||||||
"callbind",
|
|
||||||
"callbound",
|
|
||||||
"call",
|
|
||||||
"bind",
|
|
||||||
"bound",
|
|
||||||
"call-bind",
|
|
||||||
"call-bound",
|
|
||||||
"function",
|
|
||||||
"es-abstract"
|
|
||||||
],
|
|
||||||
"author": "Jordan Harband <ljharb@gmail.com>",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/ljharb/call-bound/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/ljharb/call-bound#readme",
|
|
||||||
"dependencies": {
|
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
|
||||||
"get-intrinsic": "^1.3.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@arethetypeswrong/cli": "^0.17.4",
|
|
||||||
"@ljharb/eslint-config": "^21.1.1",
|
|
||||||
"@ljharb/tsconfig": "^0.3.0",
|
|
||||||
"@types/call-bind": "^1.0.5",
|
|
||||||
"@types/get-intrinsic": "^1.2.3",
|
|
||||||
"@types/tape": "^5.8.1",
|
|
||||||
"auto-changelog": "^2.5.0",
|
|
||||||
"encoding": "^0.1.13",
|
|
||||||
"es-value-fixtures": "^1.7.1",
|
|
||||||
"eslint": "=8.8.0",
|
|
||||||
"evalmd": "^0.0.19",
|
|
||||||
"for-each": "^0.3.5",
|
|
||||||
"gopd": "^1.2.0",
|
|
||||||
"has-strict-mode": "^1.1.0",
|
|
||||||
"in-publish": "^2.0.1",
|
|
||||||
"npmignore": "^0.3.1",
|
|
||||||
"nyc": "^10.3.2",
|
|
||||||
"object-inspect": "^1.13.4",
|
|
||||||
"safe-publish-latest": "^2.0.0",
|
|
||||||
"tape": "^5.9.0",
|
|
||||||
"typescript": "next"
|
|
||||||
},
|
|
||||||
"testling": {
|
|
||||||
"files": "test/index.js"
|
|
||||||
},
|
|
||||||
"auto-changelog": {
|
|
||||||
"output": "CHANGELOG.md",
|
|
||||||
"template": "keepachangelog",
|
|
||||||
"unreleased": false,
|
|
||||||
"commitLimit": false,
|
|
||||||
"backfillLimit": false,
|
|
||||||
"hideCredit": true
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
|
||||||
"ignore": [
|
|
||||||
".github/workflows"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-61
@@ -1,61 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var test = require('tape');
|
|
||||||
|
|
||||||
var callBound = require('../');
|
|
||||||
|
|
||||||
/** @template {true} T @template U @typedef {T extends U ? T : never} AssertType */
|
|
||||||
|
|
||||||
test('callBound', function (t) {
|
|
||||||
// static primitive
|
|
||||||
t.equal(callBound('Array.length'), Array.length, 'Array.length yields itself');
|
|
||||||
t.equal(callBound('%Array.length%'), Array.length, '%Array.length% yields itself');
|
|
||||||
|
|
||||||
// static non-function object
|
|
||||||
t.equal(callBound('Array.prototype'), Array.prototype, 'Array.prototype yields itself');
|
|
||||||
t.equal(callBound('%Array.prototype%'), Array.prototype, '%Array.prototype% yields itself');
|
|
||||||
t.equal(callBound('Array.constructor'), Array.constructor, 'Array.constructor yields itself');
|
|
||||||
t.equal(callBound('%Array.constructor%'), Array.constructor, '%Array.constructor% yields itself');
|
|
||||||
|
|
||||||
// static function
|
|
||||||
t.equal(callBound('Date.parse'), Date.parse, 'Date.parse yields itself');
|
|
||||||
t.equal(callBound('%Date.parse%'), Date.parse, '%Date.parse% yields itself');
|
|
||||||
|
|
||||||
// prototype primitive
|
|
||||||
t.equal(callBound('Error.prototype.message'), Error.prototype.message, 'Error.prototype.message yields itself');
|
|
||||||
t.equal(callBound('%Error.prototype.message%'), Error.prototype.message, '%Error.prototype.message% yields itself');
|
|
||||||
|
|
||||||
var x = callBound('Object.prototype.toString');
|
|
||||||
var y = callBound('%Object.prototype.toString%');
|
|
||||||
|
|
||||||
// prototype function
|
|
||||||
t.notEqual(x, Object.prototype.toString, 'Object.prototype.toString does not yield itself');
|
|
||||||
t.notEqual(y, Object.prototype.toString, '%Object.prototype.toString% does not yield itself');
|
|
||||||
t.equal(x(true), Object.prototype.toString.call(true), 'call-bound Object.prototype.toString calls into the original');
|
|
||||||
t.equal(y(true), Object.prototype.toString.call(true), 'call-bound %Object.prototype.toString% calls into the original');
|
|
||||||
|
|
||||||
t['throws'](
|
|
||||||
// @ts-expect-error
|
|
||||||
function () { callBound('does not exist'); },
|
|
||||||
SyntaxError,
|
|
||||||
'nonexistent intrinsic throws'
|
|
||||||
);
|
|
||||||
t['throws'](
|
|
||||||
// @ts-expect-error
|
|
||||||
function () { callBound('does not exist', true); },
|
|
||||||
SyntaxError,
|
|
||||||
'allowMissing arg still throws for unknown intrinsic'
|
|
||||||
);
|
|
||||||
|
|
||||||
t.test('real but absent intrinsic', { skip: typeof WeakRef !== 'undefined' }, function (st) {
|
|
||||||
st['throws'](
|
|
||||||
function () { callBound('WeakRef'); },
|
|
||||||
TypeError,
|
|
||||||
'real but absent intrinsic throws'
|
|
||||||
);
|
|
||||||
st.equal(callBound('WeakRef', true), undefined, 'allowMissing arg avoids exception');
|
|
||||||
st.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
-10
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "@ljharb/tsconfig",
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ESNext",
|
|
||||||
"lib": ["es2024"],
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"coverage",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
-60
@@ -1,60 +0,0 @@
|
|||||||
0.5.4 / 2021-12-10
|
|
||||||
==================
|
|
||||||
|
|
||||||
* deps: safe-buffer@5.2.1
|
|
||||||
|
|
||||||
0.5.3 / 2018-12-17
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Use `safe-buffer` for improved Buffer API
|
|
||||||
|
|
||||||
0.5.2 / 2016-12-08
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix `parse` to accept any linear whitespace character
|
|
||||||
|
|
||||||
0.5.1 / 2016-01-17
|
|
||||||
==================
|
|
||||||
|
|
||||||
* perf: enable strict mode
|
|
||||||
|
|
||||||
0.5.0 / 2014-10-11
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Add `parse` function
|
|
||||||
|
|
||||||
0.4.0 / 2014-09-21
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Expand non-Unicode `filename` to the full ISO-8859-1 charset
|
|
||||||
|
|
||||||
0.3.0 / 2014-09-20
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Add `fallback` option
|
|
||||||
* Add `type` option
|
|
||||||
|
|
||||||
0.2.0 / 2014-09-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Reduce ambiguity of file names with hex escape in buggy browsers
|
|
||||||
|
|
||||||
0.1.2 / 2014-09-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix periodic invalid Unicode filename header
|
|
||||||
|
|
||||||
0.1.1 / 2014-09-19
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix invalid characters appearing in `filename*` parameter
|
|
||||||
|
|
||||||
0.1.0 / 2014-09-18
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Make the `filename` argument optional
|
|
||||||
|
|
||||||
0.0.0 / 2014-09-18
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Initial release
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
(The MIT License)
|
|
||||||
|
|
||||||
Copyright (c) 2014-2017 Douglas Christopher Wilson
|
|
||||||
|
|
||||||
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.
|
|
||||||
-142
@@ -1,142 +0,0 @@
|
|||||||
# content-disposition
|
|
||||||
|
|
||||||
[![NPM Version][npm-image]][npm-url]
|
|
||||||
[![NPM Downloads][downloads-image]][downloads-url]
|
|
||||||
[![Node.js Version][node-version-image]][node-version-url]
|
|
||||||
[![Build Status][github-actions-ci-image]][github-actions-ci-url]
|
|
||||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
|
||||||
|
|
||||||
Create and parse HTTP `Content-Disposition` header
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ npm install content-disposition
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
```js
|
|
||||||
var contentDisposition = require('content-disposition')
|
|
||||||
```
|
|
||||||
|
|
||||||
### contentDisposition(filename, options)
|
|
||||||
|
|
||||||
Create an attachment `Content-Disposition` header value using the given file name,
|
|
||||||
if supplied. The `filename` is optional and if no file name is desired, but you
|
|
||||||
want to specify `options`, set `filename` to `undefined`.
|
|
||||||
|
|
||||||
```js
|
|
||||||
res.setHeader('Content-Disposition', contentDisposition('∫ maths.pdf'))
|
|
||||||
```
|
|
||||||
|
|
||||||
**note** HTTP headers are of the ISO-8859-1 character set. If you are writing this
|
|
||||||
header through a means different from `setHeader` in Node.js, you'll want to specify
|
|
||||||
the `'binary'` encoding in Node.js.
|
|
||||||
|
|
||||||
#### Options
|
|
||||||
|
|
||||||
`contentDisposition` accepts these properties in the options object.
|
|
||||||
|
|
||||||
##### fallback
|
|
||||||
|
|
||||||
If the `filename` option is outside ISO-8859-1, then the file name is actually
|
|
||||||
stored in a supplemental field for clients that support Unicode file names and
|
|
||||||
a ISO-8859-1 version of the file name is automatically generated.
|
|
||||||
|
|
||||||
This specifies the ISO-8859-1 file name to override the automatic generation or
|
|
||||||
disables the generation all together, defaults to `true`.
|
|
||||||
|
|
||||||
- A string will specify the ISO-8859-1 file name to use in place of automatic
|
|
||||||
generation.
|
|
||||||
- `false` will disable including a ISO-8859-1 file name and only include the
|
|
||||||
Unicode version (unless the file name is already ISO-8859-1).
|
|
||||||
- `true` will enable automatic generation if the file name is outside ISO-8859-1.
|
|
||||||
|
|
||||||
If the `filename` option is ISO-8859-1 and this option is specified and has a
|
|
||||||
different value, then the `filename` option is encoded in the extended field
|
|
||||||
and this set as the fallback field, even though they are both ISO-8859-1.
|
|
||||||
|
|
||||||
##### type
|
|
||||||
|
|
||||||
Specifies the disposition type, defaults to `"attachment"`. This can also be
|
|
||||||
`"inline"`, or any other value (all values except inline are treated like
|
|
||||||
`attachment`, but can convey additional information if both parties agree to
|
|
||||||
it). The type is normalized to lower-case.
|
|
||||||
|
|
||||||
### contentDisposition.parse(string)
|
|
||||||
|
|
||||||
```js
|
|
||||||
var disposition = contentDisposition.parse('attachment; filename="EURO rates.txt"; filename*=UTF-8\'\'%e2%82%ac%20rates.txt')
|
|
||||||
```
|
|
||||||
|
|
||||||
Parse a `Content-Disposition` header string. This automatically handles extended
|
|
||||||
("Unicode") parameters by decoding them and providing them under the standard
|
|
||||||
parameter name. This will return an object with the following properties (examples
|
|
||||||
are shown for the string `'attachment; filename="EURO rates.txt"; filename*=UTF-8\'\'%e2%82%ac%20rates.txt'`):
|
|
||||||
|
|
||||||
- `type`: The disposition type (always lower case). Example: `'attachment'`
|
|
||||||
|
|
||||||
- `parameters`: An object of the parameters in the disposition (name of parameter
|
|
||||||
always lower case and extended versions replace non-extended versions). Example:
|
|
||||||
`{filename: "€ rates.txt"}`
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Send a file for download
|
|
||||||
|
|
||||||
```js
|
|
||||||
var contentDisposition = require('content-disposition')
|
|
||||||
var destroy = require('destroy')
|
|
||||||
var fs = require('fs')
|
|
||||||
var http = require('http')
|
|
||||||
var onFinished = require('on-finished')
|
|
||||||
|
|
||||||
var filePath = '/path/to/public/plans.pdf'
|
|
||||||
|
|
||||||
http.createServer(function onRequest (req, res) {
|
|
||||||
// set headers
|
|
||||||
res.setHeader('Content-Type', 'application/pdf')
|
|
||||||
res.setHeader('Content-Disposition', contentDisposition(filePath))
|
|
||||||
|
|
||||||
// send file
|
|
||||||
var stream = fs.createReadStream(filePath)
|
|
||||||
stream.pipe(res)
|
|
||||||
onFinished(res, function () {
|
|
||||||
destroy(stream)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ npm test
|
|
||||||
```
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1][rfc-2616]
|
|
||||||
- [RFC 5987: Character Set and Language Encoding for Hypertext Transfer Protocol (HTTP) Header Field Parameters][rfc-5987]
|
|
||||||
- [RFC 6266: Use of the Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP)][rfc-6266]
|
|
||||||
- [Test Cases for HTTP Content-Disposition header field (RFC 6266) and the Encodings defined in RFCs 2047, 2231 and 5987][tc-2231]
|
|
||||||
|
|
||||||
[rfc-2616]: https://tools.ietf.org/html/rfc2616
|
|
||||||
[rfc-5987]: https://tools.ietf.org/html/rfc5987
|
|
||||||
[rfc-6266]: https://tools.ietf.org/html/rfc6266
|
|
||||||
[tc-2231]: http://greenbytes.de/tech/tc2231/
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[MIT](LICENSE)
|
|
||||||
|
|
||||||
[npm-image]: https://img.shields.io/npm/v/content-disposition.svg
|
|
||||||
[npm-url]: https://npmjs.org/package/content-disposition
|
|
||||||
[node-version-image]: https://img.shields.io/node/v/content-disposition.svg
|
|
||||||
[node-version-url]: https://nodejs.org/en/download
|
|
||||||
[coveralls-image]: https://img.shields.io/coveralls/jshttp/content-disposition.svg
|
|
||||||
[coveralls-url]: https://coveralls.io/r/jshttp/content-disposition?branch=master
|
|
||||||
[downloads-image]: https://img.shields.io/npm/dm/content-disposition.svg
|
|
||||||
[downloads-url]: https://npmjs.org/package/content-disposition
|
|
||||||
[github-actions-ci-image]: https://img.shields.io/github/workflow/status/jshttp/content-disposition/ci/master?label=ci
|
|
||||||
[github-actions-ci-url]: https://github.com/jshttp/content-disposition?query=workflow%3Aci
|
|
||||||
-458
@@ -1,458 +0,0 @@
|
|||||||
/*!
|
|
||||||
* content-disposition
|
|
||||||
* Copyright(c) 2014-2017 Douglas Christopher Wilson
|
|
||||||
* MIT Licensed
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module exports.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = contentDisposition
|
|
||||||
module.exports.parse = parse
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var basename = require('path').basename
|
|
||||||
var Buffer = require('safe-buffer').Buffer
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match non attr-char, *after* encodeURIComponent (i.e. not including "%")
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var ENCODE_URL_ATTR_CHAR_REGEXP = /[\x00-\x20"'()*,/:;<=>?@[\\\]{}\x7f]/g // eslint-disable-line no-control-regex
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match percent encoding escape.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var HEX_ESCAPE_REGEXP = /%[0-9A-Fa-f]{2}/
|
|
||||||
var HEX_ESCAPE_REPLACE_REGEXP = /%([0-9A-Fa-f]{2})/g
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match non-latin1 characters.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var NON_LATIN1_REGEXP = /[^\x20-\x7e\xa0-\xff]/g
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match quoted-pair in RFC 2616
|
|
||||||
*
|
|
||||||
* quoted-pair = "\" CHAR
|
|
||||||
* CHAR = <any US-ASCII character (octets 0 - 127)>
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var QESC_REGEXP = /\\([\u0000-\u007f])/g // eslint-disable-line no-control-regex
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match chars that must be quoted-pair in RFC 2616
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var QUOTE_REGEXP = /([\\"])/g
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp for various RFC 2616 grammar
|
|
||||||
*
|
|
||||||
* parameter = token "=" ( token | quoted-string )
|
|
||||||
* token = 1*<any CHAR except CTLs or separators>
|
|
||||||
* separators = "(" | ")" | "<" | ">" | "@"
|
|
||||||
* | "," | ";" | ":" | "\" | <">
|
|
||||||
* | "/" | "[" | "]" | "?" | "="
|
|
||||||
* | "{" | "}" | SP | HT
|
|
||||||
* quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
|
|
||||||
* qdtext = <any TEXT except <">>
|
|
||||||
* quoted-pair = "\" CHAR
|
|
||||||
* CHAR = <any US-ASCII character (octets 0 - 127)>
|
|
||||||
* TEXT = <any OCTET except CTLs, but including LWS>
|
|
||||||
* LWS = [CRLF] 1*( SP | HT )
|
|
||||||
* CRLF = CR LF
|
|
||||||
* CR = <US-ASCII CR, carriage return (13)>
|
|
||||||
* LF = <US-ASCII LF, linefeed (10)>
|
|
||||||
* SP = <US-ASCII SP, space (32)>
|
|
||||||
* HT = <US-ASCII HT, horizontal-tab (9)>
|
|
||||||
* CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
|
||||||
* OCTET = <any 8-bit sequence of data>
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var PARAM_REGEXP = /;[\x09\x20]*([!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\x09\x20]*=[\x09\x20]*("(?:[\x20!\x23-\x5b\x5d-\x7e\x80-\xff]|\\[\x20-\x7e])*"|[!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\x09\x20]*/g // eslint-disable-line no-control-regex
|
|
||||||
var TEXT_REGEXP = /^[\x20-\x7e\x80-\xff]+$/
|
|
||||||
var TOKEN_REGEXP = /^[!#$%&'*+.0-9A-Z^_`a-z|~-]+$/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp for various RFC 5987 grammar
|
|
||||||
*
|
|
||||||
* ext-value = charset "'" [ language ] "'" value-chars
|
|
||||||
* charset = "UTF-8" / "ISO-8859-1" / mime-charset
|
|
||||||
* mime-charset = 1*mime-charsetc
|
|
||||||
* mime-charsetc = ALPHA / DIGIT
|
|
||||||
* / "!" / "#" / "$" / "%" / "&"
|
|
||||||
* / "+" / "-" / "^" / "_" / "`"
|
|
||||||
* / "{" / "}" / "~"
|
|
||||||
* language = ( 2*3ALPHA [ extlang ] )
|
|
||||||
* / 4ALPHA
|
|
||||||
* / 5*8ALPHA
|
|
||||||
* extlang = *3( "-" 3ALPHA )
|
|
||||||
* value-chars = *( pct-encoded / attr-char )
|
|
||||||
* pct-encoded = "%" HEXDIG HEXDIG
|
|
||||||
* attr-char = ALPHA / DIGIT
|
|
||||||
* / "!" / "#" / "$" / "&" / "+" / "-" / "."
|
|
||||||
* / "^" / "_" / "`" / "|" / "~"
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var EXT_VALUE_REGEXP = /^([A-Za-z0-9!#$%&+\-^_`{}~]+)'(?:[A-Za-z]{2,3}(?:-[A-Za-z]{3}){0,3}|[A-Za-z]{4,8}|)'((?:%[0-9A-Fa-f]{2}|[A-Za-z0-9!#$&+.^_`|~-])+)$/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp for various RFC 6266 grammar
|
|
||||||
*
|
|
||||||
* disposition-type = "inline" | "attachment" | disp-ext-type
|
|
||||||
* disp-ext-type = token
|
|
||||||
* disposition-parm = filename-parm | disp-ext-parm
|
|
||||||
* filename-parm = "filename" "=" value
|
|
||||||
* | "filename*" "=" ext-value
|
|
||||||
* disp-ext-parm = token "=" value
|
|
||||||
* | ext-token "=" ext-value
|
|
||||||
* ext-token = <the characters in token, followed by "*">
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var DISPOSITION_TYPE_REGEXP = /^([!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\x09\x20]*(?:$|;)/ // eslint-disable-line no-control-regex
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an attachment Content-Disposition header.
|
|
||||||
*
|
|
||||||
* @param {string} [filename]
|
|
||||||
* @param {object} [options]
|
|
||||||
* @param {string} [options.type=attachment]
|
|
||||||
* @param {string|boolean} [options.fallback=true]
|
|
||||||
* @return {string}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function contentDisposition (filename, options) {
|
|
||||||
var opts = options || {}
|
|
||||||
|
|
||||||
// get type
|
|
||||||
var type = opts.type || 'attachment'
|
|
||||||
|
|
||||||
// get parameters
|
|
||||||
var params = createparams(filename, opts.fallback)
|
|
||||||
|
|
||||||
// format into string
|
|
||||||
return format(new ContentDisposition(type, params))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create parameters object from filename and fallback.
|
|
||||||
*
|
|
||||||
* @param {string} [filename]
|
|
||||||
* @param {string|boolean} [fallback=true]
|
|
||||||
* @return {object}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function createparams (filename, fallback) {
|
|
||||||
if (filename === undefined) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var params = {}
|
|
||||||
|
|
||||||
if (typeof filename !== 'string') {
|
|
||||||
throw new TypeError('filename must be a string')
|
|
||||||
}
|
|
||||||
|
|
||||||
// fallback defaults to true
|
|
||||||
if (fallback === undefined) {
|
|
||||||
fallback = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof fallback !== 'string' && typeof fallback !== 'boolean') {
|
|
||||||
throw new TypeError('fallback must be a string or boolean')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof fallback === 'string' && NON_LATIN1_REGEXP.test(fallback)) {
|
|
||||||
throw new TypeError('fallback must be ISO-8859-1 string')
|
|
||||||
}
|
|
||||||
|
|
||||||
// restrict to file base name
|
|
||||||
var name = basename(filename)
|
|
||||||
|
|
||||||
// determine if name is suitable for quoted string
|
|
||||||
var isQuotedString = TEXT_REGEXP.test(name)
|
|
||||||
|
|
||||||
// generate fallback name
|
|
||||||
var fallbackName = typeof fallback !== 'string'
|
|
||||||
? fallback && getlatin1(name)
|
|
||||||
: basename(fallback)
|
|
||||||
var hasFallback = typeof fallbackName === 'string' && fallbackName !== name
|
|
||||||
|
|
||||||
// set extended filename parameter
|
|
||||||
if (hasFallback || !isQuotedString || HEX_ESCAPE_REGEXP.test(name)) {
|
|
||||||
params['filename*'] = name
|
|
||||||
}
|
|
||||||
|
|
||||||
// set filename parameter
|
|
||||||
if (isQuotedString || hasFallback) {
|
|
||||||
params.filename = hasFallback
|
|
||||||
? fallbackName
|
|
||||||
: name
|
|
||||||
}
|
|
||||||
|
|
||||||
return params
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format object to Content-Disposition header.
|
|
||||||
*
|
|
||||||
* @param {object} obj
|
|
||||||
* @param {string} obj.type
|
|
||||||
* @param {object} [obj.parameters]
|
|
||||||
* @return {string}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function format (obj) {
|
|
||||||
var parameters = obj.parameters
|
|
||||||
var type = obj.type
|
|
||||||
|
|
||||||
if (!type || typeof type !== 'string' || !TOKEN_REGEXP.test(type)) {
|
|
||||||
throw new TypeError('invalid type')
|
|
||||||
}
|
|
||||||
|
|
||||||
// start with normalized type
|
|
||||||
var string = String(type).toLowerCase()
|
|
||||||
|
|
||||||
// append parameters
|
|
||||||
if (parameters && typeof parameters === 'object') {
|
|
||||||
var param
|
|
||||||
var params = Object.keys(parameters).sort()
|
|
||||||
|
|
||||||
for (var i = 0; i < params.length; i++) {
|
|
||||||
param = params[i]
|
|
||||||
|
|
||||||
var val = param.substr(-1) === '*'
|
|
||||||
? ustring(parameters[param])
|
|
||||||
: qstring(parameters[param])
|
|
||||||
|
|
||||||
string += '; ' + param + '=' + val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode a RFC 5987 field value (gracefully).
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* @return {string}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function decodefield (str) {
|
|
||||||
var match = EXT_VALUE_REGEXP.exec(str)
|
|
||||||
|
|
||||||
if (!match) {
|
|
||||||
throw new TypeError('invalid extended field value')
|
|
||||||
}
|
|
||||||
|
|
||||||
var charset = match[1].toLowerCase()
|
|
||||||
var encoded = match[2]
|
|
||||||
var value
|
|
||||||
|
|
||||||
// to binary string
|
|
||||||
var binary = encoded.replace(HEX_ESCAPE_REPLACE_REGEXP, pdecode)
|
|
||||||
|
|
||||||
switch (charset) {
|
|
||||||
case 'iso-8859-1':
|
|
||||||
value = getlatin1(binary)
|
|
||||||
break
|
|
||||||
case 'utf-8':
|
|
||||||
value = Buffer.from(binary, 'binary').toString('utf8')
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw new TypeError('unsupported charset in extended field')
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get ISO-8859-1 version of string.
|
|
||||||
*
|
|
||||||
* @param {string} val
|
|
||||||
* @return {string}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function getlatin1 (val) {
|
|
||||||
// simple Unicode -> ISO-8859-1 transformation
|
|
||||||
return String(val).replace(NON_LATIN1_REGEXP, '?')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse Content-Disposition header string.
|
|
||||||
*
|
|
||||||
* @param {string} string
|
|
||||||
* @return {object}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function parse (string) {
|
|
||||||
if (!string || typeof string !== 'string') {
|
|
||||||
throw new TypeError('argument string is required')
|
|
||||||
}
|
|
||||||
|
|
||||||
var match = DISPOSITION_TYPE_REGEXP.exec(string)
|
|
||||||
|
|
||||||
if (!match) {
|
|
||||||
throw new TypeError('invalid type format')
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize type
|
|
||||||
var index = match[0].length
|
|
||||||
var type = match[1].toLowerCase()
|
|
||||||
|
|
||||||
var key
|
|
||||||
var names = []
|
|
||||||
var params = {}
|
|
||||||
var value
|
|
||||||
|
|
||||||
// calculate index to start at
|
|
||||||
index = PARAM_REGEXP.lastIndex = match[0].substr(-1) === ';'
|
|
||||||
? index - 1
|
|
||||||
: index
|
|
||||||
|
|
||||||
// match parameters
|
|
||||||
while ((match = PARAM_REGEXP.exec(string))) {
|
|
||||||
if (match.index !== index) {
|
|
||||||
throw new TypeError('invalid parameter format')
|
|
||||||
}
|
|
||||||
|
|
||||||
index += match[0].length
|
|
||||||
key = match[1].toLowerCase()
|
|
||||||
value = match[2]
|
|
||||||
|
|
||||||
if (names.indexOf(key) !== -1) {
|
|
||||||
throw new TypeError('invalid duplicate parameter')
|
|
||||||
}
|
|
||||||
|
|
||||||
names.push(key)
|
|
||||||
|
|
||||||
if (key.indexOf('*') + 1 === key.length) {
|
|
||||||
// decode extended value
|
|
||||||
key = key.slice(0, -1)
|
|
||||||
value = decodefield(value)
|
|
||||||
|
|
||||||
// overwrite existing value
|
|
||||||
params[key] = value
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof params[key] === 'string') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value[0] === '"') {
|
|
||||||
// remove quotes and escapes
|
|
||||||
value = value
|
|
||||||
.substr(1, value.length - 2)
|
|
||||||
.replace(QESC_REGEXP, '$1')
|
|
||||||
}
|
|
||||||
|
|
||||||
params[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index !== -1 && index !== string.length) {
|
|
||||||
throw new TypeError('invalid parameter format')
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ContentDisposition(type, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Percent decode a single character.
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* @param {string} hex
|
|
||||||
* @return {string}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function pdecode (str, hex) {
|
|
||||||
return String.fromCharCode(parseInt(hex, 16))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Percent encode a single character.
|
|
||||||
*
|
|
||||||
* @param {string} char
|
|
||||||
* @return {string}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function pencode (char) {
|
|
||||||
return '%' + String(char)
|
|
||||||
.charCodeAt(0)
|
|
||||||
.toString(16)
|
|
||||||
.toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Quote a string for HTTP.
|
|
||||||
*
|
|
||||||
* @param {string} val
|
|
||||||
* @return {string}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function qstring (val) {
|
|
||||||
var str = String(val)
|
|
||||||
|
|
||||||
return '"' + str.replace(QUOTE_REGEXP, '\\$1') + '"'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a Unicode string for HTTP (RFC 5987).
|
|
||||||
*
|
|
||||||
* @param {string} val
|
|
||||||
* @return {string}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function ustring (val) {
|
|
||||||
var str = String(val)
|
|
||||||
|
|
||||||
// percent encode as UTF-8
|
|
||||||
var encoded = encodeURIComponent(str)
|
|
||||||
.replace(ENCODE_URL_ATTR_CHAR_REGEXP, pencode)
|
|
||||||
|
|
||||||
return 'UTF-8\'\'' + encoded
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class for parsed Content-Disposition header for v8 optimization
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
* @param {string} type
|
|
||||||
* @param {object} parameters
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
|
|
||||||
function ContentDisposition (type, parameters) {
|
|
||||||
this.type = type
|
|
||||||
this.parameters = parameters
|
|
||||||
}
|
|
||||||
-44
@@ -1,44 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "content-disposition",
|
|
||||||
"description": "Create and parse Content-Disposition header",
|
|
||||||
"version": "0.5.4",
|
|
||||||
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
|
|
||||||
"license": "MIT",
|
|
||||||
"keywords": [
|
|
||||||
"content-disposition",
|
|
||||||
"http",
|
|
||||||
"rfc6266",
|
|
||||||
"res"
|
|
||||||
],
|
|
||||||
"repository": "jshttp/content-disposition",
|
|
||||||
"dependencies": {
|
|
||||||
"safe-buffer": "5.2.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"deep-equal": "1.0.1",
|
|
||||||
"eslint": "7.32.0",
|
|
||||||
"eslint-config-standard": "13.0.1",
|
|
||||||
"eslint-plugin-import": "2.25.3",
|
|
||||||
"eslint-plugin-markdown": "2.2.1",
|
|
||||||
"eslint-plugin-node": "11.1.0",
|
|
||||||
"eslint-plugin-promise": "5.2.0",
|
|
||||||
"eslint-plugin-standard": "4.1.0",
|
|
||||||
"istanbul": "0.4.5",
|
|
||||||
"mocha": "9.1.3"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"LICENSE",
|
|
||||||
"HISTORY.md",
|
|
||||||
"README.md",
|
|
||||||
"index.js"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"lint": "eslint .",
|
|
||||||
"test": "mocha --reporter spec --bail --check-leaks 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/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-29
@@ -1,29 +0,0 @@
|
|||||||
1.0.5 / 2023-01-29
|
|
||||||
==================
|
|
||||||
|
|
||||||
* perf: skip value escaping when unnecessary
|
|
||||||
|
|
||||||
1.0.4 / 2017-09-11
|
|
||||||
==================
|
|
||||||
|
|
||||||
* perf: skip parameter parsing when no parameters
|
|
||||||
|
|
||||||
1.0.3 / 2017-09-10
|
|
||||||
==================
|
|
||||||
|
|
||||||
* perf: remove argument reassignment
|
|
||||||
|
|
||||||
1.0.2 / 2016-05-09
|
|
||||||
==================
|
|
||||||
|
|
||||||
* perf: enable strict mode
|
|
||||||
|
|
||||||
1.0.1 / 2015-02-13
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Improve missing `Content-Type` header error message
|
|
||||||
|
|
||||||
1.0.0 / 2015-02-01
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Initial implementation, derived from `media-typer@0.3.0`
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
(The MIT License)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Douglas Christopher Wilson
|
|
||||||
|
|
||||||
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.
|
|
||||||
-94
@@ -1,94 +0,0 @@
|
|||||||
# content-type
|
|
||||||
|
|
||||||
[![NPM Version][npm-version-image]][npm-url]
|
|
||||||
[![NPM Downloads][npm-downloads-image]][npm-url]
|
|
||||||
[![Node.js Version][node-image]][node-url]
|
|
||||||
[![Build Status][ci-image]][ci-url]
|
|
||||||
[![Coverage Status][coveralls-image]][coveralls-url]
|
|
||||||
|
|
||||||
Create and parse HTTP Content-Type header according to RFC 7231
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ npm install content-type
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
```js
|
|
||||||
var contentType = require('content-type')
|
|
||||||
```
|
|
||||||
|
|
||||||
### contentType.parse(string)
|
|
||||||
|
|
||||||
```js
|
|
||||||
var obj = contentType.parse('image/svg+xml; charset=utf-8')
|
|
||||||
```
|
|
||||||
|
|
||||||
Parse a `Content-Type` header. This will return an object with the following
|
|
||||||
properties (examples are shown for the string `'image/svg+xml; charset=utf-8'`):
|
|
||||||
|
|
||||||
- `type`: The media type (the type and subtype, always lower case).
|
|
||||||
Example: `'image/svg+xml'`
|
|
||||||
|
|
||||||
- `parameters`: An object of the parameters in the media type (name of parameter
|
|
||||||
always lower case). Example: `{charset: 'utf-8'}`
|
|
||||||
|
|
||||||
Throws a `TypeError` if the string is missing or invalid.
|
|
||||||
|
|
||||||
### contentType.parse(req)
|
|
||||||
|
|
||||||
```js
|
|
||||||
var obj = contentType.parse(req)
|
|
||||||
```
|
|
||||||
|
|
||||||
Parse the `Content-Type` header from the given `req`. Short-cut for
|
|
||||||
`contentType.parse(req.headers['content-type'])`.
|
|
||||||
|
|
||||||
Throws a `TypeError` if the `Content-Type` header is missing or invalid.
|
|
||||||
|
|
||||||
### contentType.parse(res)
|
|
||||||
|
|
||||||
```js
|
|
||||||
var obj = contentType.parse(res)
|
|
||||||
```
|
|
||||||
|
|
||||||
Parse the `Content-Type` header set on the given `res`. Short-cut for
|
|
||||||
`contentType.parse(res.getHeader('content-type'))`.
|
|
||||||
|
|
||||||
Throws a `TypeError` if the `Content-Type` header is missing or invalid.
|
|
||||||
|
|
||||||
### contentType.format(obj)
|
|
||||||
|
|
||||||
```js
|
|
||||||
var str = contentType.format({
|
|
||||||
type: 'image/svg+xml',
|
|
||||||
parameters: { charset: 'utf-8' }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Format an object into a `Content-Type` header. This will return a string of the
|
|
||||||
content type for the given object with the following properties (examples are
|
|
||||||
shown that produce the string `'image/svg+xml; charset=utf-8'`):
|
|
||||||
|
|
||||||
- `type`: The media type (will be lower-cased). Example: `'image/svg+xml'`
|
|
||||||
|
|
||||||
- `parameters`: An object of the parameters in the media type (name of the
|
|
||||||
parameter will be lower-cased). Example: `{charset: 'utf-8'}`
|
|
||||||
|
|
||||||
Throws a `TypeError` if the object contains an invalid type or parameter names.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[MIT](LICENSE)
|
|
||||||
|
|
||||||
[ci-image]: https://badgen.net/github/checks/jshttp/content-type/master?label=ci
|
|
||||||
[ci-url]: https://github.com/jshttp/content-type/actions/workflows/ci.yml
|
|
||||||
[coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/content-type/master
|
|
||||||
[coveralls-url]: https://coveralls.io/r/jshttp/content-type?branch=master
|
|
||||||
[node-image]: https://badgen.net/npm/node/content-type
|
|
||||||
[node-url]: https://nodejs.org/en/download
|
|
||||||
[npm-downloads-image]: https://badgen.net/npm/dm/content-type
|
|
||||||
[npm-url]: https://npmjs.org/package/content-type
|
|
||||||
[npm-version-image]: https://badgen.net/npm/v/content-type
|
|
||||||
-225
@@ -1,225 +0,0 @@
|
|||||||
/*!
|
|
||||||
* content-type
|
|
||||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
|
||||||
* MIT Licensed
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match *( ";" parameter ) in RFC 7231 sec 3.1.1.1
|
|
||||||
*
|
|
||||||
* parameter = token "=" ( token / quoted-string )
|
|
||||||
* token = 1*tchar
|
|
||||||
* tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
|
|
||||||
* / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
|
|
||||||
* / DIGIT / ALPHA
|
|
||||||
* ; any VCHAR, except delimiters
|
|
||||||
* quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
|
||||||
* qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
|
|
||||||
* obs-text = %x80-FF
|
|
||||||
* quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
|
||||||
*/
|
|
||||||
var PARAM_REGEXP = /; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g // eslint-disable-line no-control-regex
|
|
||||||
var TEXT_REGEXP = /^[\u000b\u0020-\u007e\u0080-\u00ff]+$/ // eslint-disable-line no-control-regex
|
|
||||||
var TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match quoted-pair in RFC 7230 sec 3.2.6
|
|
||||||
*
|
|
||||||
* quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
|
||||||
* obs-text = %x80-FF
|
|
||||||
*/
|
|
||||||
var QESC_REGEXP = /\\([\u000b\u0020-\u00ff])/g // eslint-disable-line no-control-regex
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match chars that must be quoted-pair in RFC 7230 sec 3.2.6
|
|
||||||
*/
|
|
||||||
var QUOTE_REGEXP = /([\\"])/g
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match type in RFC 7231 sec 3.1.1.1
|
|
||||||
*
|
|
||||||
* media-type = type "/" subtype
|
|
||||||
* type = token
|
|
||||||
* subtype = token
|
|
||||||
*/
|
|
||||||
var TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module exports.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
exports.format = format
|
|
||||||
exports.parse = parse
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format object to media type.
|
|
||||||
*
|
|
||||||
* @param {object} obj
|
|
||||||
* @return {string}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function format (obj) {
|
|
||||||
if (!obj || typeof obj !== 'object') {
|
|
||||||
throw new TypeError('argument obj is required')
|
|
||||||
}
|
|
||||||
|
|
||||||
var parameters = obj.parameters
|
|
||||||
var type = obj.type
|
|
||||||
|
|
||||||
if (!type || !TYPE_REGEXP.test(type)) {
|
|
||||||
throw new TypeError('invalid type')
|
|
||||||
}
|
|
||||||
|
|
||||||
var string = type
|
|
||||||
|
|
||||||
// append parameters
|
|
||||||
if (parameters && typeof parameters === 'object') {
|
|
||||||
var param
|
|
||||||
var params = Object.keys(parameters).sort()
|
|
||||||
|
|
||||||
for (var i = 0; i < params.length; i++) {
|
|
||||||
param = params[i]
|
|
||||||
|
|
||||||
if (!TOKEN_REGEXP.test(param)) {
|
|
||||||
throw new TypeError('invalid parameter name')
|
|
||||||
}
|
|
||||||
|
|
||||||
string += '; ' + param + '=' + qstring(parameters[param])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse media type to object.
|
|
||||||
*
|
|
||||||
* @param {string|object} string
|
|
||||||
* @return {Object}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function parse (string) {
|
|
||||||
if (!string) {
|
|
||||||
throw new TypeError('argument string is required')
|
|
||||||
}
|
|
||||||
|
|
||||||
// support req/res-like objects as argument
|
|
||||||
var header = typeof string === 'object'
|
|
||||||
? getcontenttype(string)
|
|
||||||
: string
|
|
||||||
|
|
||||||
if (typeof header !== 'string') {
|
|
||||||
throw new TypeError('argument string is required to be a string')
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = header.indexOf(';')
|
|
||||||
var type = index !== -1
|
|
||||||
? header.slice(0, index).trim()
|
|
||||||
: header.trim()
|
|
||||||
|
|
||||||
if (!TYPE_REGEXP.test(type)) {
|
|
||||||
throw new TypeError('invalid media type')
|
|
||||||
}
|
|
||||||
|
|
||||||
var obj = new ContentType(type.toLowerCase())
|
|
||||||
|
|
||||||
// parse parameters
|
|
||||||
if (index !== -1) {
|
|
||||||
var key
|
|
||||||
var match
|
|
||||||
var value
|
|
||||||
|
|
||||||
PARAM_REGEXP.lastIndex = index
|
|
||||||
|
|
||||||
while ((match = PARAM_REGEXP.exec(header))) {
|
|
||||||
if (match.index !== index) {
|
|
||||||
throw new TypeError('invalid parameter format')
|
|
||||||
}
|
|
||||||
|
|
||||||
index += match[0].length
|
|
||||||
key = match[1].toLowerCase()
|
|
||||||
value = match[2]
|
|
||||||
|
|
||||||
if (value.charCodeAt(0) === 0x22 /* " */) {
|
|
||||||
// remove quotes
|
|
||||||
value = value.slice(1, -1)
|
|
||||||
|
|
||||||
// remove escapes
|
|
||||||
if (value.indexOf('\\') !== -1) {
|
|
||||||
value = value.replace(QESC_REGEXP, '$1')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.parameters[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index !== header.length) {
|
|
||||||
throw new TypeError('invalid parameter format')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get content-type from req/res objects.
|
|
||||||
*
|
|
||||||
* @param {object}
|
|
||||||
* @return {Object}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function getcontenttype (obj) {
|
|
||||||
var header
|
|
||||||
|
|
||||||
if (typeof obj.getHeader === 'function') {
|
|
||||||
// res-like
|
|
||||||
header = obj.getHeader('content-type')
|
|
||||||
} else if (typeof obj.headers === 'object') {
|
|
||||||
// req-like
|
|
||||||
header = obj.headers && obj.headers['content-type']
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof header !== 'string') {
|
|
||||||
throw new TypeError('content-type header is missing from object')
|
|
||||||
}
|
|
||||||
|
|
||||||
return header
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Quote a string if necessary.
|
|
||||||
*
|
|
||||||
* @param {string} val
|
|
||||||
* @return {string}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function qstring (val) {
|
|
||||||
var str = String(val)
|
|
||||||
|
|
||||||
// no need to quote tokens
|
|
||||||
if (TOKEN_REGEXP.test(str)) {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str.length > 0 && !TEXT_REGEXP.test(str)) {
|
|
||||||
throw new TypeError('invalid parameter value')
|
|
||||||
}
|
|
||||||
|
|
||||||
return '"' + str.replace(QUOTE_REGEXP, '\\$1') + '"'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class to represent a content type.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
function ContentType (type) {
|
|
||||||
this.parameters = Object.create(null)
|
|
||||||
this.type = type
|
|
||||||
}
|
|
||||||
-42
@@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "content-type",
|
|
||||||
"description": "Create and parse HTTP Content-Type header",
|
|
||||||
"version": "1.0.5",
|
|
||||||
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
|
|
||||||
"license": "MIT",
|
|
||||||
"keywords": [
|
|
||||||
"content-type",
|
|
||||||
"http",
|
|
||||||
"req",
|
|
||||||
"res",
|
|
||||||
"rfc7231"
|
|
||||||
],
|
|
||||||
"repository": "jshttp/content-type",
|
|
||||||
"devDependencies": {
|
|
||||||
"deep-equal": "1.0.1",
|
|
||||||
"eslint": "8.32.0",
|
|
||||||
"eslint-config-standard": "15.0.1",
|
|
||||||
"eslint-plugin-import": "2.27.5",
|
|
||||||
"eslint-plugin-node": "11.1.0",
|
|
||||||
"eslint-plugin-promise": "6.1.1",
|
|
||||||
"eslint-plugin-standard": "4.1.0",
|
|
||||||
"mocha": "10.2.0",
|
|
||||||
"nyc": "15.1.0"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"LICENSE",
|
|
||||||
"HISTORY.md",
|
|
||||||
"README.md",
|
|
||||||
"index.js"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"lint": "eslint .",
|
|
||||||
"test": "mocha --reporter spec --check-leaks --bail test/",
|
|
||||||
"test-ci": "nyc --reporter=lcovonly --reporter=text npm test",
|
|
||||||
"test-cov": "nyc --reporter=html --reporter=text npm test",
|
|
||||||
"version": "node scripts/version-history.js && git add HISTORY.md"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-42
@@ -1,42 +0,0 @@
|
|||||||
1.0.7 / 2023-04-12
|
|
||||||
==================
|
|
||||||
|
|
||||||
* backport the buffer support from the 1.2.x release branch (thanks @FadhiliNjagi!)
|
|
||||||
|
|
||||||
1.0.6 / 2015-02-03
|
|
||||||
==================
|
|
||||||
|
|
||||||
* use `npm test` instead of `make test` to run tests
|
|
||||||
* clearer assertion messages when checking input
|
|
||||||
|
|
||||||
1.0.5 / 2014-09-05
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add license to package.json
|
|
||||||
|
|
||||||
1.0.4 / 2014-06-25
|
|
||||||
==================
|
|
||||||
|
|
||||||
* corrected avoidance of timing attacks (thanks @tenbits!)
|
|
||||||
|
|
||||||
1.0.3 / 2014-01-28
|
|
||||||
==================
|
|
||||||
|
|
||||||
* [incorrect] fix for timing attacks
|
|
||||||
|
|
||||||
1.0.2 / 2014-01-28
|
|
||||||
==================
|
|
||||||
|
|
||||||
* fix missing repository warning
|
|
||||||
* fix typo in test
|
|
||||||
|
|
||||||
1.0.1 / 2013-04-15
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Revert "Changed underlying HMAC algo. to sha512."
|
|
||||||
* Revert "Fix for timing attacks on MAC verification."
|
|
||||||
|
|
||||||
0.0.1 / 2010-01-03
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Initial release
|
|
||||||
-42
@@ -1,42 +0,0 @@
|
|||||||
|
|
||||||
# cookie-signature
|
|
||||||
|
|
||||||
Sign and unsign cookies.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
```js
|
|
||||||
var cookie = require('cookie-signature');
|
|
||||||
|
|
||||||
var val = cookie.sign('hello', 'tobiiscool');
|
|
||||||
val.should.equal('hello.DGDUkGlIkCzPz+C0B064FNgHdEjox7ch8tOBGslZ5QI');
|
|
||||||
|
|
||||||
var val = cookie.sign('hello', 'tobiiscool');
|
|
||||||
cookie.unsign(val, 'tobiiscool').should.equal('hello');
|
|
||||||
cookie.unsign(val, 'luna').should.be.false;
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
(The MIT License)
|
|
||||||
|
|
||||||
Copyright (c) 2012 LearnBoost <tj@learnboost.com>
|
|
||||||
|
|
||||||
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.
|
|
||||||
-51
@@ -1,51 +0,0 @@
|
|||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var crypto = require('crypto');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign the given `val` with `secret`.
|
|
||||||
*
|
|
||||||
* @param {String} val
|
|
||||||
* @param {String|NodeJS.ArrayBufferView|crypto.KeyObject} 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.");
|
|
||||||
return val + '.' + crypto
|
|
||||||
.createHmac('sha256', secret)
|
|
||||||
.update(val)
|
|
||||||
.digest('base64')
|
|
||||||
.replace(/\=+$/, '');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unsign and decode the given `val` with `secret`,
|
|
||||||
* returning `false` if the signature is invalid.
|
|
||||||
*
|
|
||||||
* @param {String} val
|
|
||||||
* @param {String|NodeJS.ArrayBufferView|crypto.KeyObject} secret
|
|
||||||
* @return {String|Boolean}
|
|
||||||
* @api private
|
|
||||||
*/
|
|
||||||
|
|
||||||
exports.unsign = function(val, secret){
|
|
||||||
if ('string' !== typeof val) throw new TypeError("Signed cookie string must be provided.");
|
|
||||||
if (null == secret) throw new TypeError("Secret key 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');
|
|
||||||
}
|
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "cookie-signature",
|
|
||||||
"version": "1.0.7",
|
|
||||||
"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"},
|
|
||||||
"dependencies": {},
|
|
||||||
"devDependencies": {
|
|
||||||
"mocha": "*",
|
|
||||||
"should": "*"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "mocha --require should --reporter spec"
|
|
||||||
},
|
|
||||||
"main": "index"
|
|
||||||
}
|
|
||||||
-24
@@ -1,24 +0,0 @@
|
|||||||
(The MIT License)
|
|
||||||
|
|
||||||
Copyright (c) 2012-2014 Roman Shtylman <shtylman@gmail.com>
|
|
||||||
Copyright (c) 2015 Douglas Christopher Wilson <doug@somethingdoug.com>
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
-317
@@ -1,317 +0,0 @@
|
|||||||
# cookie
|
|
||||||
|
|
||||||
[![NPM Version][npm-version-image]][npm-url]
|
|
||||||
[![NPM Downloads][npm-downloads-image]][npm-url]
|
|
||||||
[![Node.js Version][node-image]][node-url]
|
|
||||||
[![Build Status][ci-image]][ci-url]
|
|
||||||
[![Coverage Status][coveralls-image]][coveralls-url]
|
|
||||||
|
|
||||||
Basic HTTP cookie parser and serializer for HTTP servers.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
This is a [Node.js](https://nodejs.org/en/) module available through the
|
|
||||||
[npm registry](https://www.npmjs.com/). Installation is done using the
|
|
||||||
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ npm install cookie
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
```js
|
|
||||||
var cookie = require('cookie');
|
|
||||||
```
|
|
||||||
|
|
||||||
### cookie.parse(str, options)
|
|
||||||
|
|
||||||
Parse an HTTP `Cookie` header string and returning an object of all cookie name-value pairs.
|
|
||||||
The `str` argument is the string representing a `Cookie` header value and `options` is an
|
|
||||||
optional object containing additional parsing options.
|
|
||||||
|
|
||||||
```js
|
|
||||||
var cookies = cookie.parse('foo=bar; equation=E%3Dmc%5E2');
|
|
||||||
// { foo: 'bar', equation: 'E=mc^2' }
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Options
|
|
||||||
|
|
||||||
`cookie.parse` accepts these properties in the options object.
|
|
||||||
|
|
||||||
##### decode
|
|
||||||
|
|
||||||
Specifies a function that will be used to decode a cookie's value. Since the value of a cookie
|
|
||||||
has a limited character set (and must be a simple string), this function can be used to decode
|
|
||||||
a previously-encoded cookie value into a JavaScript string or other object.
|
|
||||||
|
|
||||||
The default function is the global `decodeURIComponent`, which will decode any URL-encoded
|
|
||||||
sequences into their byte representations.
|
|
||||||
|
|
||||||
**note** if an error is thrown from this function, the original, non-decoded cookie value will
|
|
||||||
be returned as the cookie's value.
|
|
||||||
|
|
||||||
### cookie.serialize(name, value, options)
|
|
||||||
|
|
||||||
Serialize a cookie name-value pair into a `Set-Cookie` header string. The `name` argument is the
|
|
||||||
name for the cookie, the `value` argument is the value to set the cookie to, and the `options`
|
|
||||||
argument is an optional object containing additional serialization options.
|
|
||||||
|
|
||||||
```js
|
|
||||||
var setCookie = cookie.serialize('foo', 'bar');
|
|
||||||
// foo=bar
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Options
|
|
||||||
|
|
||||||
`cookie.serialize` accepts these properties in the options object.
|
|
||||||
|
|
||||||
##### domain
|
|
||||||
|
|
||||||
Specifies the value for the [`Domain` `Set-Cookie` attribute][rfc-6265-5.2.3]. By default, no
|
|
||||||
domain is set, and most clients will consider the cookie to apply to only the current domain.
|
|
||||||
|
|
||||||
##### encode
|
|
||||||
|
|
||||||
Specifies a function that will be used to encode a cookie's value. Since value of a cookie
|
|
||||||
has a limited character set (and must be a simple string), this function can be used to encode
|
|
||||||
a value into a string suited for a cookie's value.
|
|
||||||
|
|
||||||
The default function is the global `encodeURIComponent`, which will encode a JavaScript string
|
|
||||||
into UTF-8 byte sequences and then URL-encode any that fall outside of the cookie range.
|
|
||||||
|
|
||||||
##### expires
|
|
||||||
|
|
||||||
Specifies the `Date` object to be the value for the [`Expires` `Set-Cookie` attribute][rfc-6265-5.2.1].
|
|
||||||
By default, no expiration is set, and most clients will consider this a "non-persistent cookie" and
|
|
||||||
will delete it on a condition like exiting a web browser application.
|
|
||||||
|
|
||||||
**note** the [cookie storage model specification][rfc-6265-5.3] states that if both `expires` and
|
|
||||||
`maxAge` are set, then `maxAge` takes precedence, but it is possible not all clients by obey this,
|
|
||||||
so if both are set, they should point to the same date and time.
|
|
||||||
|
|
||||||
##### httpOnly
|
|
||||||
|
|
||||||
Specifies the `boolean` value for the [`HttpOnly` `Set-Cookie` attribute][rfc-6265-5.2.6]. When truthy,
|
|
||||||
the `HttpOnly` attribute is set, otherwise it is not. By default, the `HttpOnly` attribute is not set.
|
|
||||||
|
|
||||||
**note** be careful when setting this to `true`, as compliant clients will not allow client-side
|
|
||||||
JavaScript to see the cookie in `document.cookie`.
|
|
||||||
|
|
||||||
##### maxAge
|
|
||||||
|
|
||||||
Specifies the `number` (in seconds) to be the value for the [`Max-Age` `Set-Cookie` attribute][rfc-6265-5.2.2].
|
|
||||||
The given number will be converted to an integer by rounding down. By default, no maximum age is set.
|
|
||||||
|
|
||||||
**note** the [cookie storage model specification][rfc-6265-5.3] states that if both `expires` and
|
|
||||||
`maxAge` are set, then `maxAge` takes precedence, but it is possible not all clients by obey this,
|
|
||||||
so if both are set, they should point to the same date and time.
|
|
||||||
|
|
||||||
##### partitioned
|
|
||||||
|
|
||||||
Specifies the `boolean` value for the [`Partitioned` `Set-Cookie`](rfc-cutler-httpbis-partitioned-cookies)
|
|
||||||
attribute. When truthy, the `Partitioned` attribute is set, otherwise it is not. By default, the
|
|
||||||
`Partitioned` attribute is not set.
|
|
||||||
|
|
||||||
**note** This is an attribute that has not yet been fully standardized, and may change in the future.
|
|
||||||
This also means many clients may ignore this attribute until they understand it.
|
|
||||||
|
|
||||||
More information about can be found in [the proposal](https://github.com/privacycg/CHIPS).
|
|
||||||
|
|
||||||
##### path
|
|
||||||
|
|
||||||
Specifies the value for the [`Path` `Set-Cookie` attribute][rfc-6265-5.2.4]. By default, the path
|
|
||||||
is considered the ["default path"][rfc-6265-5.1.4].
|
|
||||||
|
|
||||||
##### priority
|
|
||||||
|
|
||||||
Specifies the `string` to be the value for the [`Priority` `Set-Cookie` attribute][rfc-west-cookie-priority-00-4.1].
|
|
||||||
|
|
||||||
- `'low'` will set the `Priority` attribute to `Low`.
|
|
||||||
- `'medium'` will set the `Priority` attribute to `Medium`, the default priority when not set.
|
|
||||||
- `'high'` will set the `Priority` attribute to `High`.
|
|
||||||
|
|
||||||
More information about the different priority levels can be found in
|
|
||||||
[the specification][rfc-west-cookie-priority-00-4.1].
|
|
||||||
|
|
||||||
**note** This is an attribute that has not yet been fully standardized, and may change in the future.
|
|
||||||
This also means many clients may ignore this attribute until they understand it.
|
|
||||||
|
|
||||||
##### sameSite
|
|
||||||
|
|
||||||
Specifies the `boolean` or `string` to be the value for the [`SameSite` `Set-Cookie` attribute][rfc-6265bis-09-5.4.7].
|
|
||||||
|
|
||||||
- `true` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
|
|
||||||
- `false` will not set the `SameSite` attribute.
|
|
||||||
- `'lax'` will set the `SameSite` attribute to `Lax` for lax same site enforcement.
|
|
||||||
- `'none'` will set the `SameSite` attribute to `None` for an explicit cross-site cookie.
|
|
||||||
- `'strict'` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
|
|
||||||
|
|
||||||
More information about the different enforcement levels can be found in
|
|
||||||
[the specification][rfc-6265bis-09-5.4.7].
|
|
||||||
|
|
||||||
**note** This is an attribute that has not yet been fully standardized, and may change in the future.
|
|
||||||
This also means many clients may ignore this attribute until they understand it.
|
|
||||||
|
|
||||||
##### secure
|
|
||||||
|
|
||||||
Specifies the `boolean` value for the [`Secure` `Set-Cookie` attribute][rfc-6265-5.2.5]. When truthy,
|
|
||||||
the `Secure` attribute is set, otherwise it is not. By default, the `Secure` attribute is not set.
|
|
||||||
|
|
||||||
**note** be careful when setting this to `true`, as compliant clients will not send the cookie back to
|
|
||||||
the server in the future if the browser does not have an HTTPS connection.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
The following example uses this module in conjunction with the Node.js core HTTP server
|
|
||||||
to prompt a user for their name and display it back on future visits.
|
|
||||||
|
|
||||||
```js
|
|
||||||
var cookie = require('cookie');
|
|
||||||
var escapeHtml = require('escape-html');
|
|
||||||
var http = require('http');
|
|
||||||
var url = require('url');
|
|
||||||
|
|
||||||
function onRequest(req, res) {
|
|
||||||
// Parse the query string
|
|
||||||
var query = url.parse(req.url, true, true).query;
|
|
||||||
|
|
||||||
if (query && query.name) {
|
|
||||||
// Set a new cookie with the name
|
|
||||||
res.setHeader('Set-Cookie', cookie.serialize('name', String(query.name), {
|
|
||||||
httpOnly: true,
|
|
||||||
maxAge: 60 * 60 * 24 * 7 // 1 week
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Redirect back after setting cookie
|
|
||||||
res.statusCode = 302;
|
|
||||||
res.setHeader('Location', req.headers.referer || '/');
|
|
||||||
res.end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the cookies on the request
|
|
||||||
var cookies = cookie.parse(req.headers.cookie || '');
|
|
||||||
|
|
||||||
// Get the visitor name set in the cookie
|
|
||||||
var name = cookies.name;
|
|
||||||
|
|
||||||
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
|
|
||||||
|
|
||||||
if (name) {
|
|
||||||
res.write('<p>Welcome back, <b>' + escapeHtml(name) + '</b>!</p>');
|
|
||||||
} else {
|
|
||||||
res.write('<p>Hello, new visitor!</p>');
|
|
||||||
}
|
|
||||||
|
|
||||||
res.write('<form method="GET">');
|
|
||||||
res.write('<input placeholder="enter your name" name="name"> <input type="submit" value="Set Name">');
|
|
||||||
res.end('</form>');
|
|
||||||
}
|
|
||||||
|
|
||||||
http.createServer(onRequest).listen(3000);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ npm test
|
|
||||||
```
|
|
||||||
|
|
||||||
## Benchmark
|
|
||||||
|
|
||||||
```
|
|
||||||
$ npm run bench
|
|
||||||
|
|
||||||
> cookie@0.5.0 bench
|
|
||||||
> node benchmark/index.js
|
|
||||||
|
|
||||||
node@18.18.2
|
|
||||||
acorn@8.10.0
|
|
||||||
ada@2.6.0
|
|
||||||
ares@1.19.1
|
|
||||||
brotli@1.0.9
|
|
||||||
cldr@43.1
|
|
||||||
icu@73.2
|
|
||||||
llhttp@6.0.11
|
|
||||||
modules@108
|
|
||||||
napi@9
|
|
||||||
nghttp2@1.57.0
|
|
||||||
nghttp3@0.7.0
|
|
||||||
ngtcp2@0.8.1
|
|
||||||
openssl@3.0.10+quic
|
|
||||||
simdutf@3.2.14
|
|
||||||
tz@2023c
|
|
||||||
undici@5.26.3
|
|
||||||
unicode@15.0
|
|
||||||
uv@1.44.2
|
|
||||||
uvwasi@0.0.18
|
|
||||||
v8@10.2.154.26-node.26
|
|
||||||
zlib@1.2.13.1-motley
|
|
||||||
|
|
||||||
> node benchmark/parse-top.js
|
|
||||||
|
|
||||||
cookie.parse - top sites
|
|
||||||
|
|
||||||
14 tests completed.
|
|
||||||
|
|
||||||
parse accounts.google.com x 2,588,913 ops/sec ±0.74% (186 runs sampled)
|
|
||||||
parse apple.com x 2,370,002 ops/sec ±0.69% (186 runs sampled)
|
|
||||||
parse cloudflare.com x 2,213,102 ops/sec ±0.88% (188 runs sampled)
|
|
||||||
parse docs.google.com x 2,194,157 ops/sec ±1.03% (184 runs sampled)
|
|
||||||
parse drive.google.com x 2,265,084 ops/sec ±0.79% (187 runs sampled)
|
|
||||||
parse en.wikipedia.org x 457,099 ops/sec ±0.81% (186 runs sampled)
|
|
||||||
parse linkedin.com x 504,407 ops/sec ±0.89% (186 runs sampled)
|
|
||||||
parse maps.google.com x 1,230,959 ops/sec ±0.98% (186 runs sampled)
|
|
||||||
parse microsoft.com x 926,294 ops/sec ±0.88% (184 runs sampled)
|
|
||||||
parse play.google.com x 2,311,338 ops/sec ±0.83% (185 runs sampled)
|
|
||||||
parse support.google.com x 1,508,850 ops/sec ±0.86% (186 runs sampled)
|
|
||||||
parse www.google.com x 1,022,582 ops/sec ±1.32% (182 runs sampled)
|
|
||||||
parse youtu.be x 332,136 ops/sec ±1.02% (185 runs sampled)
|
|
||||||
parse youtube.com x 323,833 ops/sec ±0.77% (183 runs sampled)
|
|
||||||
|
|
||||||
> node benchmark/parse.js
|
|
||||||
|
|
||||||
cookie.parse - generic
|
|
||||||
|
|
||||||
6 tests completed.
|
|
||||||
|
|
||||||
simple x 3,214,032 ops/sec ±1.61% (183 runs sampled)
|
|
||||||
decode x 587,237 ops/sec ±1.16% (187 runs sampled)
|
|
||||||
unquote x 2,954,618 ops/sec ±1.35% (183 runs sampled)
|
|
||||||
duplicates x 857,008 ops/sec ±0.89% (187 runs sampled)
|
|
||||||
10 cookies x 292,133 ops/sec ±0.89% (187 runs sampled)
|
|
||||||
100 cookies x 22,610 ops/sec ±0.68% (187 runs sampled)
|
|
||||||
```
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [RFC 6265: HTTP State Management Mechanism][rfc-6265]
|
|
||||||
- [Same-site Cookies][rfc-6265bis-09-5.4.7]
|
|
||||||
|
|
||||||
[rfc-cutler-httpbis-partitioned-cookies]: https://tools.ietf.org/html/draft-cutler-httpbis-partitioned-cookies/
|
|
||||||
[rfc-west-cookie-priority-00-4.1]: https://tools.ietf.org/html/draft-west-cookie-priority-00#section-4.1
|
|
||||||
[rfc-6265bis-09-5.4.7]: https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-09#section-5.4.7
|
|
||||||
[rfc-6265]: https://tools.ietf.org/html/rfc6265
|
|
||||||
[rfc-6265-5.1.4]: https://tools.ietf.org/html/rfc6265#section-5.1.4
|
|
||||||
[rfc-6265-5.2.1]: https://tools.ietf.org/html/rfc6265#section-5.2.1
|
|
||||||
[rfc-6265-5.2.2]: https://tools.ietf.org/html/rfc6265#section-5.2.2
|
|
||||||
[rfc-6265-5.2.3]: https://tools.ietf.org/html/rfc6265#section-5.2.3
|
|
||||||
[rfc-6265-5.2.4]: https://tools.ietf.org/html/rfc6265#section-5.2.4
|
|
||||||
[rfc-6265-5.2.5]: https://tools.ietf.org/html/rfc6265#section-5.2.5
|
|
||||||
[rfc-6265-5.2.6]: https://tools.ietf.org/html/rfc6265#section-5.2.6
|
|
||||||
[rfc-6265-5.3]: https://tools.ietf.org/html/rfc6265#section-5.3
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[MIT](LICENSE)
|
|
||||||
|
|
||||||
[ci-image]: https://badgen.net/github/checks/jshttp/cookie/master?label=ci
|
|
||||||
[ci-url]: https://github.com/jshttp/cookie/actions/workflows/ci.yml
|
|
||||||
[coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/cookie/master
|
|
||||||
[coveralls-url]: https://coveralls.io/r/jshttp/cookie?branch=master
|
|
||||||
[node-image]: https://badgen.net/npm/node/cookie
|
|
||||||
[node-url]: https://nodejs.org/en/download
|
|
||||||
[npm-downloads-image]: https://badgen.net/npm/dm/cookie
|
|
||||||
[npm-url]: https://npmjs.org/package/cookie
|
|
||||||
[npm-version-image]: https://badgen.net/npm/v/cookie
|
|
||||||
-25
@@ -1,25 +0,0 @@
|
|||||||
# Security Policies and Procedures
|
|
||||||
|
|
||||||
## Reporting a Bug
|
|
||||||
|
|
||||||
The `cookie` team and community take all security bugs seriously. Thank
|
|
||||||
you for improving the security of the project. We appreciate your efforts and
|
|
||||||
responsible disclosure and will make every effort to acknowledge your
|
|
||||||
contributions.
|
|
||||||
|
|
||||||
Report security bugs by emailing the current owner(s) of `cookie`. This
|
|
||||||
information can be found in the npm registry using the command
|
|
||||||
`npm owner ls cookie`.
|
|
||||||
If unsure or unable to get the information from the above, open an issue
|
|
||||||
in the [project issue tracker](https://github.com/jshttp/cookie/issues)
|
|
||||||
asking for the current contact information.
|
|
||||||
|
|
||||||
To ensure the timely response to your report, please ensure that the entirety
|
|
||||||
of the report is contained within the email body and not solely behind a web
|
|
||||||
link or an attachment.
|
|
||||||
|
|
||||||
At least one owner will acknowledge your email within 48 hours, and will send a
|
|
||||||
more detailed response within 48 hours indicating the next steps in handling
|
|
||||||
your report. After the initial reply to your report, the owners will
|
|
||||||
endeavor to keep you informed of the progress towards a fix and full
|
|
||||||
announcement, and may ask for additional information or guidance.
|
|
||||||
-335
@@ -1,335 +0,0 @@
|
|||||||
/*!
|
|
||||||
* cookie
|
|
||||||
* Copyright(c) 2012-2014 Roman Shtylman
|
|
||||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
|
||||||
* MIT Licensed
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module exports.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
exports.parse = parse;
|
|
||||||
exports.serialize = serialize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module variables.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
var __toString = Object.prototype.toString
|
|
||||||
var __hasOwnProperty = Object.prototype.hasOwnProperty
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match cookie-name in RFC 6265 sec 4.1.1
|
|
||||||
* This refers out to the obsoleted definition of token in RFC 2616 sec 2.2
|
|
||||||
* which has been replaced by the token definition in RFC 7230 appendix B.
|
|
||||||
*
|
|
||||||
* cookie-name = token
|
|
||||||
* token = 1*tchar
|
|
||||||
* tchar = "!" / "#" / "$" / "%" / "&" / "'" /
|
|
||||||
* "*" / "+" / "-" / "." / "^" / "_" /
|
|
||||||
* "`" / "|" / "~" / DIGIT / ALPHA
|
|
||||||
*/
|
|
||||||
|
|
||||||
var cookieNameRegExp = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match cookie-value in RFC 6265 sec 4.1.1
|
|
||||||
*
|
|
||||||
* cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
|
|
||||||
* cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
|
|
||||||
* ; US-ASCII characters excluding CTLs,
|
|
||||||
* ; whitespace DQUOTE, comma, semicolon,
|
|
||||||
* ; and backslash
|
|
||||||
*/
|
|
||||||
|
|
||||||
var cookieValueRegExp = /^("?)[\u0021\u0023-\u002B\u002D-\u003A\u003C-\u005B\u005D-\u007E]*\1$/;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match domain-value in RFC 6265 sec 4.1.1
|
|
||||||
*
|
|
||||||
* domain-value = <subdomain>
|
|
||||||
* ; defined in [RFC1034], Section 3.5, as
|
|
||||||
* ; enhanced by [RFC1123], Section 2.1
|
|
||||||
* <subdomain> = <label> | <subdomain> "." <label>
|
|
||||||
* <label> = <let-dig> [ [ <ldh-str> ] <let-dig> ]
|
|
||||||
* Labels must be 63 characters or less.
|
|
||||||
* 'let-dig' not 'letter' in the first char, per RFC1123
|
|
||||||
* <ldh-str> = <let-dig-hyp> | <let-dig-hyp> <ldh-str>
|
|
||||||
* <let-dig-hyp> = <let-dig> | "-"
|
|
||||||
* <let-dig> = <letter> | <digit>
|
|
||||||
* <letter> = any one of the 52 alphabetic characters A through Z in
|
|
||||||
* upper case and a through z in lower case
|
|
||||||
* <digit> = any one of the ten digits 0 through 9
|
|
||||||
*
|
|
||||||
* Keep support for leading dot: https://github.com/jshttp/cookie/issues/173
|
|
||||||
*
|
|
||||||
* > (Note that a leading %x2E ("."), if present, is ignored even though that
|
|
||||||
* character is not permitted, but a trailing %x2E ("."), if present, will
|
|
||||||
* cause the user agent to ignore the attribute.)
|
|
||||||
*/
|
|
||||||
|
|
||||||
var domainValueRegExp = /^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RegExp to match path-value in RFC 6265 sec 4.1.1
|
|
||||||
*
|
|
||||||
* path-value = <any CHAR except CTLs or ";">
|
|
||||||
* CHAR = %x01-7F
|
|
||||||
* ; defined in RFC 5234 appendix B.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
var pathValueRegExp = /^[\u0020-\u003A\u003D-\u007E]*$/;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a cookie header.
|
|
||||||
*
|
|
||||||
* Parse the given cookie header string into an object
|
|
||||||
* The object has the various cookies as keys(names) => values
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* @param {object} [opt]
|
|
||||||
* @return {object}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function parse(str, opt) {
|
|
||||||
if (typeof str !== 'string') {
|
|
||||||
throw new TypeError('argument str must be a string');
|
|
||||||
}
|
|
||||||
|
|
||||||
var obj = {};
|
|
||||||
var len = str.length;
|
|
||||||
// RFC 6265 sec 4.1.1, RFC 2616 2.2 defines a cookie name consists of one char minimum, plus '='.
|
|
||||||
if (len < 2) return obj;
|
|
||||||
|
|
||||||
var dec = (opt && opt.decode) || decode;
|
|
||||||
var index = 0;
|
|
||||||
var eqIdx = 0;
|
|
||||||
var endIdx = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
eqIdx = str.indexOf('=', index);
|
|
||||||
if (eqIdx === -1) break; // No more cookie pairs.
|
|
||||||
|
|
||||||
endIdx = str.indexOf(';', index);
|
|
||||||
|
|
||||||
if (endIdx === -1) {
|
|
||||||
endIdx = len;
|
|
||||||
} else if (eqIdx > endIdx) {
|
|
||||||
// backtrack on prior semicolon
|
|
||||||
index = str.lastIndexOf(';', eqIdx - 1) + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var keyStartIdx = startIndex(str, index, eqIdx);
|
|
||||||
var keyEndIdx = endIndex(str, eqIdx, keyStartIdx);
|
|
||||||
var key = str.slice(keyStartIdx, keyEndIdx);
|
|
||||||
|
|
||||||
// only assign once
|
|
||||||
if (!__hasOwnProperty.call(obj, key)) {
|
|
||||||
var valStartIdx = startIndex(str, eqIdx + 1, endIdx);
|
|
||||||
var valEndIdx = endIndex(str, endIdx, valStartIdx);
|
|
||||||
|
|
||||||
if (str.charCodeAt(valStartIdx) === 0x22 /* " */ && str.charCodeAt(valEndIdx - 1) === 0x22 /* " */) {
|
|
||||||
valStartIdx++;
|
|
||||||
valEndIdx--;
|
|
||||||
}
|
|
||||||
|
|
||||||
var val = str.slice(valStartIdx, valEndIdx);
|
|
||||||
obj[key] = tryDecode(val, dec);
|
|
||||||
}
|
|
||||||
|
|
||||||
index = endIdx + 1
|
|
||||||
} while (index < len);
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
function startIndex(str, index, max) {
|
|
||||||
do {
|
|
||||||
var code = str.charCodeAt(index);
|
|
||||||
if (code !== 0x20 /* */ && code !== 0x09 /* \t */) return index;
|
|
||||||
} while (++index < max);
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
|
|
||||||
function endIndex(str, index, min) {
|
|
||||||
while (index > min) {
|
|
||||||
var code = str.charCodeAt(--index);
|
|
||||||
if (code !== 0x20 /* */ && code !== 0x09 /* \t */) return index + 1;
|
|
||||||
}
|
|
||||||
return min;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize data into a cookie header.
|
|
||||||
*
|
|
||||||
* Serialize a name value pair into a cookie string suitable for
|
|
||||||
* http headers. An optional options object specifies cookie parameters.
|
|
||||||
*
|
|
||||||
* serialize('foo', 'bar', { httpOnly: true })
|
|
||||||
* => "foo=bar; httpOnly"
|
|
||||||
*
|
|
||||||
* @param {string} name
|
|
||||||
* @param {string} val
|
|
||||||
* @param {object} [opt]
|
|
||||||
* @return {string}
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
function serialize(name, val, opt) {
|
|
||||||
var enc = (opt && opt.encode) || encodeURIComponent;
|
|
||||||
|
|
||||||
if (typeof enc !== 'function') {
|
|
||||||
throw new TypeError('option encode is invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cookieNameRegExp.test(name)) {
|
|
||||||
throw new TypeError('argument name is invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
var value = enc(val);
|
|
||||||
|
|
||||||
if (!cookieValueRegExp.test(value)) {
|
|
||||||
throw new TypeError('argument val is invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
var str = name + '=' + value;
|
|
||||||
if (!opt) return str;
|
|
||||||
|
|
||||||
if (null != opt.maxAge) {
|
|
||||||
var maxAge = Math.floor(opt.maxAge);
|
|
||||||
|
|
||||||
if (!isFinite(maxAge)) {
|
|
||||||
throw new TypeError('option maxAge is invalid')
|
|
||||||
}
|
|
||||||
|
|
||||||
str += '; Max-Age=' + maxAge;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.domain) {
|
|
||||||
if (!domainValueRegExp.test(opt.domain)) {
|
|
||||||
throw new TypeError('option domain is invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
str += '; Domain=' + opt.domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.path) {
|
|
||||||
if (!pathValueRegExp.test(opt.path)) {
|
|
||||||
throw new TypeError('option path is invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
str += '; Path=' + opt.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.expires) {
|
|
||||||
var expires = opt.expires
|
|
||||||
|
|
||||||
if (!isDate(expires) || isNaN(expires.valueOf())) {
|
|
||||||
throw new TypeError('option expires is invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
str += '; Expires=' + expires.toUTCString()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.httpOnly) {
|
|
||||||
str += '; HttpOnly';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.secure) {
|
|
||||||
str += '; Secure';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.partitioned) {
|
|
||||||
str += '; Partitioned'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.priority) {
|
|
||||||
var priority = typeof opt.priority === 'string'
|
|
||||||
? opt.priority.toLowerCase() : opt.priority;
|
|
||||||
|
|
||||||
switch (priority) {
|
|
||||||
case 'low':
|
|
||||||
str += '; Priority=Low'
|
|
||||||
break
|
|
||||||
case 'medium':
|
|
||||||
str += '; Priority=Medium'
|
|
||||||
break
|
|
||||||
case 'high':
|
|
||||||
str += '; Priority=High'
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw new TypeError('option priority is invalid')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opt.sameSite) {
|
|
||||||
var sameSite = typeof opt.sameSite === 'string'
|
|
||||||
? opt.sameSite.toLowerCase() : opt.sameSite;
|
|
||||||
|
|
||||||
switch (sameSite) {
|
|
||||||
case true:
|
|
||||||
str += '; SameSite=Strict';
|
|
||||||
break;
|
|
||||||
case 'lax':
|
|
||||||
str += '; SameSite=Lax';
|
|
||||||
break;
|
|
||||||
case 'strict':
|
|
||||||
str += '; SameSite=Strict';
|
|
||||||
break;
|
|
||||||
case 'none':
|
|
||||||
str += '; SameSite=None';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new TypeError('option sameSite is invalid');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* URL-decode string value. Optimized to skip native call when no %.
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function decode (str) {
|
|
||||||
return str.indexOf('%') !== -1
|
|
||||||
? decodeURIComponent(str)
|
|
||||||
: str
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if value is a Date.
|
|
||||||
*
|
|
||||||
* @param {*} val
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function isDate (val) {
|
|
||||||
return __toString.call(val) === '[object Date]';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try decoding a string using a decoding function.
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* @param {function} decode
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
|
|
||||||
function tryDecode(str, decode) {
|
|
||||||
try {
|
|
||||||
return decode(str);
|
|
||||||
} catch (e) {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-44
@@ -1,44 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "cookie",
|
|
||||||
"description": "HTTP server cookie parsing and serialization",
|
|
||||||
"version": "0.7.2",
|
|
||||||
"author": "Roman Shtylman <shtylman@gmail.com>",
|
|
||||||
"contributors": [
|
|
||||||
"Douglas Christopher Wilson <doug@somethingdoug.com>"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"keywords": [
|
|
||||||
"cookie",
|
|
||||||
"cookies"
|
|
||||||
],
|
|
||||||
"repository": "jshttp/cookie",
|
|
||||||
"devDependencies": {
|
|
||||||
"beautify-benchmark": "0.2.4",
|
|
||||||
"benchmark": "2.1.4",
|
|
||||||
"eslint": "8.53.0",
|
|
||||||
"eslint-plugin-markdown": "3.0.1",
|
|
||||||
"mocha": "10.2.0",
|
|
||||||
"nyc": "15.1.0",
|
|
||||||
"safe-buffer": "5.2.1",
|
|
||||||
"top-sites": "1.1.194"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"HISTORY.md",
|
|
||||||
"LICENSE",
|
|
||||||
"README.md",
|
|
||||||
"SECURITY.md",
|
|
||||||
"index.js"
|
|
||||||
],
|
|
||||||
"main": "index.js",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"bench": "node benchmark/index.js",
|
|
||||||
"lint": "eslint .",
|
|
||||||
"test": "mocha --reporter spec --bail --check-leaks test/",
|
|
||||||
"test-ci": "nyc --reporter=lcov --reporter=text npm test",
|
|
||||||
"test-cov": "nyc --reporter=html --reporter=text npm test",
|
|
||||||
"update-bench": "node scripts/update-benchmark.js"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
(The MIT License)
|
|
||||||
|
|
||||||
Copyright (c) 2013 Troy Goode <troygoode@gmail.com>
|
|
||||||
|
|
||||||
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.
|
|
||||||
-277
@@ -1,277 +0,0 @@
|
|||||||
# cors
|
|
||||||
|
|
||||||
[![NPM Version][npm-image]][npm-url]
|
|
||||||
[![NPM Downloads][downloads-image]][downloads-url]
|
|
||||||
[![Build Status][github-actions-ci-image]][github-actions-ci-url]
|
|
||||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
|
||||||
|
|
||||||
CORS is a [Node.js](https://nodejs.org/en/) middleware for [Express](https://expressjs.com/)/[Connect](https://github.com/senchalabs/connect) that sets [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) response headers. These headers tell browsers which origins can read responses from your server.
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> **How CORS Works:** This package sets response headers—it doesn't block requests. CORS is enforced by browsers: they check the headers and decide if JavaScript can read the response. Non-browser clients (curl, Postman, other servers) ignore CORS entirely. See the [MDN CORS guide](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) for details.
|
|
||||||
|
|
||||||
* [Installation](#installation)
|
|
||||||
* [Usage](#usage)
|
|
||||||
* [Simple Usage](#simple-usage-enable-all-cors-requests)
|
|
||||||
* [Enable CORS for a Single Route](#enable-cors-for-a-single-route)
|
|
||||||
* [Configuring CORS](#configuring-cors)
|
|
||||||
* [Configuring CORS w/ Dynamic Origin](#configuring-cors-w-dynamic-origin)
|
|
||||||
* [Enabling CORS Pre-Flight](#enabling-cors-pre-flight)
|
|
||||||
* [Customizing CORS Settings Dynamically per Request](#customizing-cors-settings-dynamically-per-request)
|
|
||||||
* [Configuration Options](#configuration-options)
|
|
||||||
* [Common Misconceptions](#common-misconceptions)
|
|
||||||
* [License](#license)
|
|
||||||
* [Original Author](#original-author)
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
This is a [Node.js](https://nodejs.org/en/) module available through the
|
|
||||||
[npm registry](https://www.npmjs.com/). Installation is done using the
|
|
||||||
[`npm install` command](https://docs.npmjs.com/downloading-and-installing-packages-locally):
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ npm install cors
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Simple Usage (Enable *All* CORS Requests)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var express = require('express')
|
|
||||||
var cors = require('cors')
|
|
||||||
var app = express()
|
|
||||||
|
|
||||||
// Adds headers: Access-Control-Allow-Origin: *
|
|
||||||
app.use(cors())
|
|
||||||
|
|
||||||
app.get('/products/:id', function (req, res, next) {
|
|
||||||
res.json({msg: 'Hello'})
|
|
||||||
})
|
|
||||||
|
|
||||||
app.listen(80, function () {
|
|
||||||
console.log('web server listening on port 80')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Enable CORS for a Single Route
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var express = require('express')
|
|
||||||
var cors = require('cors')
|
|
||||||
var app = express()
|
|
||||||
|
|
||||||
// Adds headers: Access-Control-Allow-Origin: *
|
|
||||||
app.get('/products/:id', cors(), function (req, res, next) {
|
|
||||||
res.json({msg: 'Hello'})
|
|
||||||
})
|
|
||||||
|
|
||||||
app.listen(80, function () {
|
|
||||||
console.log('web server listening on port 80')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuring CORS
|
|
||||||
|
|
||||||
See the [configuration options](#configuration-options) for details.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var express = require('express')
|
|
||||||
var cors = require('cors')
|
|
||||||
var app = express()
|
|
||||||
|
|
||||||
var corsOptions = {
|
|
||||||
origin: 'http://example.com',
|
|
||||||
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds headers: Access-Control-Allow-Origin: http://example.com, Vary: Origin
|
|
||||||
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
|
|
||||||
res.json({msg: 'Hello'})
|
|
||||||
})
|
|
||||||
|
|
||||||
app.listen(80, function () {
|
|
||||||
console.log('web server listening on port 80')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuring CORS w/ Dynamic Origin
|
|
||||||
|
|
||||||
This module supports validating the origin dynamically using a function provided
|
|
||||||
to the `origin` option. This function will be passed a string that is the origin
|
|
||||||
(or `undefined` if the request has no origin), and a `callback` with the signature
|
|
||||||
`callback(error, origin)`.
|
|
||||||
|
|
||||||
The `origin` argument to the callback can be any value allowed for the `origin`
|
|
||||||
option of the middleware, except a function. See the
|
|
||||||
[configuration options](#configuration-options) section for more information on all
|
|
||||||
the possible value types.
|
|
||||||
|
|
||||||
This function is designed to allow the dynamic loading of allowed origin(s) from
|
|
||||||
a backing datasource, like a database.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var express = require('express')
|
|
||||||
var cors = require('cors')
|
|
||||||
var app = express()
|
|
||||||
|
|
||||||
var corsOptions = {
|
|
||||||
origin: function (origin, callback) {
|
|
||||||
// db.loadOrigins is an example call to load
|
|
||||||
// a list of origins from a backing database
|
|
||||||
db.loadOrigins(function (error, origins) {
|
|
||||||
callback(error, origins)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds headers: Access-Control-Allow-Origin: <matched origin>, Vary: Origin
|
|
||||||
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
|
|
||||||
res.json({msg: 'Hello'})
|
|
||||||
})
|
|
||||||
|
|
||||||
app.listen(80, function () {
|
|
||||||
console.log('web server listening on port 80')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Enabling CORS Pre-Flight
|
|
||||||
|
|
||||||
Certain CORS requests are considered 'complex' and require an initial
|
|
||||||
`OPTIONS` request (called the "pre-flight request"). An example of a
|
|
||||||
'complex' CORS request is one that uses an HTTP verb other than
|
|
||||||
GET/HEAD/POST (such as DELETE) or that uses custom headers. To enable
|
|
||||||
pre-flighting, you must add a new OPTIONS handler for the route you want
|
|
||||||
to support:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var express = require('express')
|
|
||||||
var cors = require('cors')
|
|
||||||
var app = express()
|
|
||||||
|
|
||||||
app.options('/products/:id', cors()) // preflight for DELETE
|
|
||||||
app.del('/products/:id', cors(), function (req, res, next) {
|
|
||||||
res.json({msg: 'Hello'})
|
|
||||||
})
|
|
||||||
|
|
||||||
app.listen(80, function () {
|
|
||||||
console.log('web server listening on port 80')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also enable pre-flight across-the-board like so:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
app.options('*', cors()) // include before other routes
|
|
||||||
```
|
|
||||||
|
|
||||||
NOTE: When using this middleware as an application level middleware (for
|
|
||||||
example, `app.use(cors())`), pre-flight requests are already handled for all
|
|
||||||
routes.
|
|
||||||
|
|
||||||
### Customizing CORS Settings Dynamically per Request
|
|
||||||
|
|
||||||
For APIs that require different CORS configurations for specific routes or requests, you can dynamically generate CORS options based on the incoming request. The `cors` middleware allows you to achieve this by passing a function instead of static options. This function is called for each incoming request and must use the callback pattern to return the appropriate CORS options.
|
|
||||||
|
|
||||||
The function accepts:
|
|
||||||
1. **`req`**:
|
|
||||||
- The incoming request object.
|
|
||||||
|
|
||||||
2. **`callback(error, corsOptions)`**:
|
|
||||||
- A function used to return the computed CORS options.
|
|
||||||
- **Arguments**:
|
|
||||||
- **`error`**: Pass `null` if there’s no error, or an error object to indicate a failure.
|
|
||||||
- **`corsOptions`**: An object specifying the CORS policy for the current request.
|
|
||||||
|
|
||||||
Here’s an example that handles both public routes and restricted, credential-sensitive routes:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var dynamicCorsOptions = function(req, callback) {
|
|
||||||
var corsOptions;
|
|
||||||
if (req.path.startsWith('/auth/connect/')) {
|
|
||||||
// Access-Control-Allow-Origin: http://mydomain.com, Access-Control-Allow-Credentials: true, Vary: Origin
|
|
||||||
corsOptions = {
|
|
||||||
origin: 'http://mydomain.com',
|
|
||||||
credentials: true
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Access-Control-Allow-Origin: *
|
|
||||||
corsOptions = { origin: '*' };
|
|
||||||
}
|
|
||||||
callback(null, corsOptions);
|
|
||||||
};
|
|
||||||
|
|
||||||
app.use(cors(dynamicCorsOptions));
|
|
||||||
|
|
||||||
app.get('/auth/connect/twitter', function (req, res) {
|
|
||||||
res.send('Hello');
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/public', function (req, res) {
|
|
||||||
res.send('Hello');
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(80, function () {
|
|
||||||
console.log('web server listening on port 80')
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
* `origin`: Configures the **Access-Control-Allow-Origin** CORS header. Possible values:
|
|
||||||
- `Boolean` - set `origin` to `true` to reflect the [request origin](https://datatracker.ietf.org/doc/html/draft-abarth-origin-09), as defined by `req.header('Origin')`, or set it to `false` to disable CORS.
|
|
||||||
- `String` - set `origin` to a specific origin. For example, if you set it to
|
|
||||||
- `"http://example.com"` only requests from "http://example.com" will be allowed.
|
|
||||||
- `"*"` for all domains to be allowed.
|
|
||||||
- `RegExp` - set `origin` to a regular expression pattern which will be used to test the request origin. If it's a match, the request origin will be reflected. For example the pattern `/example\.com$/` will reflect any request that is coming from an origin ending with "example.com".
|
|
||||||
- `Array` - set `origin` to an array of valid origins. Each origin can be a `String` or a `RegExp`. For example `["http://example1.com", /\.example2\.com$/]` will accept any request from "http://example1.com" or from a subdomain of "example2.com".
|
|
||||||
- `Function` - set `origin` to a function implementing some custom logic. The function takes the request origin as the first parameter and a callback (called as `callback(err, origin)`, where `origin` is a non-function value of the `origin` option) as the second.
|
|
||||||
* `methods`: Configures the **Access-Control-Allow-Methods** CORS header. Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex: `['GET', 'PUT', 'POST']`).
|
|
||||||
* `allowedHeaders`: Configures the **Access-Control-Allow-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Type,Authorization') or an array (ex: `['Content-Type', 'Authorization']`). If not specified, defaults to reflecting the headers specified in the request's **Access-Control-Request-Headers** header.
|
|
||||||
* `exposedHeaders`: Configures the **Access-Control-Expose-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Range,X-Content-Range') or an array (ex: `['Content-Range', 'X-Content-Range']`). If not specified, no custom headers are exposed.
|
|
||||||
* `credentials`: Configures the **Access-Control-Allow-Credentials** CORS header. Set to `true` to pass the header, otherwise it is omitted.
|
|
||||||
* `maxAge`: Configures the **Access-Control-Max-Age** CORS header. Set to an integer to pass the header, otherwise it is omitted.
|
|
||||||
* `preflightContinue`: Pass the CORS preflight response to the next handler.
|
|
||||||
* `optionsSuccessStatus`: Provides a status code to use for successful `OPTIONS` requests, since some legacy browsers (IE11, various SmartTVs) choke on `204`.
|
|
||||||
|
|
||||||
The default configuration is the equivalent of:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"origin": "*",
|
|
||||||
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
|
|
||||||
"preflightContinue": false,
|
|
||||||
"optionsSuccessStatus": 204
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Misconceptions
|
|
||||||
|
|
||||||
### "CORS blocks requests from disallowed origins"
|
|
||||||
|
|
||||||
**No.** Your server receives and processes every request. CORS headers tell the browser whether JavaScript can read the response—not whether the request is allowed.
|
|
||||||
|
|
||||||
### "CORS protects my API from unauthorized access"
|
|
||||||
|
|
||||||
**No.** CORS is not access control. Any HTTP client (curl, Postman, another server) can call your API regardless of CORS settings. Use authentication and authorization to protect your API.
|
|
||||||
|
|
||||||
### "Setting `origin: 'http://example.com'` means only that domain can access my server"
|
|
||||||
|
|
||||||
**No.** It means browsers will only let JavaScript from that origin read responses. The server still responds to all requests.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[MIT License](http://www.opensource.org/licenses/mit-license.php)
|
|
||||||
|
|
||||||
## Original Author
|
|
||||||
|
|
||||||
[Troy Goode](https://github.com/TroyGoode) ([troygoode@gmail.com](mailto:troygoode@gmail.com))
|
|
||||||
|
|
||||||
[coveralls-image]: https://img.shields.io/coveralls/expressjs/cors/master.svg
|
|
||||||
[coveralls-url]: https://coveralls.io/r/expressjs/cors?branch=master
|
|
||||||
[downloads-image]: https://img.shields.io/npm/dm/cors.svg
|
|
||||||
[downloads-url]: https://npmjs.com/package/cors
|
|
||||||
[github-actions-ci-image]: https://img.shields.io/github/actions/workflow/status/expressjs/cors/ci.yml?branch=master&label=ci
|
|
||||||
[github-actions-ci-url]: https://github.com/expressjs/cors?query=workflow%3Aci
|
|
||||||
[npm-image]: https://img.shields.io/npm/v/cors.svg
|
|
||||||
[npm-url]: https://npmjs.com/package/cors
|
|
||||||
-238
@@ -1,238 +0,0 @@
|
|||||||
(function () {
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var assign = require('object-assign');
|
|
||||||
var vary = require('vary');
|
|
||||||
|
|
||||||
var defaults = {
|
|
||||||
origin: '*',
|
|
||||||
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
|
||||||
preflightContinue: false,
|
|
||||||
optionsSuccessStatus: 204
|
|
||||||
};
|
|
||||||
|
|
||||||
function isString(s) {
|
|
||||||
return typeof s === 'string' || s instanceof String;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isOriginAllowed(origin, allowedOrigin) {
|
|
||||||
if (Array.isArray(allowedOrigin)) {
|
|
||||||
for (var i = 0; i < allowedOrigin.length; ++i) {
|
|
||||||
if (isOriginAllowed(origin, allowedOrigin[i])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else if (isString(allowedOrigin)) {
|
|
||||||
return origin === allowedOrigin;
|
|
||||||
} else if (allowedOrigin instanceof RegExp) {
|
|
||||||
return allowedOrigin.test(origin);
|
|
||||||
} else {
|
|
||||||
return !!allowedOrigin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function configureOrigin(options, req) {
|
|
||||||
var requestOrigin = req.headers.origin,
|
|
||||||
headers = [],
|
|
||||||
isAllowed;
|
|
||||||
|
|
||||||
if (!options.origin || options.origin === '*') {
|
|
||||||
// allow any origin
|
|
||||||
headers.push([{
|
|
||||||
key: 'Access-Control-Allow-Origin',
|
|
||||||
value: '*'
|
|
||||||
}]);
|
|
||||||
} else if (isString(options.origin)) {
|
|
||||||
// fixed origin
|
|
||||||
headers.push([{
|
|
||||||
key: 'Access-Control-Allow-Origin',
|
|
||||||
value: options.origin
|
|
||||||
}]);
|
|
||||||
headers.push([{
|
|
||||||
key: 'Vary',
|
|
||||||
value: 'Origin'
|
|
||||||
}]);
|
|
||||||
} else {
|
|
||||||
isAllowed = isOriginAllowed(requestOrigin, options.origin);
|
|
||||||
// reflect origin
|
|
||||||
headers.push([{
|
|
||||||
key: 'Access-Control-Allow-Origin',
|
|
||||||
value: isAllowed ? requestOrigin : false
|
|
||||||
}]);
|
|
||||||
headers.push([{
|
|
||||||
key: 'Vary',
|
|
||||||
value: 'Origin'
|
|
||||||
}]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
function configureMethods(options) {
|
|
||||||
var methods = options.methods;
|
|
||||||
if (methods.join) {
|
|
||||||
methods = options.methods.join(','); // .methods is an array, so turn it into a string
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
key: 'Access-Control-Allow-Methods',
|
|
||||||
value: methods
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function configureCredentials(options) {
|
|
||||||
if (options.credentials === true) {
|
|
||||||
return {
|
|
||||||
key: 'Access-Control-Allow-Credentials',
|
|
||||||
value: 'true'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function configureAllowedHeaders(options, req) {
|
|
||||||
var allowedHeaders = options.allowedHeaders || options.headers;
|
|
||||||
var headers = [];
|
|
||||||
|
|
||||||
if (!allowedHeaders) {
|
|
||||||
allowedHeaders = req.headers['access-control-request-headers']; // .headers wasn't specified, so reflect the request headers
|
|
||||||
headers.push([{
|
|
||||||
key: 'Vary',
|
|
||||||
value: 'Access-Control-Request-Headers'
|
|
||||||
}]);
|
|
||||||
} else if (allowedHeaders.join) {
|
|
||||||
allowedHeaders = allowedHeaders.join(','); // .headers is an array, so turn it into a string
|
|
||||||
}
|
|
||||||
if (allowedHeaders && allowedHeaders.length) {
|
|
||||||
headers.push([{
|
|
||||||
key: 'Access-Control-Allow-Headers',
|
|
||||||
value: allowedHeaders
|
|
||||||
}]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
function configureExposedHeaders(options) {
|
|
||||||
var headers = options.exposedHeaders;
|
|
||||||
if (!headers) {
|
|
||||||
return null;
|
|
||||||
} else if (headers.join) {
|
|
||||||
headers = headers.join(','); // .headers is an array, so turn it into a string
|
|
||||||
}
|
|
||||||
if (headers && headers.length) {
|
|
||||||
return {
|
|
||||||
key: 'Access-Control-Expose-Headers',
|
|
||||||
value: headers
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function configureMaxAge(options) {
|
|
||||||
var maxAge = (typeof options.maxAge === 'number' || options.maxAge) && options.maxAge.toString()
|
|
||||||
if (maxAge && maxAge.length) {
|
|
||||||
return {
|
|
||||||
key: 'Access-Control-Max-Age',
|
|
||||||
value: maxAge
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyHeaders(headers, res) {
|
|
||||||
for (var i = 0, n = headers.length; i < n; i++) {
|
|
||||||
var header = headers[i];
|
|
||||||
if (header) {
|
|
||||||
if (Array.isArray(header)) {
|
|
||||||
applyHeaders(header, res);
|
|
||||||
} else if (header.key === 'Vary' && header.value) {
|
|
||||||
vary(res, header.value);
|
|
||||||
} else if (header.value) {
|
|
||||||
res.setHeader(header.key, header.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function cors(options, req, res, next) {
|
|
||||||
var headers = [],
|
|
||||||
method = req.method && req.method.toUpperCase && req.method.toUpperCase();
|
|
||||||
|
|
||||||
if (method === 'OPTIONS') {
|
|
||||||
// preflight
|
|
||||||
headers.push(configureOrigin(options, req));
|
|
||||||
headers.push(configureCredentials(options))
|
|
||||||
headers.push(configureMethods(options))
|
|
||||||
headers.push(configureAllowedHeaders(options, req));
|
|
||||||
headers.push(configureMaxAge(options))
|
|
||||||
headers.push(configureExposedHeaders(options))
|
|
||||||
applyHeaders(headers, res);
|
|
||||||
|
|
||||||
if (options.preflightContinue) {
|
|
||||||
next();
|
|
||||||
} else {
|
|
||||||
// Safari (and potentially other browsers) need content-length 0,
|
|
||||||
// for 204 or they just hang waiting for a body
|
|
||||||
res.statusCode = options.optionsSuccessStatus;
|
|
||||||
res.setHeader('Content-Length', '0');
|
|
||||||
res.end();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// actual response
|
|
||||||
headers.push(configureOrigin(options, req));
|
|
||||||
headers.push(configureCredentials(options))
|
|
||||||
headers.push(configureExposedHeaders(options))
|
|
||||||
applyHeaders(headers, res);
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function middlewareWrapper(o) {
|
|
||||||
// if options are static (either via defaults or custom options passed in), wrap in a function
|
|
||||||
var optionsCallback = null;
|
|
||||||
if (typeof o === 'function') {
|
|
||||||
optionsCallback = o;
|
|
||||||
} else {
|
|
||||||
optionsCallback = function (req, cb) {
|
|
||||||
cb(null, o);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return function corsMiddleware(req, res, next) {
|
|
||||||
optionsCallback(req, function (err, options) {
|
|
||||||
if (err) {
|
|
||||||
next(err);
|
|
||||||
} else {
|
|
||||||
var corsOptions = assign({}, defaults, options);
|
|
||||||
var originCallback = null;
|
|
||||||
if (corsOptions.origin && typeof corsOptions.origin === 'function') {
|
|
||||||
originCallback = corsOptions.origin;
|
|
||||||
} else if (corsOptions.origin) {
|
|
||||||
originCallback = function (origin, cb) {
|
|
||||||
cb(null, corsOptions.origin);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (originCallback) {
|
|
||||||
originCallback(req.headers.origin, function (err2, origin) {
|
|
||||||
if (err2 || !origin) {
|
|
||||||
next(err2);
|
|
||||||
} else {
|
|
||||||
corsOptions.origin = origin;
|
|
||||||
cors(corsOptions, req, res, next);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// can pass either an options hash, an options delegate, or nothing
|
|
||||||
module.exports = middlewareWrapper;
|
|
||||||
|
|
||||||
}());
|
|
||||||
-42
@@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "cors",
|
|
||||||
"description": "Node.js CORS middleware",
|
|
||||||
"version": "2.8.6",
|
|
||||||
"author": "Troy Goode <troygoode@gmail.com> (https://github.com/troygoode/)",
|
|
||||||
"license": "MIT",
|
|
||||||
"keywords": [
|
|
||||||
"cors",
|
|
||||||
"express",
|
|
||||||
"connect",
|
|
||||||
"middleware"
|
|
||||||
],
|
|
||||||
"repository": "expressjs/cors",
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/express"
|
|
||||||
},
|
|
||||||
"main": "./lib/index.js",
|
|
||||||
"dependencies": {
|
|
||||||
"object-assign": "^4",
|
|
||||||
"vary": "^1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"after": "0.8.2",
|
|
||||||
"eslint": "7.30.0",
|
|
||||||
"express": "4.21.2",
|
|
||||||
"mocha": "9.2.2",
|
|
||||||
"nyc": "15.1.0",
|
|
||||||
"supertest": "6.1.3"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"lib/index.js"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.10"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "npm run lint && npm run test-ci",
|
|
||||||
"test-ci": "nyc --reporter=lcov --reporter=text mocha --require test/support/env",
|
|
||||||
"lint": "eslint lib test"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
repo_token: SIAeZjKYlHK74rbcFvNHMUzjRiMpflxve
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"no-console": 0,
|
|
||||||
"no-empty": [1, { "allowEmptyCatch": true }]
|
|
||||||
},
|
|
||||||
"extends": "eslint:recommended"
|
|
||||||
}
|
|
||||||
-9
@@ -1,9 +0,0 @@
|
|||||||
support
|
|
||||||
test
|
|
||||||
examples
|
|
||||||
example
|
|
||||||
*.sock
|
|
||||||
dist
|
|
||||||
yarn.lock
|
|
||||||
coverage
|
|
||||||
bower.json
|
|
||||||
-14
@@ -1,14 +0,0 @@
|
|||||||
|
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- "6"
|
|
||||||
- "5"
|
|
||||||
- "4"
|
|
||||||
|
|
||||||
install:
|
|
||||||
- make node_modules
|
|
||||||
|
|
||||||
script:
|
|
||||||
- make lint
|
|
||||||
- make test
|
|
||||||
- make coveralls
|
|
||||||
-362
@@ -1,362 +0,0 @@
|
|||||||
|
|
||||||
2.6.9 / 2017-09-22
|
|
||||||
==================
|
|
||||||
|
|
||||||
* remove ReDoS regexp in %o formatter (#504)
|
|
||||||
|
|
||||||
2.6.8 / 2017-05-18
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: Check for undefined on browser globals (#462, @marbemac)
|
|
||||||
|
|
||||||
2.6.7 / 2017-05-16
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: Update ms to 2.0.0 to fix regular expression denial of service vulnerability (#458, @hubdotcom)
|
|
||||||
* Fix: Inline extend function in node implementation (#452, @dougwilson)
|
|
||||||
* Docs: Fix typo (#455, @msasad)
|
|
||||||
|
|
||||||
2.6.5 / 2017-04-27
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: null reference check on window.documentElement.style.WebkitAppearance (#447, @thebigredgeek)
|
|
||||||
* Misc: clean up browser reference checks (#447, @thebigredgeek)
|
|
||||||
* Misc: add npm-debug.log to .gitignore (@thebigredgeek)
|
|
||||||
|
|
||||||
|
|
||||||
2.6.4 / 2017-04-20
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: bug that would occure if process.env.DEBUG is a non-string value. (#444, @LucianBuzzo)
|
|
||||||
* Chore: ignore bower.json in npm installations. (#437, @joaovieira)
|
|
||||||
* Misc: update "ms" to v0.7.3 (@tootallnate)
|
|
||||||
|
|
||||||
2.6.3 / 2017-03-13
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: Electron reference to `process.env.DEBUG` (#431, @paulcbetts)
|
|
||||||
* Docs: Changelog fix (@thebigredgeek)
|
|
||||||
|
|
||||||
2.6.2 / 2017-03-10
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: DEBUG_MAX_ARRAY_LENGTH (#420, @slavaGanzin)
|
|
||||||
* Docs: Add backers and sponsors from Open Collective (#422, @piamancini)
|
|
||||||
* Docs: Add Slackin invite badge (@tootallnate)
|
|
||||||
|
|
||||||
2.6.1 / 2017-02-10
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: Module's `export default` syntax fix for IE8 `Expected identifier` error
|
|
||||||
* Fix: Whitelist DEBUG_FD for values 1 and 2 only (#415, @pi0)
|
|
||||||
* Fix: IE8 "Expected identifier" error (#414, @vgoma)
|
|
||||||
* Fix: Namespaces would not disable once enabled (#409, @musikov)
|
|
||||||
|
|
||||||
2.6.0 / 2016-12-28
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: added better null pointer checks for browser useColors (@thebigredgeek)
|
|
||||||
* Improvement: removed explicit `window.debug` export (#404, @tootallnate)
|
|
||||||
* Improvement: deprecated `DEBUG_FD` environment variable (#405, @tootallnate)
|
|
||||||
|
|
||||||
2.5.2 / 2016-12-25
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: reference error on window within webworkers (#393, @KlausTrainer)
|
|
||||||
* Docs: fixed README typo (#391, @lurch)
|
|
||||||
* Docs: added notice about v3 api discussion (@thebigredgeek)
|
|
||||||
|
|
||||||
2.5.1 / 2016-12-20
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: babel-core compatibility
|
|
||||||
|
|
||||||
2.5.0 / 2016-12-20
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: wrong reference in bower file (@thebigredgeek)
|
|
||||||
* Fix: webworker compatibility (@thebigredgeek)
|
|
||||||
* Fix: output formatting issue (#388, @kribblo)
|
|
||||||
* Fix: babel-loader compatibility (#383, @escwald)
|
|
||||||
* Misc: removed built asset from repo and publications (@thebigredgeek)
|
|
||||||
* Misc: moved source files to /src (#378, @yamikuronue)
|
|
||||||
* Test: added karma integration and replaced babel with browserify for browser tests (#378, @yamikuronue)
|
|
||||||
* Test: coveralls integration (#378, @yamikuronue)
|
|
||||||
* Docs: simplified language in the opening paragraph (#373, @yamikuronue)
|
|
||||||
|
|
||||||
2.4.5 / 2016-12-17
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: `navigator` undefined in Rhino (#376, @jochenberger)
|
|
||||||
* Fix: custom log function (#379, @hsiliev)
|
|
||||||
* Improvement: bit of cleanup + linting fixes (@thebigredgeek)
|
|
||||||
* Improvement: rm non-maintainted `dist/` dir (#375, @freewil)
|
|
||||||
* Docs: simplified language in the opening paragraph. (#373, @yamikuronue)
|
|
||||||
|
|
||||||
2.4.4 / 2016-12-14
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: work around debug being loaded in preload scripts for electron (#368, @paulcbetts)
|
|
||||||
|
|
||||||
2.4.3 / 2016-12-14
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: navigation.userAgent error for react native (#364, @escwald)
|
|
||||||
|
|
||||||
2.4.2 / 2016-12-14
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: browser colors (#367, @tootallnate)
|
|
||||||
* Misc: travis ci integration (@thebigredgeek)
|
|
||||||
* Misc: added linting and testing boilerplate with sanity check (@thebigredgeek)
|
|
||||||
|
|
||||||
2.4.1 / 2016-12-13
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: typo that broke the package (#356)
|
|
||||||
|
|
||||||
2.4.0 / 2016-12-13
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: bower.json references unbuilt src entry point (#342, @justmatt)
|
|
||||||
* Fix: revert "handle regex special characters" (@tootallnate)
|
|
||||||
* Feature: configurable util.inspect()`options for NodeJS (#327, @tootallnate)
|
|
||||||
* Feature: %O`(big O) pretty-prints objects (#322, @tootallnate)
|
|
||||||
* Improvement: allow colors in workers (#335, @botverse)
|
|
||||||
* Improvement: use same color for same namespace. (#338, @lchenay)
|
|
||||||
|
|
||||||
2.3.3 / 2016-11-09
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: Catch `JSON.stringify()` errors (#195, Jovan Alleyne)
|
|
||||||
* Fix: Returning `localStorage` saved values (#331, Levi Thomason)
|
|
||||||
* Improvement: Don't create an empty object when no `process` (Nathan Rajlich)
|
|
||||||
|
|
||||||
2.3.2 / 2016-11-09
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: be super-safe in index.js as well (@TooTallNate)
|
|
||||||
* Fix: should check whether process exists (Tom Newby)
|
|
||||||
|
|
||||||
2.3.1 / 2016-11-09
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: Added electron compatibility (#324, @paulcbetts)
|
|
||||||
* Improvement: Added performance optimizations (@tootallnate)
|
|
||||||
* Readme: Corrected PowerShell environment variable example (#252, @gimre)
|
|
||||||
* Misc: Removed yarn lock file from source control (#321, @fengmk2)
|
|
||||||
|
|
||||||
2.3.0 / 2016-11-07
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Fix: Consistent placement of ms diff at end of output (#215, @gorangajic)
|
|
||||||
* Fix: Escaping of regex special characters in namespace strings (#250, @zacronos)
|
|
||||||
* Fix: Fixed bug causing crash on react-native (#282, @vkarpov15)
|
|
||||||
* Feature: Enabled ES6+ compatible import via default export (#212 @bucaran)
|
|
||||||
* Feature: Added %O formatter to reflect Chrome's console.log capability (#279, @oncletom)
|
|
||||||
* Package: Update "ms" to 0.7.2 (#315, @DevSide)
|
|
||||||
* Package: removed superfluous version property from bower.json (#207 @kkirsche)
|
|
||||||
* Readme: fix USE_COLORS to DEBUG_COLORS
|
|
||||||
* Readme: Doc fixes for format string sugar (#269, @mlucool)
|
|
||||||
* Readme: Updated docs for DEBUG_FD and DEBUG_COLORS environment variables (#232, @mattlyons0)
|
|
||||||
* Readme: doc fixes for PowerShell (#271 #243, @exoticknight @unreadable)
|
|
||||||
* Readme: better docs for browser support (#224, @matthewmueller)
|
|
||||||
* Tooling: Added yarn integration for development (#317, @thebigredgeek)
|
|
||||||
* Misc: Renamed History.md to CHANGELOG.md (@thebigredgeek)
|
|
||||||
* Misc: Added license file (#226 #274, @CantemoInternal @sdaitzman)
|
|
||||||
* Misc: Updated contributors (@thebigredgeek)
|
|
||||||
|
|
||||||
2.2.0 / 2015-05-09
|
|
||||||
==================
|
|
||||||
|
|
||||||
* package: update "ms" to v0.7.1 (#202, @dougwilson)
|
|
||||||
* README: add logging to file example (#193, @DanielOchoa)
|
|
||||||
* README: fixed a typo (#191, @amir-s)
|
|
||||||
* browser: expose `storage` (#190, @stephenmathieson)
|
|
||||||
* Makefile: add a `distclean` target (#189, @stephenmathieson)
|
|
||||||
|
|
||||||
2.1.3 / 2015-03-13
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Updated stdout/stderr example (#186)
|
|
||||||
* Updated example/stdout.js to match debug current behaviour
|
|
||||||
* Renamed example/stderr.js to stdout.js
|
|
||||||
* Update Readme.md (#184)
|
|
||||||
* replace high intensity foreground color for bold (#182, #183)
|
|
||||||
|
|
||||||
2.1.2 / 2015-03-01
|
|
||||||
==================
|
|
||||||
|
|
||||||
* dist: recompile
|
|
||||||
* update "ms" to v0.7.0
|
|
||||||
* package: update "browserify" to v9.0.3
|
|
||||||
* component: fix "ms.js" repo location
|
|
||||||
* changed bower package name
|
|
||||||
* updated documentation about using debug in a browser
|
|
||||||
* fix: security error on safari (#167, #168, @yields)
|
|
||||||
|
|
||||||
2.1.1 / 2014-12-29
|
|
||||||
==================
|
|
||||||
|
|
||||||
* browser: use `typeof` to check for `console` existence
|
|
||||||
* browser: check for `console.log` truthiness (fix IE 8/9)
|
|
||||||
* browser: add support for Chrome apps
|
|
||||||
* Readme: added Windows usage remarks
|
|
||||||
* Add `bower.json` to properly support bower install
|
|
||||||
|
|
||||||
2.1.0 / 2014-10-15
|
|
||||||
==================
|
|
||||||
|
|
||||||
* node: implement `DEBUG_FD` env variable support
|
|
||||||
* package: update "browserify" to v6.1.0
|
|
||||||
* package: add "license" field to package.json (#135, @panuhorsmalahti)
|
|
||||||
|
|
||||||
2.0.0 / 2014-09-01
|
|
||||||
==================
|
|
||||||
|
|
||||||
* package: update "browserify" to v5.11.0
|
|
||||||
* node: use stderr rather than stdout for logging (#29, @stephenmathieson)
|
|
||||||
|
|
||||||
1.0.4 / 2014-07-15
|
|
||||||
==================
|
|
||||||
|
|
||||||
* dist: recompile
|
|
||||||
* example: remove `console.info()` log usage
|
|
||||||
* example: add "Content-Type" UTF-8 header to browser example
|
|
||||||
* browser: place %c marker after the space character
|
|
||||||
* browser: reset the "content" color via `color: inherit`
|
|
||||||
* browser: add colors support for Firefox >= v31
|
|
||||||
* debug: prefer an instance `log()` function over the global one (#119)
|
|
||||||
* Readme: update documentation about styled console logs for FF v31 (#116, @wryk)
|
|
||||||
|
|
||||||
1.0.3 / 2014-07-09
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Add support for multiple wildcards in namespaces (#122, @seegno)
|
|
||||||
* browser: fix lint
|
|
||||||
|
|
||||||
1.0.2 / 2014-06-10
|
|
||||||
==================
|
|
||||||
|
|
||||||
* browser: update color palette (#113, @gscottolson)
|
|
||||||
* common: make console logging function configurable (#108, @timoxley)
|
|
||||||
* node: fix %o colors on old node <= 0.8.x
|
|
||||||
* Makefile: find node path using shell/which (#109, @timoxley)
|
|
||||||
|
|
||||||
1.0.1 / 2014-06-06
|
|
||||||
==================
|
|
||||||
|
|
||||||
* browser: use `removeItem()` to clear localStorage
|
|
||||||
* browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777)
|
|
||||||
* package: add "contributors" section
|
|
||||||
* node: fix comment typo
|
|
||||||
* README: list authors
|
|
||||||
|
|
||||||
1.0.0 / 2014-06-04
|
|
||||||
==================
|
|
||||||
|
|
||||||
* make ms diff be global, not be scope
|
|
||||||
* debug: ignore empty strings in enable()
|
|
||||||
* node: make DEBUG_COLORS able to disable coloring
|
|
||||||
* *: export the `colors` array
|
|
||||||
* npmignore: don't publish the `dist` dir
|
|
||||||
* Makefile: refactor to use browserify
|
|
||||||
* package: add "browserify" as a dev dependency
|
|
||||||
* Readme: add Web Inspector Colors section
|
|
||||||
* node: reset terminal color for the debug content
|
|
||||||
* node: map "%o" to `util.inspect()`
|
|
||||||
* browser: map "%j" to `JSON.stringify()`
|
|
||||||
* debug: add custom "formatters"
|
|
||||||
* debug: use "ms" module for humanizing the diff
|
|
||||||
* Readme: add "bash" syntax highlighting
|
|
||||||
* browser: add Firebug color support
|
|
||||||
* browser: add colors for WebKit browsers
|
|
||||||
* node: apply log to `console`
|
|
||||||
* rewrite: abstract common logic for Node & browsers
|
|
||||||
* add .jshintrc file
|
|
||||||
|
|
||||||
0.8.1 / 2014-04-14
|
|
||||||
==================
|
|
||||||
|
|
||||||
* package: re-add the "component" section
|
|
||||||
|
|
||||||
0.8.0 / 2014-03-30
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add `enable()` method for nodejs. Closes #27
|
|
||||||
* change from stderr to stdout
|
|
||||||
* remove unnecessary index.js file
|
|
||||||
|
|
||||||
0.7.4 / 2013-11-13
|
|
||||||
==================
|
|
||||||
|
|
||||||
* remove "browserify" key from package.json (fixes something in browserify)
|
|
||||||
|
|
||||||
0.7.3 / 2013-10-30
|
|
||||||
==================
|
|
||||||
|
|
||||||
* fix: catch localStorage security error when cookies are blocked (Chrome)
|
|
||||||
* add debug(err) support. Closes #46
|
|
||||||
* add .browser prop to package.json. Closes #42
|
|
||||||
|
|
||||||
0.7.2 / 2013-02-06
|
|
||||||
==================
|
|
||||||
|
|
||||||
* fix package.json
|
|
||||||
* fix: Mobile Safari (private mode) is broken with debug
|
|
||||||
* fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript
|
|
||||||
|
|
||||||
0.7.1 / 2013-02-05
|
|
||||||
==================
|
|
||||||
|
|
||||||
* add repository URL to package.json
|
|
||||||
* add DEBUG_COLORED to force colored output
|
|
||||||
* add browserify support
|
|
||||||
* fix component. Closes #24
|
|
||||||
|
|
||||||
0.7.0 / 2012-05-04
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Added .component to package.json
|
|
||||||
* Added debug.component.js build
|
|
||||||
|
|
||||||
0.6.0 / 2012-03-16
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Added support for "-" prefix in DEBUG [Vinay Pulim]
|
|
||||||
* Added `.enabled` flag to the node version [TooTallNate]
|
|
||||||
|
|
||||||
0.5.0 / 2012-02-02
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Added: humanize diffs. Closes #8
|
|
||||||
* Added `debug.disable()` to the CS variant
|
|
||||||
* Removed padding. Closes #10
|
|
||||||
* Fixed: persist client-side variant again. Closes #9
|
|
||||||
|
|
||||||
0.4.0 / 2012-02-01
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Added browser variant support for older browsers [TooTallNate]
|
|
||||||
* Added `debug.enable('project:*')` to browser variant [TooTallNate]
|
|
||||||
* Added padding to diff (moved it to the right)
|
|
||||||
|
|
||||||
0.3.0 / 2012-01-26
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Added millisecond diff when isatty, otherwise UTC string
|
|
||||||
|
|
||||||
0.2.0 / 2012-01-22
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Added wildcard support
|
|
||||||
|
|
||||||
0.1.0 / 2011-12-02
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Added: remove colors unless stderr isatty [TooTallNate]
|
|
||||||
|
|
||||||
0.0.1 / 2010-01-03
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Initial release
|
|
||||||
Loaded 100 of 740 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user