Compare commits
1
Commits
master
..
5292833b02
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5292833b02 |
-10
@@ -1,10 +0,0 @@
|
|||||||
node_modules/
|
|
||||||
*.db
|
|
||||||
*.db-wal
|
|
||||||
*.db-shm
|
|
||||||
.env
|
|
||||||
*.bak
|
|
||||||
*.backup
|
|
||||||
server/data.db
|
|
||||||
server/data.db-wal
|
|
||||||
server/data.db-shm
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
# 🏰 Der letzte Auftrag – AI-Befehle
|
|
||||||
|
|
||||||
## Projektbeschreibung
|
|
||||||
|
|
||||||
"Der letzte Auftrag" ist ein interaktives Brautabschied-Spiel (Heldenspiel/Drill) als Web-App.
|
|
||||||
Der Bräutigam (der "Held") muss 8 Quests absolvieren, um alle Besitztümer für die Braut zu erbeuten.
|
|
||||||
|
|
||||||
## Architektur
|
|
||||||
|
|
||||||
- **Frontend:** Single-page HTML (CSS + Vanilla JS), keine Frameworks
|
|
||||||
- **Backend:** Node.js/Express auf Port 8082
|
|
||||||
- **Datenbank:** SQLite (better-sqlite3), Dateiname `data.db`
|
|
||||||
- **Deployment:** systemd Service auf 192.168.0.187, nginx Reverse Proxy
|
|
||||||
|
|
||||||
## API-Endpunkte
|
|
||||||
|
|
||||||
### Game State
|
|
||||||
```
|
|
||||||
GET /api/game – Full game state + spieler + notes + stats
|
|
||||||
POST /api/game/start – {"name": "Spielname"} – Spiel starten
|
|
||||||
POST /api/game/reset – Alles zurücksetzen
|
|
||||||
POST /api/game/save-state – {"items":[...], "drinks": N, "quests":[...]} – Zustand speichern
|
|
||||||
POST /api/game/drink – {"amount": ±N} – Trinken hinzufügen/abziehen
|
|
||||||
POST /api/game/drink/reset – Null trinken
|
|
||||||
POST /api/game/toggle-item – {"index": 0-7} – Inventar-Toggle
|
|
||||||
POST /api/game/toggle-quest – {"index": 0-7} – Quest-Toggle
|
|
||||||
```
|
|
||||||
|
|
||||||
### Spieler (Participants)
|
|
||||||
```
|
|
||||||
GET /api/spieler – Alle Spieler
|
|
||||||
POST /api/spieler – {"name": "...","rolle": "...","quest_id": N|null}
|
|
||||||
PUT /api/spieler/:id – {"rolle": "...","quest_id": N|null}
|
|
||||||
DELETE /api/spieler/:id – Spieler entfernen
|
|
||||||
```
|
|
||||||
|
|
||||||
### Notes
|
|
||||||
```
|
|
||||||
GET /api/game (im state.notes) – Alle Notizen
|
|
||||||
POST /api/notes – {"body": "Text"}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stats
|
|
||||||
```
|
|
||||||
GET /api/game/stats – {collected, total, questsDone, questsTotal, drinks, gameName, spielerCount, spieler}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Rolen-System
|
|
||||||
|
|
||||||
| Rolle | Code | Bedeutung |
|
|
||||||
|-------|------|-----------|
|
|
||||||
| Held | `held` | Der Bräutigam – löst alle Quests |
|
|
||||||
| Spielleiter | `spielleiter` | Organisator, kennt alle Aufgaben |
|
|
||||||
| Hüter | `hüter` | Bewacht ein Besitztum |
|
|
||||||
| Ratsmitglied | `ratsmitglied` | Erfahrener RPG-Spieler |
|
|
||||||
| Einsteiger | `einsteiger` | Tabletop-Neuling |
|
|
||||||
| Gast | `gast` | Schaut zu, trinkt mit |
|
|
||||||
|
|
||||||
## Quest-System
|
|
||||||
|
|
||||||
8 Quests mit 3 Schwierigkeitsstufen:
|
|
||||||
- **EINSTEIGER** (0-3): Brief, Ring, Schlüssel, Blume
|
|
||||||
- **MITTEL** (4-5): Foto, Schuh
|
|
||||||
- **HERAUSFORDERUNG** (6-7): Maskottchen, Urkunde
|
|
||||||
|
|
||||||
Jede Quest hat: id, badge, badgeLabel, name, holder, item, desc, success, fail, details
|
|
||||||
|
|
||||||
## Frontend-Struktur
|
|
||||||
|
|
||||||
```
|
|
||||||
index.html (Single File)
|
|
||||||
├── Lines 1-1257: HTML-Struktur + CSS (NICHT ÄNDERN!)
|
|
||||||
└── Lines 1258+: JavaScript (API-basiert)
|
|
||||||
├── PART 1-2: API-Helpers, State-Sync, Quest-Daten
|
|
||||||
├── PART 3-6: UI-Funktionen (Stars, Tabs, Quests, Items, Drinks, Status)
|
|
||||||
├── PART 7: Würfelfunktionen (W4, W6, W8, W10, W12, W20, W100)
|
|
||||||
└── PART 8: Spieler-Management (API-Version)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Backend-Struktur
|
|
||||||
|
|
||||||
```
|
|
||||||
server/
|
|
||||||
├── server.js – Express-Server mit SQLite-Backend
|
|
||||||
├── package.json – Abhängigkeiten (express, better-sqlite3)
|
|
||||||
├── data.db – SQLite-Datenbank (wird erstellt)
|
|
||||||
└── node_modules/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Datenbank-Schema
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE TABLE state (key TEXT PRIMARY KEY, value TEXT);
|
|
||||||
CREATE TABLE spieler (id INTEGER PRIMARY KEY, name TEXT, rolle TEXT, quest_id INTEGER, created_at DATETIME);
|
|
||||||
CREATE TABLE notes (id INTEGER PRIMARY KEY, body TEXT, created_at DATETIME);
|
|
||||||
CREATE TABLE drinks_log (id INTEGER PRIMARY KEY, amount INTEGER, note TEXT, created_at DATETIME);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Deployment
|
|
||||||
|
|
||||||
### systemd Service
|
|
||||||
```ini
|
|
||||||
[Unit]
|
|
||||||
Description=Der letzte Auftrag Server
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=www-data
|
|
||||||
WorkingDirectory=/opt/der_letzte_auftrag/server
|
|
||||||
ExecStart=/usr/bin/node server.js
|
|
||||||
Restart=on-failure
|
|
||||||
Environment=NODE_ENV=production
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
```
|
|
||||||
|
|
||||||
### Nginx Konfiguration
|
|
||||||
```nginx
|
|
||||||
location / {
|
|
||||||
proxy_pass http://127.0.0.1:8082;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_read_timeout 30s;
|
|
||||||
proxy_send_timeout 30s;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## AI-Anweisungen
|
|
||||||
|
|
||||||
### Bei Änderungen am Frontend:
|
|
||||||
1. ÄNDERE NIE die HTML-Struktur (Zeilen 1-1257)
|
|
||||||
2. ÄNDERE NIE das CSS
|
|
||||||
3. Nutze IMMER die API-Helpers (apiGet/apiPost/apiPut/apiDelete)
|
|
||||||
4. Lokal State nach API-Call: syncState() und refreshUI()
|
|
||||||
5. Keine localStorage mehr – alles geht über die API
|
|
||||||
|
|
||||||
### Bei Änderungen am Backend:
|
|
||||||
1. Neue Endpunkte: Immer PUT für Updates, DELETE für Löschungen, POST für Creates
|
|
||||||
2. Datenbanken: Immer `initState()` aufrufen vor state-Leseoperationen
|
|
||||||
3. Fehlerbehandlung: Immer auf null/undefined prüfen
|
|
||||||
4. WAL-Modus aktiviert für bessere Concurrent-Performance
|
|
||||||
|
|
||||||
### Deployment-Schritte:
|
|
||||||
1. `git push origin main`
|
|
||||||
2. SSH zum Server (192.168.0.187): `git pull`
|
|
||||||
3. `cd server && npm install`
|
|
||||||
4. `systemctl restart der-letzte-auftrag`
|
|
||||||
5. Test: `curl http://localhost:8082/api/game`
|
|
||||||
-1642
File diff suppressed because it is too large
Load Diff
-1
@@ -1 +0,0 @@
|
|||||||
../mime/cli.js
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
../prebuild-install/bin.js
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
../rc/cli.js
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
../semver/bin/semver.js
|
|
||||||
-1258
File diff suppressed because it is too large
Load Diff
-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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Jameson Little
|
|
||||||
|
|
||||||
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.
|
|
||||||
-34
@@ -1,34 +0,0 @@
|
|||||||
base64-js
|
|
||||||
=========
|
|
||||||
|
|
||||||
`base64-js` does basic base64 encoding/decoding in pure JS.
|
|
||||||
|
|
||||||
[](http://travis-ci.org/beatgammit/base64-js)
|
|
||||||
|
|
||||||
Many browsers already have base64 encoding/decoding functionality, but it is for text data, not all-purpose binary data.
|
|
||||||
|
|
||||||
Sometimes encoding/decoding binary data in the browser is useful, and that is what this module does.
|
|
||||||
|
|
||||||
## install
|
|
||||||
|
|
||||||
With [npm](https://npmjs.org) do:
|
|
||||||
|
|
||||||
`npm install base64-js` and `var base64js = require('base64-js')`
|
|
||||||
|
|
||||||
For use in web browsers do:
|
|
||||||
|
|
||||||
`<script src="base64js.min.js"></script>`
|
|
||||||
|
|
||||||
[Get supported base64-js with the Tidelift Subscription](https://tidelift.com/subscription/pkg/npm-base64-js?utm_source=npm-base64-js&utm_medium=referral&utm_campaign=readme)
|
|
||||||
|
|
||||||
## methods
|
|
||||||
|
|
||||||
`base64js` has three exposed functions, `byteLength`, `toByteArray` and `fromByteArray`, which both take a single argument.
|
|
||||||
|
|
||||||
* `byteLength` - Takes a base64 string and returns length of byte array
|
|
||||||
* `toByteArray` - Takes a base64 string and returns a byte array
|
|
||||||
* `fromByteArray` - Takes a byte array and returns a base64 string
|
|
||||||
|
|
||||||
## license
|
|
||||||
|
|
||||||
MIT
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
(function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"==typeof window?"undefined"==typeof global?"undefined"==typeof self?this:self:global:window,b.base64js=a()}})(function(){return function(){function b(d,e,g){function a(j,i){if(!e[j]){if(!d[j]){var f="function"==typeof require&&require;if(!i&&f)return f(j,!0);if(h)return h(j,!0);var c=new Error("Cannot find module '"+j+"'");throw c.code="MODULE_NOT_FOUND",c}var k=e[j]={exports:{}};d[j][0].call(k.exports,function(b){var c=d[j][1][b];return a(c||b)},k,k.exports,b,d,e,g)}return e[j].exports}for(var h="function"==typeof require&&require,c=0;c<g.length;c++)a(g[c]);return a}return b}()({"/":[function(a,b,c){'use strict';function d(a){var b=a.length;if(0<b%4)throw new Error("Invalid string. Length must be a multiple of 4");var c=a.indexOf("=");-1===c&&(c=b);var d=c===b?0:4-c%4;return[c,d]}function e(a,b,c){return 3*(b+c)/4-c}function f(a){var b,c,f=d(a),g=f[0],h=f[1],j=new m(e(a,g,h)),k=0,n=0<h?g-4:g;for(c=0;c<n;c+=4)b=l[a.charCodeAt(c)]<<18|l[a.charCodeAt(c+1)]<<12|l[a.charCodeAt(c+2)]<<6|l[a.charCodeAt(c+3)],j[k++]=255&b>>16,j[k++]=255&b>>8,j[k++]=255&b;return 2===h&&(b=l[a.charCodeAt(c)]<<2|l[a.charCodeAt(c+1)]>>4,j[k++]=255&b),1===h&&(b=l[a.charCodeAt(c)]<<10|l[a.charCodeAt(c+1)]<<4|l[a.charCodeAt(c+2)]>>2,j[k++]=255&b>>8,j[k++]=255&b),j}function g(a){return k[63&a>>18]+k[63&a>>12]+k[63&a>>6]+k[63&a]}function h(a,b,c){for(var d,e=[],f=b;f<c;f+=3)d=(16711680&a[f]<<16)+(65280&a[f+1]<<8)+(255&a[f+2]),e.push(g(d));return e.join("")}function j(a){for(var b,c=a.length,d=c%3,e=[],f=16383,g=0,j=c-d;g<j;g+=f)e.push(h(a,g,g+f>j?j:g+f));return 1===d?(b=a[c-1],e.push(k[b>>2]+k[63&b<<4]+"==")):2===d&&(b=(a[c-2]<<8)+a[c-1],e.push(k[b>>10]+k[63&b>>4]+k[63&b<<2]+"=")),e.join("")}c.byteLength=function(a){var b=d(a),c=b[0],e=b[1];return 3*(c+e)/4-e},c.toByteArray=f,c.fromByteArray=j;for(var k=[],l=[],m="undefined"==typeof Uint8Array?Array:Uint8Array,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",o=0,p=n.length;o<p;++o)k[o]=n[o],l[n.charCodeAt(o)]=o;l[45]=62,l[95]=63},{}]},{},[])("/")});
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
export function byteLength(b64: string): number;
|
|
||||||
export function toByteArray(b64: string): Uint8Array;
|
|
||||||
export function fromByteArray(uint8: Uint8Array): string;
|
|
||||||
-150
@@ -1,150 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
exports.byteLength = byteLength
|
|
||||||
exports.toByteArray = toByteArray
|
|
||||||
exports.fromByteArray = fromByteArray
|
|
||||||
|
|
||||||
var lookup = []
|
|
||||||
var revLookup = []
|
|
||||||
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
|
|
||||||
|
|
||||||
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
|
||||||
for (var i = 0, len = code.length; i < len; ++i) {
|
|
||||||
lookup[i] = code[i]
|
|
||||||
revLookup[code.charCodeAt(i)] = i
|
|
||||||
}
|
|
||||||
|
|
||||||
// Support decoding URL-safe base64 strings, as Node.js does.
|
|
||||||
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
|
|
||||||
revLookup['-'.charCodeAt(0)] = 62
|
|
||||||
revLookup['_'.charCodeAt(0)] = 63
|
|
||||||
|
|
||||||
function getLens (b64) {
|
|
||||||
var len = b64.length
|
|
||||||
|
|
||||||
if (len % 4 > 0) {
|
|
||||||
throw new Error('Invalid string. Length must be a multiple of 4')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim off extra bytes after placeholder bytes are found
|
|
||||||
// See: https://github.com/beatgammit/base64-js/issues/42
|
|
||||||
var validLen = b64.indexOf('=')
|
|
||||||
if (validLen === -1) validLen = len
|
|
||||||
|
|
||||||
var placeHoldersLen = validLen === len
|
|
||||||
? 0
|
|
||||||
: 4 - (validLen % 4)
|
|
||||||
|
|
||||||
return [validLen, placeHoldersLen]
|
|
||||||
}
|
|
||||||
|
|
||||||
// base64 is 4/3 + up to two characters of the original data
|
|
||||||
function byteLength (b64) {
|
|
||||||
var lens = getLens(b64)
|
|
||||||
var validLen = lens[0]
|
|
||||||
var placeHoldersLen = lens[1]
|
|
||||||
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
|
|
||||||
}
|
|
||||||
|
|
||||||
function _byteLength (b64, validLen, placeHoldersLen) {
|
|
||||||
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
|
|
||||||
}
|
|
||||||
|
|
||||||
function toByteArray (b64) {
|
|
||||||
var tmp
|
|
||||||
var lens = getLens(b64)
|
|
||||||
var validLen = lens[0]
|
|
||||||
var placeHoldersLen = lens[1]
|
|
||||||
|
|
||||||
var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))
|
|
||||||
|
|
||||||
var curByte = 0
|
|
||||||
|
|
||||||
// if there are placeholders, only get up to the last complete 4 chars
|
|
||||||
var len = placeHoldersLen > 0
|
|
||||||
? validLen - 4
|
|
||||||
: validLen
|
|
||||||
|
|
||||||
var i
|
|
||||||
for (i = 0; i < len; i += 4) {
|
|
||||||
tmp =
|
|
||||||
(revLookup[b64.charCodeAt(i)] << 18) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 1)] << 12) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 2)] << 6) |
|
|
||||||
revLookup[b64.charCodeAt(i + 3)]
|
|
||||||
arr[curByte++] = (tmp >> 16) & 0xFF
|
|
||||||
arr[curByte++] = (tmp >> 8) & 0xFF
|
|
||||||
arr[curByte++] = tmp & 0xFF
|
|
||||||
}
|
|
||||||
|
|
||||||
if (placeHoldersLen === 2) {
|
|
||||||
tmp =
|
|
||||||
(revLookup[b64.charCodeAt(i)] << 2) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 1)] >> 4)
|
|
||||||
arr[curByte++] = tmp & 0xFF
|
|
||||||
}
|
|
||||||
|
|
||||||
if (placeHoldersLen === 1) {
|
|
||||||
tmp =
|
|
||||||
(revLookup[b64.charCodeAt(i)] << 10) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 1)] << 4) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 2)] >> 2)
|
|
||||||
arr[curByte++] = (tmp >> 8) & 0xFF
|
|
||||||
arr[curByte++] = tmp & 0xFF
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
function tripletToBase64 (num) {
|
|
||||||
return lookup[num >> 18 & 0x3F] +
|
|
||||||
lookup[num >> 12 & 0x3F] +
|
|
||||||
lookup[num >> 6 & 0x3F] +
|
|
||||||
lookup[num & 0x3F]
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeChunk (uint8, start, end) {
|
|
||||||
var tmp
|
|
||||||
var output = []
|
|
||||||
for (var i = start; i < end; i += 3) {
|
|
||||||
tmp =
|
|
||||||
((uint8[i] << 16) & 0xFF0000) +
|
|
||||||
((uint8[i + 1] << 8) & 0xFF00) +
|
|
||||||
(uint8[i + 2] & 0xFF)
|
|
||||||
output.push(tripletToBase64(tmp))
|
|
||||||
}
|
|
||||||
return output.join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromByteArray (uint8) {
|
|
||||||
var tmp
|
|
||||||
var len = uint8.length
|
|
||||||
var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
|
|
||||||
var parts = []
|
|
||||||
var maxChunkLength = 16383 // must be multiple of 3
|
|
||||||
|
|
||||||
// go through the array every three bytes, we'll deal with trailing stuff later
|
|
||||||
for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
|
||||||
parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
||||||
if (extraBytes === 1) {
|
|
||||||
tmp = uint8[len - 1]
|
|
||||||
parts.push(
|
|
||||||
lookup[tmp >> 2] +
|
|
||||||
lookup[(tmp << 4) & 0x3F] +
|
|
||||||
'=='
|
|
||||||
)
|
|
||||||
} else if (extraBytes === 2) {
|
|
||||||
tmp = (uint8[len - 2] << 8) + uint8[len - 1]
|
|
||||||
parts.push(
|
|
||||||
lookup[tmp >> 10] +
|
|
||||||
lookup[(tmp >> 4) & 0x3F] +
|
|
||||||
lookup[(tmp << 2) & 0x3F] +
|
|
||||||
'='
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts.join('')
|
|
||||||
}
|
|
||||||
-47
@@ -1,47 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "base64-js",
|
|
||||||
"description": "Base64 encoding/decoding in pure JS",
|
|
||||||
"version": "1.5.1",
|
|
||||||
"author": "T. Jameson Little <t.jameson.little@gmail.com>",
|
|
||||||
"typings": "index.d.ts",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/beatgammit/base64-js/issues"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"babel-minify": "^0.5.1",
|
|
||||||
"benchmark": "^2.1.4",
|
|
||||||
"browserify": "^16.3.0",
|
|
||||||
"standard": "*",
|
|
||||||
"tape": "4.x"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/beatgammit/base64-js",
|
|
||||||
"keywords": [
|
|
||||||
"base64"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"main": "index.js",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git://github.com/beatgammit/base64-js.git"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"build": "browserify -s base64js -r ./ | minify > base64js.min.js",
|
|
||||||
"lint": "standard",
|
|
||||||
"test": "npm run lint && npm run unit",
|
|
||||||
"unit": "tape test/*.js"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/feross"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "patreon",
|
|
||||||
"url": "https://www.patreon.com/feross"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "consulting",
|
|
||||||
"url": "https://feross.org/support"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2017 Joshua Wise
|
|
||||||
|
|
||||||
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.
|
|
||||||
-99
@@ -1,99 +0,0 @@
|
|||||||
# better-sqlite3 [](https://github.com/JoshuaWise/better-sqlite3/actions/workflows/build.yml?query=branch%3Amaster)
|
|
||||||
|
|
||||||
The fastest and simplest library for SQLite in Node.js.
|
|
||||||
|
|
||||||
- Full transaction support
|
|
||||||
- High performance, efficiency, and safety
|
|
||||||
- Easy-to-use synchronous API *(better concurrency than an asynchronous API... yes, you read that correctly)*
|
|
||||||
- Support for user-defined functions, aggregates, virtual tables, and extensions
|
|
||||||
- 64-bit integers *(invisible until you need them)*
|
|
||||||
- Worker thread support *(for large/slow queries)*
|
|
||||||
|
|
||||||
## Help this project stay strong! 💪
|
|
||||||
|
|
||||||
`better-sqlite3` is used by thousands of developers and engineers on a daily basis. Long nights and weekends were spent keeping this project strong and dependable, with no ask for compensation or funding, until now. If your company uses `better-sqlite3`, ask your manager to consider supporting the project:
|
|
||||||
|
|
||||||
- [Become a GitHub sponsor](https://github.com/sponsors/JoshuaWise)
|
|
||||||
- [Become a backer on Patreon](https://www.patreon.com/joshuawise)
|
|
||||||
- [Make a one-time donation on PayPal](https://www.paypal.me/joshuathomaswise)
|
|
||||||
|
|
||||||
## How other libraries compare
|
|
||||||
|
|
||||||
| |select 1 row `get()` |select 100 rows `all()` |select 100 rows `iterate()` 1-by-1|insert 1 row `run()`|insert 100 rows in a transaction|
|
|
||||||
|---|---|---|---|---|---|
|
|
||||||
|better-sqlite3|1x|1x|1x|1x|1x|
|
|
||||||
|[sqlite](https://www.npmjs.com/package/sqlite) and [sqlite3](https://www.npmjs.com/package/sqlite3)|11.7x slower|2.9x slower|24.4x slower|2.8x slower|15.6x slower|
|
|
||||||
|
|
||||||
> You can verify these results by [running the benchmark yourself](./docs/benchmark.md).
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install better-sqlite3
|
|
||||||
```
|
|
||||||
|
|
||||||
> Requires Node.js v14.21.1 or later. Prebuilt binaries are available for [LTS versions](https://nodejs.org/en/about/releases/). If you have trouble installing, check the [troubleshooting guide](./docs/troubleshooting.md).
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```js
|
|
||||||
const db = require('better-sqlite3')('foobar.db', options);
|
|
||||||
|
|
||||||
const row = db.prepare('SELECT * FROM users WHERE id = ?').get(userId);
|
|
||||||
console.log(row.firstName, row.lastName, row.email);
|
|
||||||
```
|
|
||||||
|
|
||||||
Though not required, [it is generally important to set the WAL pragma for performance reasons](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/performance.md).
|
|
||||||
|
|
||||||
```js
|
|
||||||
db.pragma('journal_mode = WAL');
|
|
||||||
```
|
|
||||||
|
|
||||||
##### In ES6 module notation:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import Database from 'better-sqlite3';
|
|
||||||
const db = new Database('foobar.db', options);
|
|
||||||
db.pragma('journal_mode = WAL');
|
|
||||||
```
|
|
||||||
|
|
||||||
## Why should I use this instead of [node-sqlite3](https://github.com/mapbox/node-sqlite3)?
|
|
||||||
|
|
||||||
- `node-sqlite3` uses asynchronous APIs for tasks that are either CPU-bound or serialized. That's not only bad design, but it wastes tons of resources. It also causes [mutex thrashing](https://en.wikipedia.org/wiki/Resource_contention) which has devastating effects on performance.
|
|
||||||
- `node-sqlite3` exposes low-level (C language) memory management functions. `better-sqlite3` does it the JavaScript way, allowing the garbage collector to worry about memory management.
|
|
||||||
- `better-sqlite3` is simpler to use, and it provides nice utilities for some operations that are very difficult or impossible in `node-sqlite3`.
|
|
||||||
- `better-sqlite3` is much faster than `node-sqlite3` in most cases, and just as fast in all other cases.
|
|
||||||
|
|
||||||
#### When is this library not appropriate?
|
|
||||||
|
|
||||||
In most cases, if you're attempting something that cannot be reasonably accomplished with `better-sqlite3`, it probably cannot be reasonably accomplished with SQLite in general. For example, if you're executing queries that take one second to complete, and you expect to have many concurrent users executing those queries, no amount of asynchronicity will save you from SQLite's serialized nature. Fortunately, SQLite is very *very* fast. With proper indexing, we've been able to achieve upward of 2000 queries per second with 5-way-joins in a 60 GB database, where each query was handling 5–50 kilobytes of real data.
|
|
||||||
|
|
||||||
If you have a performance problem, the most likely causes are inefficient queries, improper indexing, or a lack of [WAL mode](./docs/performance.md)—not `better-sqlite3` itself. However, there are some cases where `better-sqlite3` could be inappropriate:
|
|
||||||
|
|
||||||
- If you expect a high volume of concurrent reads each returning many megabytes of data (i.e., videos)
|
|
||||||
- If you expect a high volume of concurrent writes (i.e., a social media site)
|
|
||||||
- If your database's size is near the terabyte range
|
|
||||||
|
|
||||||
For these situations, you should probably use a full-fledged RDBMS such as [PostgreSQL](https://www.postgresql.org/).
|
|
||||||
|
|
||||||
## Upgrading
|
|
||||||
|
|
||||||
Upgrading your `better-sqlite3` dependency can potentially introduce breaking changes, either in the `better-sqlite3` API (if you upgrade to a new [major version](https://semver.org/)), or between your existing database(s) and the underlying version of SQLite. Before upgrading, review:
|
|
||||||
|
|
||||||
* [`better-sqlite3` release notes](https://github.com/WiseLibs/better-sqlite3/releases)
|
|
||||||
* [SQLite release history](https://www.sqlite.org/changes.html)
|
|
||||||
|
|
||||||
# Documentation
|
|
||||||
|
|
||||||
- [API documentation](./docs/api.md)
|
|
||||||
- [Performance](./docs/performance.md) (also see [benchmark results](./docs/benchmark.md))
|
|
||||||
- [64-bit integer support](./docs/integer.md)
|
|
||||||
- [Worker thread support](./docs/threads.md)
|
|
||||||
- [Unsafe mode (advanced)](./docs/unsafe.md)
|
|
||||||
- [SQLite compilation (advanced)](./docs/compilation.md)
|
|
||||||
- [Contribution rules](./docs/contribution.md)
|
|
||||||
- [Code of conduct](./docs/conduct.md)
|
|
||||||
|
|
||||||
# License
|
|
||||||
|
|
||||||
[MIT](./LICENSE)
|
|
||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
# ===
|
|
||||||
# This is the main GYP file, which builds better-sqlite3 with SQLite itself.
|
|
||||||
# ===
|
|
||||||
|
|
||||||
{
|
|
||||||
'includes': ['deps/common.gypi'],
|
|
||||||
'targets': [
|
|
||||||
{
|
|
||||||
'target_name': 'better_sqlite3',
|
|
||||||
'dependencies': ['deps/sqlite3.gyp:sqlite3'],
|
|
||||||
'sources': ['src/better_sqlite3.cpp'],
|
|
||||||
'cflags_cc': ['-std=c++20'],
|
|
||||||
'xcode_settings': {
|
|
||||||
'OTHER_CPLUSPLUSFLAGS': ['-std=c++20', '-stdlib=libc++'],
|
|
||||||
},
|
|
||||||
'msvs_settings': {
|
|
||||||
'VCCLCompilerTool': {
|
|
||||||
'AdditionalOptions': [
|
|
||||||
'/std:c++20',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'conditions': [
|
|
||||||
['OS=="linux"', {
|
|
||||||
'ldflags': [
|
|
||||||
'-Wl,-Bsymbolic',
|
|
||||||
'-Wl,--exclude-libs,ALL',
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'target_name': 'test_extension',
|
|
||||||
'dependencies': ['deps/sqlite3.gyp:sqlite3'],
|
|
||||||
'conditions': [['sqlite3 == ""', { 'sources': ['deps/test_extension.c'] }]],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
BIN
Binary file not shown.
-68
@@ -1,68 +0,0 @@
|
|||||||
# ===
|
|
||||||
# This configuration defines the differences between Release and Debug builds.
|
|
||||||
# Some miscellaneous Windows settings are also defined here.
|
|
||||||
# ===
|
|
||||||
|
|
||||||
{
|
|
||||||
'variables': { 'sqlite3%': '' },
|
|
||||||
'target_defaults': {
|
|
||||||
'default_configuration': 'Release',
|
|
||||||
'msvs_settings': {
|
|
||||||
'VCCLCompilerTool': {
|
|
||||||
'ExceptionHandling': 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'conditions': [
|
|
||||||
['OS == "win"', {
|
|
||||||
'defines': ['WIN32'],
|
|
||||||
}],
|
|
||||||
],
|
|
||||||
'configurations': {
|
|
||||||
'Debug': {
|
|
||||||
'defines!': [
|
|
||||||
'NDEBUG',
|
|
||||||
],
|
|
||||||
'defines': [
|
|
||||||
'DEBUG',
|
|
||||||
'_DEBUG',
|
|
||||||
'SQLITE_DEBUG',
|
|
||||||
'SQLITE_MEMDEBUG',
|
|
||||||
'SQLITE_ENABLE_API_ARMOR',
|
|
||||||
'SQLITE_WIN32_MALLOC_VALIDATE',
|
|
||||||
],
|
|
||||||
'cflags': [
|
|
||||||
'-O0',
|
|
||||||
],
|
|
||||||
'xcode_settings': {
|
|
||||||
'MACOSX_DEPLOYMENT_TARGET': '10.7',
|
|
||||||
'GCC_OPTIMIZATION_LEVEL': '0',
|
|
||||||
'GCC_GENERATE_DEBUGGING_SYMBOLS': 'YES',
|
|
||||||
},
|
|
||||||
'msvs_settings': {
|
|
||||||
'VCLinkerTool': {
|
|
||||||
'GenerateDebugInformation': 'true',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'Release': {
|
|
||||||
'defines!': [
|
|
||||||
'DEBUG',
|
|
||||||
'_DEBUG',
|
|
||||||
],
|
|
||||||
'defines': [
|
|
||||||
'NDEBUG',
|
|
||||||
],
|
|
||||||
'cflags': [
|
|
||||||
'-O3',
|
|
||||||
],
|
|
||||||
'xcode_settings': {
|
|
||||||
'MACOSX_DEPLOYMENT_TARGET': '10.7',
|
|
||||||
'GCC_OPTIMIZATION_LEVEL': '3',
|
|
||||||
'GCC_GENERATE_DEBUGGING_SYMBOLS': 'NO',
|
|
||||||
'DEAD_CODE_STRIPPING': 'YES',
|
|
||||||
'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
-31
@@ -1,31 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const path = require('path');
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
const dest = process.argv[2];
|
|
||||||
const source = path.resolve(path.sep, process.argv[3] || path.join(__dirname, 'sqlite3'));
|
|
||||||
const files = [
|
|
||||||
{ filename: 'sqlite3.c', optional: false },
|
|
||||||
{ filename: 'sqlite3.h', optional: false },
|
|
||||||
];
|
|
||||||
|
|
||||||
if (process.argv[3]) {
|
|
||||||
// Support "_HAVE_SQLITE_CONFIG_H" in custom builds.
|
|
||||||
files.push({ filename: 'config.h', optional: true });
|
|
||||||
} else {
|
|
||||||
// Required for some tests.
|
|
||||||
files.push({ filename: 'sqlite3ext.h', optional: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const { filename, optional } of files) {
|
|
||||||
const sourceFilepath = path.join(source, filename);
|
|
||||||
const destFilepath = path.join(dest, filename);
|
|
||||||
|
|
||||||
if (optional && !fs.existsSync(sourceFilepath)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.accessSync(sourceFilepath);
|
|
||||||
fs.mkdirSync(path.dirname(destFilepath), { recursive: true });
|
|
||||||
fs.copyFileSync(sourceFilepath, destFilepath);
|
|
||||||
}
|
|
||||||
-41
@@ -1,41 +0,0 @@
|
|||||||
# THIS FILE IS AUTOMATICALLY GENERATED BY deps/download.sh (DO NOT EDIT)
|
|
||||||
|
|
||||||
{
|
|
||||||
'defines': [
|
|
||||||
'HAVE_INT16_T=1',
|
|
||||||
'HAVE_INT32_T=1',
|
|
||||||
'HAVE_INT8_T=1',
|
|
||||||
'HAVE_STDINT_H=1',
|
|
||||||
'HAVE_UINT16_T=1',
|
|
||||||
'HAVE_UINT32_T=1',
|
|
||||||
'HAVE_UINT8_T=1',
|
|
||||||
'HAVE_USLEEP=1',
|
|
||||||
'SQLITE_DEFAULT_CACHE_SIZE=-16000',
|
|
||||||
'SQLITE_DEFAULT_FOREIGN_KEYS=1',
|
|
||||||
'SQLITE_DEFAULT_MEMSTATUS=0',
|
|
||||||
'SQLITE_DEFAULT_WAL_SYNCHRONOUS=1',
|
|
||||||
'SQLITE_DQS=0',
|
|
||||||
'SQLITE_ENABLE_COLUMN_METADATA',
|
|
||||||
'SQLITE_ENABLE_DBSTAT_VTAB',
|
|
||||||
'SQLITE_ENABLE_DESERIALIZE',
|
|
||||||
'SQLITE_ENABLE_FTS3',
|
|
||||||
'SQLITE_ENABLE_FTS3_PARENTHESIS',
|
|
||||||
'SQLITE_ENABLE_FTS4',
|
|
||||||
'SQLITE_ENABLE_FTS5',
|
|
||||||
'SQLITE_ENABLE_GEOPOLY',
|
|
||||||
'SQLITE_ENABLE_JSON1',
|
|
||||||
'SQLITE_ENABLE_MATH_FUNCTIONS',
|
|
||||||
'SQLITE_ENABLE_RTREE',
|
|
||||||
'SQLITE_ENABLE_STAT4',
|
|
||||||
'SQLITE_ENABLE_UPDATE_DELETE_LIMIT',
|
|
||||||
'SQLITE_LIKE_DOESNT_MATCH_BLOBS',
|
|
||||||
'SQLITE_OMIT_DEPRECATED',
|
|
||||||
'SQLITE_OMIT_PROGRESS_CALLBACK',
|
|
||||||
'SQLITE_OMIT_SHARED_CACHE',
|
|
||||||
'SQLITE_OMIT_TCL_VARIABLE',
|
|
||||||
'SQLITE_SOUNDEX',
|
|
||||||
'SQLITE_THREADSAFE=2',
|
|
||||||
'SQLITE_TRACE_SIZE_LIMIT=32',
|
|
||||||
'SQLITE_USE_URI=0',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
-122
@@ -1,122 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# ===
|
|
||||||
# This script defines and generates the bundled SQLite unit (sqlite3.c).
|
|
||||||
#
|
|
||||||
# The following steps are taken:
|
|
||||||
# 1. populate the shell environment with the defined compile-time options.
|
|
||||||
# 2. download and extract the SQLite source code into a temporary directory.
|
|
||||||
# 3. run "sh configure" and "make sqlite3.c" within the source directory.
|
|
||||||
# 4. copy the generated amalgamation into the output directory (./sqlite3).
|
|
||||||
# 5. export the defined compile-time options to a gyp file (./defines.gypi).
|
|
||||||
# 6. update the docs (../docs/compilation.md) with details of this distribution.
|
|
||||||
#
|
|
||||||
# When a user builds better-sqlite3, the following steps are taken:
|
|
||||||
# 1. node-gyp loads the previously exported compile-time options (defines.gypi).
|
|
||||||
# 2. the copy.js script copies the bundled amalgamation into the build folder.
|
|
||||||
# 3. node-gyp compiles the copied sqlite3.c along with better_sqlite3.cpp.
|
|
||||||
# 4. node-gyp links the two resulting binaries to generate better_sqlite3.node.
|
|
||||||
# ===
|
|
||||||
|
|
||||||
YEAR="2025"
|
|
||||||
VERSION="3490200"
|
|
||||||
|
|
||||||
# Defines below are sorted alphabetically
|
|
||||||
DEFINES="
|
|
||||||
HAVE_INT16_T=1
|
|
||||||
HAVE_INT32_T=1
|
|
||||||
HAVE_INT8_T=1
|
|
||||||
HAVE_STDINT_H=1
|
|
||||||
HAVE_UINT16_T=1
|
|
||||||
HAVE_UINT32_T=1
|
|
||||||
HAVE_UINT8_T=1
|
|
||||||
HAVE_USLEEP=1
|
|
||||||
SQLITE_DEFAULT_CACHE_SIZE=-16000
|
|
||||||
SQLITE_DEFAULT_FOREIGN_KEYS=1
|
|
||||||
SQLITE_DEFAULT_MEMSTATUS=0
|
|
||||||
SQLITE_DEFAULT_WAL_SYNCHRONOUS=1
|
|
||||||
SQLITE_DQS=0
|
|
||||||
SQLITE_ENABLE_COLUMN_METADATA
|
|
||||||
SQLITE_ENABLE_DBSTAT_VTAB
|
|
||||||
SQLITE_ENABLE_DESERIALIZE
|
|
||||||
SQLITE_ENABLE_FTS3
|
|
||||||
SQLITE_ENABLE_FTS3_PARENTHESIS
|
|
||||||
SQLITE_ENABLE_FTS4
|
|
||||||
SQLITE_ENABLE_FTS5
|
|
||||||
SQLITE_ENABLE_GEOPOLY
|
|
||||||
SQLITE_ENABLE_JSON1
|
|
||||||
SQLITE_ENABLE_MATH_FUNCTIONS
|
|
||||||
SQLITE_ENABLE_RTREE
|
|
||||||
SQLITE_ENABLE_STAT4
|
|
||||||
SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
|
||||||
SQLITE_LIKE_DOESNT_MATCH_BLOBS
|
|
||||||
SQLITE_OMIT_DEPRECATED
|
|
||||||
SQLITE_OMIT_PROGRESS_CALLBACK
|
|
||||||
SQLITE_OMIT_SHARED_CACHE
|
|
||||||
SQLITE_OMIT_TCL_VARIABLE
|
|
||||||
SQLITE_SOUNDEX
|
|
||||||
SQLITE_THREADSAFE=2
|
|
||||||
SQLITE_TRACE_SIZE_LIMIT=32
|
|
||||||
SQLITE_USE_URI=0
|
|
||||||
"
|
|
||||||
|
|
||||||
# ========== START SCRIPT ========== #
|
|
||||||
|
|
||||||
echo "setting up environment..."
|
|
||||||
DEPS="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
|
||||||
TEMP="$DEPS/temp"
|
|
||||||
OUTPUT="$DEPS/sqlite3"
|
|
||||||
rm -rf "$TEMP"
|
|
||||||
rm -rf "$OUTPUT"
|
|
||||||
mkdir -p "$TEMP"
|
|
||||||
mkdir -p "$OUTPUT"
|
|
||||||
export CFLAGS=`echo $(echo "$DEFINES" | sed -e "/^\s*$/d" -e "s/^/-D/")`
|
|
||||||
|
|
||||||
echo "downloading source..."
|
|
||||||
curl -#f "https://www.sqlite.org/$YEAR/sqlite-src-$VERSION.zip" > "$TEMP/source.zip" || exit 1
|
|
||||||
|
|
||||||
echo "extracting source..."
|
|
||||||
unzip "$TEMP/source.zip" -d "$TEMP" > /dev/null || exit 1
|
|
||||||
cd "$TEMP/sqlite-src-$VERSION" || exit 1
|
|
||||||
|
|
||||||
echo "configuring amalgamation..."
|
|
||||||
sh configure > /dev/null || exit 1
|
|
||||||
|
|
||||||
echo "building amalgamation..."
|
|
||||||
make OPTIONS="$CFLAGS" sqlite3.c > /dev/null || exit 1
|
|
||||||
|
|
||||||
echo "copying amalgamation..."
|
|
||||||
cp sqlite3.c sqlite3.h sqlite3ext.h "$OUTPUT/" || exit 1
|
|
||||||
|
|
||||||
echo "applying patches..."
|
|
||||||
cd "$DEPS" || exit 1
|
|
||||||
for patch in patches/*.patch; do
|
|
||||||
# If a patch fails, just skip it an move on.
|
|
||||||
# By default `patch` tries to be clever and reverse the patch, so we have to specify `--forward`.
|
|
||||||
# If the patch fails, we # don't write .orig and .rej files, so we have to specify `--no-backup-if-mismatch` and `--reject-file=-`.
|
|
||||||
patch --batch --forward --no-backup-if-mismatch --reject-file=- -p2 < "$patch"
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "updating gyp configs..."
|
|
||||||
GYP="$DEPS/defines.gypi"
|
|
||||||
printf "# THIS FILE IS AUTOMATICALLY GENERATED BY deps/download.sh (DO NOT EDIT)\n\n{\n 'defines': [\n" > "$GYP"
|
|
||||||
printf "$DEFINES" | sed -e "/^\s*$/d" -e "s/\(.*\)/ '\1',/" >> "$GYP"
|
|
||||||
printf " ],\n}\n" >> "$GYP"
|
|
||||||
|
|
||||||
echo "updating docs..."
|
|
||||||
DOCS="$DEPS/../docs/compilation.md"
|
|
||||||
MAJOR=`expr "${VERSION:0:1}" + 0`
|
|
||||||
MINOR=`expr "${VERSION:1:2}" + 0`
|
|
||||||
PATCH=`expr "${VERSION:3:2}" + 0`
|
|
||||||
sed -Ei.bak -e "s/version [0-9]+\.[0-9]+\.[0-9]+/version $MAJOR.$MINOR.$PATCH/g" "$DOCS"
|
|
||||||
sed -i.bak -e "/^SQLITE_/,\$d" "$DOCS"
|
|
||||||
sed -i.bak -e "/^HAVE_/,\$d" "$DOCS"
|
|
||||||
rm "$DOCS".bak
|
|
||||||
printf "$DEFINES" | sed -e "/^\s*$/d" >> "$DOCS"
|
|
||||||
printf "\`\`\`\n" >> "$DOCS"
|
|
||||||
|
|
||||||
echo "cleaning up..."
|
|
||||||
cd - > /dev/null || exit 1
|
|
||||||
rm -rf "$TEMP"
|
|
||||||
|
|
||||||
echo "done!"
|
|
||||||
-15
@@ -1,15 +0,0 @@
|
|||||||
diff --git a/deps/sqlite3/sqlite3.c b/deps/sqlite3/sqlite3.c
|
|
||||||
index b1a807f..38bd1e6 100644
|
|
||||||
--- a/deps/sqlite3/sqlite3.c
|
|
||||||
+++ b/deps/sqlite3/sqlite3.c
|
|
||||||
@@ -24887,8 +24887,8 @@ static const struct {
|
|
||||||
/* 1 */ { 6, "minute", 7.7379e+12, 60.0 },
|
|
||||||
/* 2 */ { 4, "hour", 1.2897e+11, 3600.0 },
|
|
||||||
/* 3 */ { 3, "day", 5373485.0, 86400.0 },
|
|
||||||
- /* 4 */ { 5, "month", 176546.0, 30.0*86400.0 },
|
|
||||||
- /* 5 */ { 4, "year", 14713.0, 365.0*86400.0 },
|
|
||||||
+ /* 4 */ { 5, "month", 176546.0, 2592000.0 },
|
|
||||||
+ /* 5 */ { 4, "year", 14713.0, 31536000.0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
-80
@@ -1,80 +0,0 @@
|
|||||||
# ===
|
|
||||||
# This configuration defines options specific to compiling SQLite itself.
|
|
||||||
# Compile-time options are loaded by the auto-generated file "defines.gypi".
|
|
||||||
# The --sqlite3 option can be provided to use a custom amalgamation instead.
|
|
||||||
# ===
|
|
||||||
|
|
||||||
{
|
|
||||||
'includes': ['common.gypi'],
|
|
||||||
'targets': [
|
|
||||||
{
|
|
||||||
'target_name': 'locate_sqlite3',
|
|
||||||
'type': 'none',
|
|
||||||
'hard_dependency': 1,
|
|
||||||
'conditions': [
|
|
||||||
['sqlite3 == ""', {
|
|
||||||
'actions': [{
|
|
||||||
'action_name': 'copy_builtin_sqlite3',
|
|
||||||
'inputs': [
|
|
||||||
'sqlite3/sqlite3.c',
|
|
||||||
'sqlite3/sqlite3.h',
|
|
||||||
'sqlite3/sqlite3ext.h',
|
|
||||||
],
|
|
||||||
'outputs': [
|
|
||||||
'<(SHARED_INTERMEDIATE_DIR)/sqlite3/sqlite3.c',
|
|
||||||
'<(SHARED_INTERMEDIATE_DIR)/sqlite3/sqlite3.h',
|
|
||||||
'<(SHARED_INTERMEDIATE_DIR)/sqlite3/sqlite3ext.h',
|
|
||||||
],
|
|
||||||
'action': ['node', 'copy.js', '<(SHARED_INTERMEDIATE_DIR)/sqlite3', ''],
|
|
||||||
}],
|
|
||||||
}, {
|
|
||||||
'actions': [{
|
|
||||||
'action_name': 'copy_custom_sqlite3',
|
|
||||||
'inputs': [
|
|
||||||
'<(sqlite3)/sqlite3.c',
|
|
||||||
'<(sqlite3)/sqlite3.h',
|
|
||||||
],
|
|
||||||
'outputs': [
|
|
||||||
'<(SHARED_INTERMEDIATE_DIR)/sqlite3/sqlite3.c',
|
|
||||||
'<(SHARED_INTERMEDIATE_DIR)/sqlite3/sqlite3.h',
|
|
||||||
],
|
|
||||||
'action': ['node', 'copy.js', '<(SHARED_INTERMEDIATE_DIR)/sqlite3', '<(sqlite3)'],
|
|
||||||
}],
|
|
||||||
}],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'target_name': 'sqlite3',
|
|
||||||
'type': 'static_library',
|
|
||||||
'dependencies': ['locate_sqlite3'],
|
|
||||||
'sources': ['<(SHARED_INTERMEDIATE_DIR)/sqlite3/sqlite3.c'],
|
|
||||||
'include_dirs': ['<(SHARED_INTERMEDIATE_DIR)/sqlite3/'],
|
|
||||||
'direct_dependent_settings': {
|
|
||||||
'include_dirs': ['<(SHARED_INTERMEDIATE_DIR)/sqlite3/'],
|
|
||||||
},
|
|
||||||
'cflags': ['-std=c99', '-w'],
|
|
||||||
'xcode_settings': {
|
|
||||||
'OTHER_CFLAGS': ['-std=c99'],
|
|
||||||
'WARNING_CFLAGS': ['-w'],
|
|
||||||
},
|
|
||||||
'conditions': [
|
|
||||||
['sqlite3 == ""', {
|
|
||||||
'includes': ['defines.gypi'],
|
|
||||||
}, {
|
|
||||||
'defines': [
|
|
||||||
# This is currently required by better-sqlite3.
|
|
||||||
'SQLITE_ENABLE_COLUMN_METADATA',
|
|
||||||
],
|
|
||||||
}]
|
|
||||||
],
|
|
||||||
'configurations': {
|
|
||||||
'Debug': {
|
|
||||||
'msvs_settings': { 'VCCLCompilerTool': { 'RuntimeLibrary': 1 } }, # static debug
|
|
||||||
},
|
|
||||||
'Release': {
|
|
||||||
'msvs_settings': { 'VCCLCompilerTool': { 'RuntimeLibrary': 0 } }, # static release
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
-261480
File diff suppressed because it is too large
Load Diff
-13715
File diff suppressed because it is too large
Load Diff
-719
@@ -1,719 +0,0 @@
|
|||||||
/*
|
|
||||||
** 2006 June 7
|
|
||||||
**
|
|
||||||
** The author disclaims copyright to this source code. In place of
|
|
||||||
** a legal notice, here is a blessing:
|
|
||||||
**
|
|
||||||
** May you do good and not evil.
|
|
||||||
** May you find forgiveness for yourself and forgive others.
|
|
||||||
** May you share freely, never taking more than you give.
|
|
||||||
**
|
|
||||||
*************************************************************************
|
|
||||||
** This header file defines the SQLite interface for use by
|
|
||||||
** shared libraries that want to be imported as extensions into
|
|
||||||
** an SQLite instance. Shared libraries that intend to be loaded
|
|
||||||
** as extensions by SQLite should #include this file instead of
|
|
||||||
** sqlite3.h.
|
|
||||||
*/
|
|
||||||
#ifndef SQLITE3EXT_H
|
|
||||||
#define SQLITE3EXT_H
|
|
||||||
#include "sqlite3.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
** The following structure holds pointers to all of the SQLite API
|
|
||||||
** routines.
|
|
||||||
**
|
|
||||||
** WARNING: In order to maintain backwards compatibility, add new
|
|
||||||
** interfaces to the end of this structure only. If you insert new
|
|
||||||
** interfaces in the middle of this structure, then older different
|
|
||||||
** versions of SQLite will not be able to load each other's shared
|
|
||||||
** libraries!
|
|
||||||
*/
|
|
||||||
struct sqlite3_api_routines {
|
|
||||||
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
|
||||||
int (*aggregate_count)(sqlite3_context*);
|
|
||||||
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
|
||||||
int (*bind_double)(sqlite3_stmt*,int,double);
|
|
||||||
int (*bind_int)(sqlite3_stmt*,int,int);
|
|
||||||
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
|
||||||
int (*bind_null)(sqlite3_stmt*,int);
|
|
||||||
int (*bind_parameter_count)(sqlite3_stmt*);
|
|
||||||
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
|
||||||
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
|
||||||
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
|
||||||
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
|
||||||
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
|
||||||
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
|
||||||
int (*busy_timeout)(sqlite3*,int ms);
|
|
||||||
int (*changes)(sqlite3*);
|
|
||||||
int (*close)(sqlite3*);
|
|
||||||
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
|
||||||
int eTextRep,const char*));
|
|
||||||
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
|
||||||
int eTextRep,const void*));
|
|
||||||
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
|
||||||
int (*column_bytes)(sqlite3_stmt*,int iCol);
|
|
||||||
int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
|
||||||
int (*column_count)(sqlite3_stmt*pStmt);
|
|
||||||
const char * (*column_database_name)(sqlite3_stmt*,int);
|
|
||||||
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
|
||||||
const char * (*column_decltype)(sqlite3_stmt*,int i);
|
|
||||||
const void * (*column_decltype16)(sqlite3_stmt*,int);
|
|
||||||
double (*column_double)(sqlite3_stmt*,int iCol);
|
|
||||||
int (*column_int)(sqlite3_stmt*,int iCol);
|
|
||||||
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
|
|
||||||
const char * (*column_name)(sqlite3_stmt*,int);
|
|
||||||
const void * (*column_name16)(sqlite3_stmt*,int);
|
|
||||||
const char * (*column_origin_name)(sqlite3_stmt*,int);
|
|
||||||
const void * (*column_origin_name16)(sqlite3_stmt*,int);
|
|
||||||
const char * (*column_table_name)(sqlite3_stmt*,int);
|
|
||||||
const void * (*column_table_name16)(sqlite3_stmt*,int);
|
|
||||||
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
|
|
||||||
const void * (*column_text16)(sqlite3_stmt*,int iCol);
|
|
||||||
int (*column_type)(sqlite3_stmt*,int iCol);
|
|
||||||
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
|
|
||||||
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
|
|
||||||
int (*complete)(const char*sql);
|
|
||||||
int (*complete16)(const void*sql);
|
|
||||||
int (*create_collation)(sqlite3*,const char*,int,void*,
|
|
||||||
int(*)(void*,int,const void*,int,const void*));
|
|
||||||
int (*create_collation16)(sqlite3*,const void*,int,void*,
|
|
||||||
int(*)(void*,int,const void*,int,const void*));
|
|
||||||
int (*create_function)(sqlite3*,const char*,int,int,void*,
|
|
||||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void (*xFinal)(sqlite3_context*));
|
|
||||||
int (*create_function16)(sqlite3*,const void*,int,int,void*,
|
|
||||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void (*xFinal)(sqlite3_context*));
|
|
||||||
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
|
|
||||||
int (*data_count)(sqlite3_stmt*pStmt);
|
|
||||||
sqlite3 * (*db_handle)(sqlite3_stmt*);
|
|
||||||
int (*declare_vtab)(sqlite3*,const char*);
|
|
||||||
int (*enable_shared_cache)(int);
|
|
||||||
int (*errcode)(sqlite3*db);
|
|
||||||
const char * (*errmsg)(sqlite3*);
|
|
||||||
const void * (*errmsg16)(sqlite3*);
|
|
||||||
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
|
|
||||||
int (*expired)(sqlite3_stmt*);
|
|
||||||
int (*finalize)(sqlite3_stmt*pStmt);
|
|
||||||
void (*free)(void*);
|
|
||||||
void (*free_table)(char**result);
|
|
||||||
int (*get_autocommit)(sqlite3*);
|
|
||||||
void * (*get_auxdata)(sqlite3_context*,int);
|
|
||||||
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
|
|
||||||
int (*global_recover)(void);
|
|
||||||
void (*interruptx)(sqlite3*);
|
|
||||||
sqlite_int64 (*last_insert_rowid)(sqlite3*);
|
|
||||||
const char * (*libversion)(void);
|
|
||||||
int (*libversion_number)(void);
|
|
||||||
void *(*malloc)(int);
|
|
||||||
char * (*mprintf)(const char*,...);
|
|
||||||
int (*open)(const char*,sqlite3**);
|
|
||||||
int (*open16)(const void*,sqlite3**);
|
|
||||||
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
|
||||||
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
|
||||||
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
|
|
||||||
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
|
||||||
void *(*realloc)(void*,int);
|
|
||||||
int (*reset)(sqlite3_stmt*pStmt);
|
|
||||||
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
|
||||||
void (*result_double)(sqlite3_context*,double);
|
|
||||||
void (*result_error)(sqlite3_context*,const char*,int);
|
|
||||||
void (*result_error16)(sqlite3_context*,const void*,int);
|
|
||||||
void (*result_int)(sqlite3_context*,int);
|
|
||||||
void (*result_int64)(sqlite3_context*,sqlite_int64);
|
|
||||||
void (*result_null)(sqlite3_context*);
|
|
||||||
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
|
|
||||||
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
|
|
||||||
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
|
|
||||||
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
|
|
||||||
void (*result_value)(sqlite3_context*,sqlite3_value*);
|
|
||||||
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
|
|
||||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
|
||||||
const char*,const char*),void*);
|
|
||||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
|
||||||
char * (*xsnprintf)(int,char*,const char*,...);
|
|
||||||
int (*step)(sqlite3_stmt*);
|
|
||||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
|
||||||
char const**,char const**,int*,int*,int*);
|
|
||||||
void (*thread_cleanup)(void);
|
|
||||||
int (*total_changes)(sqlite3*);
|
|
||||||
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
|
||||||
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
|
||||||
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
|
||||||
sqlite_int64),void*);
|
|
||||||
void * (*user_data)(sqlite3_context*);
|
|
||||||
const void * (*value_blob)(sqlite3_value*);
|
|
||||||
int (*value_bytes)(sqlite3_value*);
|
|
||||||
int (*value_bytes16)(sqlite3_value*);
|
|
||||||
double (*value_double)(sqlite3_value*);
|
|
||||||
int (*value_int)(sqlite3_value*);
|
|
||||||
sqlite_int64 (*value_int64)(sqlite3_value*);
|
|
||||||
int (*value_numeric_type)(sqlite3_value*);
|
|
||||||
const unsigned char * (*value_text)(sqlite3_value*);
|
|
||||||
const void * (*value_text16)(sqlite3_value*);
|
|
||||||
const void * (*value_text16be)(sqlite3_value*);
|
|
||||||
const void * (*value_text16le)(sqlite3_value*);
|
|
||||||
int (*value_type)(sqlite3_value*);
|
|
||||||
char *(*vmprintf)(const char*,va_list);
|
|
||||||
/* Added ??? */
|
|
||||||
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
|
|
||||||
/* Added by 3.3.13 */
|
|
||||||
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
|
||||||
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
|
||||||
int (*clear_bindings)(sqlite3_stmt*);
|
|
||||||
/* Added by 3.4.1 */
|
|
||||||
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
|
|
||||||
void (*xDestroy)(void *));
|
|
||||||
/* Added by 3.5.0 */
|
|
||||||
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
|
|
||||||
int (*blob_bytes)(sqlite3_blob*);
|
|
||||||
int (*blob_close)(sqlite3_blob*);
|
|
||||||
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
|
|
||||||
int,sqlite3_blob**);
|
|
||||||
int (*blob_read)(sqlite3_blob*,void*,int,int);
|
|
||||||
int (*blob_write)(sqlite3_blob*,const void*,int,int);
|
|
||||||
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
|
|
||||||
int(*)(void*,int,const void*,int,const void*),
|
|
||||||
void(*)(void*));
|
|
||||||
int (*file_control)(sqlite3*,const char*,int,void*);
|
|
||||||
sqlite3_int64 (*memory_highwater)(int);
|
|
||||||
sqlite3_int64 (*memory_used)(void);
|
|
||||||
sqlite3_mutex *(*mutex_alloc)(int);
|
|
||||||
void (*mutex_enter)(sqlite3_mutex*);
|
|
||||||
void (*mutex_free)(sqlite3_mutex*);
|
|
||||||
void (*mutex_leave)(sqlite3_mutex*);
|
|
||||||
int (*mutex_try)(sqlite3_mutex*);
|
|
||||||
int (*open_v2)(const char*,sqlite3**,int,const char*);
|
|
||||||
int (*release_memory)(int);
|
|
||||||
void (*result_error_nomem)(sqlite3_context*);
|
|
||||||
void (*result_error_toobig)(sqlite3_context*);
|
|
||||||
int (*sleep)(int);
|
|
||||||
void (*soft_heap_limit)(int);
|
|
||||||
sqlite3_vfs *(*vfs_find)(const char*);
|
|
||||||
int (*vfs_register)(sqlite3_vfs*,int);
|
|
||||||
int (*vfs_unregister)(sqlite3_vfs*);
|
|
||||||
int (*xthreadsafe)(void);
|
|
||||||
void (*result_zeroblob)(sqlite3_context*,int);
|
|
||||||
void (*result_error_code)(sqlite3_context*,int);
|
|
||||||
int (*test_control)(int, ...);
|
|
||||||
void (*randomness)(int,void*);
|
|
||||||
sqlite3 *(*context_db_handle)(sqlite3_context*);
|
|
||||||
int (*extended_result_codes)(sqlite3*,int);
|
|
||||||
int (*limit)(sqlite3*,int,int);
|
|
||||||
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
|
|
||||||
const char *(*sql)(sqlite3_stmt*);
|
|
||||||
int (*status)(int,int*,int*,int);
|
|
||||||
int (*backup_finish)(sqlite3_backup*);
|
|
||||||
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
|
|
||||||
int (*backup_pagecount)(sqlite3_backup*);
|
|
||||||
int (*backup_remaining)(sqlite3_backup*);
|
|
||||||
int (*backup_step)(sqlite3_backup*,int);
|
|
||||||
const char *(*compileoption_get)(int);
|
|
||||||
int (*compileoption_used)(const char*);
|
|
||||||
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
|
|
||||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void (*xFinal)(sqlite3_context*),
|
|
||||||
void(*xDestroy)(void*));
|
|
||||||
int (*db_config)(sqlite3*,int,...);
|
|
||||||
sqlite3_mutex *(*db_mutex)(sqlite3*);
|
|
||||||
int (*db_status)(sqlite3*,int,int*,int*,int);
|
|
||||||
int (*extended_errcode)(sqlite3*);
|
|
||||||
void (*log)(int,const char*,...);
|
|
||||||
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
|
|
||||||
const char *(*sourceid)(void);
|
|
||||||
int (*stmt_status)(sqlite3_stmt*,int,int);
|
|
||||||
int (*strnicmp)(const char*,const char*,int);
|
|
||||||
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
|
|
||||||
int (*wal_autocheckpoint)(sqlite3*,int);
|
|
||||||
int (*wal_checkpoint)(sqlite3*,const char*);
|
|
||||||
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
|
|
||||||
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
|
|
||||||
int (*vtab_config)(sqlite3*,int op,...);
|
|
||||||
int (*vtab_on_conflict)(sqlite3*);
|
|
||||||
/* Version 3.7.16 and later */
|
|
||||||
int (*close_v2)(sqlite3*);
|
|
||||||
const char *(*db_filename)(sqlite3*,const char*);
|
|
||||||
int (*db_readonly)(sqlite3*,const char*);
|
|
||||||
int (*db_release_memory)(sqlite3*);
|
|
||||||
const char *(*errstr)(int);
|
|
||||||
int (*stmt_busy)(sqlite3_stmt*);
|
|
||||||
int (*stmt_readonly)(sqlite3_stmt*);
|
|
||||||
int (*stricmp)(const char*,const char*);
|
|
||||||
int (*uri_boolean)(const char*,const char*,int);
|
|
||||||
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
|
||||||
const char *(*uri_parameter)(const char*,const char*);
|
|
||||||
char *(*xvsnprintf)(int,char*,const char*,va_list);
|
|
||||||
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
|
||||||
/* Version 3.8.7 and later */
|
|
||||||
int (*auto_extension)(void(*)(void));
|
|
||||||
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
|
|
||||||
void(*)(void*));
|
|
||||||
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
|
|
||||||
void(*)(void*),unsigned char);
|
|
||||||
int (*cancel_auto_extension)(void(*)(void));
|
|
||||||
int (*load_extension)(sqlite3*,const char*,const char*,char**);
|
|
||||||
void *(*malloc64)(sqlite3_uint64);
|
|
||||||
sqlite3_uint64 (*msize)(void*);
|
|
||||||
void *(*realloc64)(void*,sqlite3_uint64);
|
|
||||||
void (*reset_auto_extension)(void);
|
|
||||||
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
|
|
||||||
void(*)(void*));
|
|
||||||
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
|
|
||||||
void(*)(void*), unsigned char);
|
|
||||||
int (*strglob)(const char*,const char*);
|
|
||||||
/* Version 3.8.11 and later */
|
|
||||||
sqlite3_value *(*value_dup)(const sqlite3_value*);
|
|
||||||
void (*value_free)(sqlite3_value*);
|
|
||||||
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
|
|
||||||
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
|
|
||||||
/* Version 3.9.0 and later */
|
|
||||||
unsigned int (*value_subtype)(sqlite3_value*);
|
|
||||||
void (*result_subtype)(sqlite3_context*,unsigned int);
|
|
||||||
/* Version 3.10.0 and later */
|
|
||||||
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
|
|
||||||
int (*strlike)(const char*,const char*,unsigned int);
|
|
||||||
int (*db_cacheflush)(sqlite3*);
|
|
||||||
/* Version 3.12.0 and later */
|
|
||||||
int (*system_errno)(sqlite3*);
|
|
||||||
/* Version 3.14.0 and later */
|
|
||||||
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
|
|
||||||
char *(*expanded_sql)(sqlite3_stmt*);
|
|
||||||
/* Version 3.18.0 and later */
|
|
||||||
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
|
|
||||||
/* Version 3.20.0 and later */
|
|
||||||
int (*prepare_v3)(sqlite3*,const char*,int,unsigned int,
|
|
||||||
sqlite3_stmt**,const char**);
|
|
||||||
int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int,
|
|
||||||
sqlite3_stmt**,const void**);
|
|
||||||
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
|
|
||||||
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
|
|
||||||
void *(*value_pointer)(sqlite3_value*,const char*);
|
|
||||||
int (*vtab_nochange)(sqlite3_context*);
|
|
||||||
int (*value_nochange)(sqlite3_value*);
|
|
||||||
const char *(*vtab_collation)(sqlite3_index_info*,int);
|
|
||||||
/* Version 3.24.0 and later */
|
|
||||||
int (*keyword_count)(void);
|
|
||||||
int (*keyword_name)(int,const char**,int*);
|
|
||||||
int (*keyword_check)(const char*,int);
|
|
||||||
sqlite3_str *(*str_new)(sqlite3*);
|
|
||||||
char *(*str_finish)(sqlite3_str*);
|
|
||||||
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
|
|
||||||
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
|
|
||||||
void (*str_append)(sqlite3_str*, const char *zIn, int N);
|
|
||||||
void (*str_appendall)(sqlite3_str*, const char *zIn);
|
|
||||||
void (*str_appendchar)(sqlite3_str*, int N, char C);
|
|
||||||
void (*str_reset)(sqlite3_str*);
|
|
||||||
int (*str_errcode)(sqlite3_str*);
|
|
||||||
int (*str_length)(sqlite3_str*);
|
|
||||||
char *(*str_value)(sqlite3_str*);
|
|
||||||
/* Version 3.25.0 and later */
|
|
||||||
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
|
|
||||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void (*xFinal)(sqlite3_context*),
|
|
||||||
void (*xValue)(sqlite3_context*),
|
|
||||||
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
|
|
||||||
void(*xDestroy)(void*));
|
|
||||||
/* Version 3.26.0 and later */
|
|
||||||
const char *(*normalized_sql)(sqlite3_stmt*);
|
|
||||||
/* Version 3.28.0 and later */
|
|
||||||
int (*stmt_isexplain)(sqlite3_stmt*);
|
|
||||||
int (*value_frombind)(sqlite3_value*);
|
|
||||||
/* Version 3.30.0 and later */
|
|
||||||
int (*drop_modules)(sqlite3*,const char**);
|
|
||||||
/* Version 3.31.0 and later */
|
|
||||||
sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64);
|
|
||||||
const char *(*uri_key)(const char*,int);
|
|
||||||
const char *(*filename_database)(const char*);
|
|
||||||
const char *(*filename_journal)(const char*);
|
|
||||||
const char *(*filename_wal)(const char*);
|
|
||||||
/* Version 3.32.0 and later */
|
|
||||||
const char *(*create_filename)(const char*,const char*,const char*,
|
|
||||||
int,const char**);
|
|
||||||
void (*free_filename)(const char*);
|
|
||||||
sqlite3_file *(*database_file_object)(const char*);
|
|
||||||
/* Version 3.34.0 and later */
|
|
||||||
int (*txn_state)(sqlite3*,const char*);
|
|
||||||
/* Version 3.36.1 and later */
|
|
||||||
sqlite3_int64 (*changes64)(sqlite3*);
|
|
||||||
sqlite3_int64 (*total_changes64)(sqlite3*);
|
|
||||||
/* Version 3.37.0 and later */
|
|
||||||
int (*autovacuum_pages)(sqlite3*,
|
|
||||||
unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
|
|
||||||
void*, void(*)(void*));
|
|
||||||
/* Version 3.38.0 and later */
|
|
||||||
int (*error_offset)(sqlite3*);
|
|
||||||
int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
|
|
||||||
int (*vtab_distinct)(sqlite3_index_info*);
|
|
||||||
int (*vtab_in)(sqlite3_index_info*,int,int);
|
|
||||||
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
|
|
||||||
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
|
|
||||||
/* Version 3.39.0 and later */
|
|
||||||
int (*deserialize)(sqlite3*,const char*,unsigned char*,
|
|
||||||
sqlite3_int64,sqlite3_int64,unsigned);
|
|
||||||
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
|
|
||||||
unsigned int);
|
|
||||||
const char *(*db_name)(sqlite3*,int);
|
|
||||||
/* Version 3.40.0 and later */
|
|
||||||
int (*value_encoding)(sqlite3_value*);
|
|
||||||
/* Version 3.41.0 and later */
|
|
||||||
int (*is_interrupted)(sqlite3*);
|
|
||||||
/* Version 3.43.0 and later */
|
|
||||||
int (*stmt_explain)(sqlite3_stmt*,int);
|
|
||||||
/* Version 3.44.0 and later */
|
|
||||||
void *(*get_clientdata)(sqlite3*,const char*);
|
|
||||||
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
** This is the function signature used for all extension entry points. It
|
|
||||||
** is also defined in the file "loadext.c".
|
|
||||||
*/
|
|
||||||
typedef int (*sqlite3_loadext_entry)(
|
|
||||||
sqlite3 *db, /* Handle to the database. */
|
|
||||||
char **pzErrMsg, /* Used to set error string on failure. */
|
|
||||||
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
|
||||||
** The following macros redefine the API routines so that they are
|
|
||||||
** redirected through the global sqlite3_api structure.
|
|
||||||
**
|
|
||||||
** This header file is also used by the loadext.c source file
|
|
||||||
** (part of the main SQLite library - not an extension) so that
|
|
||||||
** it can get access to the sqlite3_api_routines structure
|
|
||||||
** definition. But the main library does not want to redefine
|
|
||||||
** the API. So the redefinition macros are only valid if the
|
|
||||||
** SQLITE_CORE macros is undefined.
|
|
||||||
*/
|
|
||||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
|
||||||
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
|
||||||
#ifndef SQLITE_OMIT_DEPRECATED
|
|
||||||
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
|
||||||
#endif
|
|
||||||
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
|
||||||
#define sqlite3_bind_double sqlite3_api->bind_double
|
|
||||||
#define sqlite3_bind_int sqlite3_api->bind_int
|
|
||||||
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
|
||||||
#define sqlite3_bind_null sqlite3_api->bind_null
|
|
||||||
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
|
||||||
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
|
||||||
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
|
||||||
#define sqlite3_bind_text sqlite3_api->bind_text
|
|
||||||
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
|
||||||
#define sqlite3_bind_value sqlite3_api->bind_value
|
|
||||||
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
|
||||||
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
|
||||||
#define sqlite3_changes sqlite3_api->changes
|
|
||||||
#define sqlite3_close sqlite3_api->close
|
|
||||||
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
|
||||||
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
|
||||||
#define sqlite3_column_blob sqlite3_api->column_blob
|
|
||||||
#define sqlite3_column_bytes sqlite3_api->column_bytes
|
|
||||||
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
|
|
||||||
#define sqlite3_column_count sqlite3_api->column_count
|
|
||||||
#define sqlite3_column_database_name sqlite3_api->column_database_name
|
|
||||||
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
|
|
||||||
#define sqlite3_column_decltype sqlite3_api->column_decltype
|
|
||||||
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
|
|
||||||
#define sqlite3_column_double sqlite3_api->column_double
|
|
||||||
#define sqlite3_column_int sqlite3_api->column_int
|
|
||||||
#define sqlite3_column_int64 sqlite3_api->column_int64
|
|
||||||
#define sqlite3_column_name sqlite3_api->column_name
|
|
||||||
#define sqlite3_column_name16 sqlite3_api->column_name16
|
|
||||||
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
|
|
||||||
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
|
|
||||||
#define sqlite3_column_table_name sqlite3_api->column_table_name
|
|
||||||
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
|
|
||||||
#define sqlite3_column_text sqlite3_api->column_text
|
|
||||||
#define sqlite3_column_text16 sqlite3_api->column_text16
|
|
||||||
#define sqlite3_column_type sqlite3_api->column_type
|
|
||||||
#define sqlite3_column_value sqlite3_api->column_value
|
|
||||||
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
|
||||||
#define sqlite3_complete sqlite3_api->complete
|
|
||||||
#define sqlite3_complete16 sqlite3_api->complete16
|
|
||||||
#define sqlite3_create_collation sqlite3_api->create_collation
|
|
||||||
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
|
||||||
#define sqlite3_create_function sqlite3_api->create_function
|
|
||||||
#define sqlite3_create_function16 sqlite3_api->create_function16
|
|
||||||
#define sqlite3_create_module sqlite3_api->create_module
|
|
||||||
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
|
||||||
#define sqlite3_data_count sqlite3_api->data_count
|
|
||||||
#define sqlite3_db_handle sqlite3_api->db_handle
|
|
||||||
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
|
|
||||||
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
|
|
||||||
#define sqlite3_errcode sqlite3_api->errcode
|
|
||||||
#define sqlite3_errmsg sqlite3_api->errmsg
|
|
||||||
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
|
||||||
#define sqlite3_exec sqlite3_api->exec
|
|
||||||
#ifndef SQLITE_OMIT_DEPRECATED
|
|
||||||
#define sqlite3_expired sqlite3_api->expired
|
|
||||||
#endif
|
|
||||||
#define sqlite3_finalize sqlite3_api->finalize
|
|
||||||
#define sqlite3_free sqlite3_api->free
|
|
||||||
#define sqlite3_free_table sqlite3_api->free_table
|
|
||||||
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
|
||||||
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
|
||||||
#define sqlite3_get_table sqlite3_api->get_table
|
|
||||||
#ifndef SQLITE_OMIT_DEPRECATED
|
|
||||||
#define sqlite3_global_recover sqlite3_api->global_recover
|
|
||||||
#endif
|
|
||||||
#define sqlite3_interrupt sqlite3_api->interruptx
|
|
||||||
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
|
||||||
#define sqlite3_libversion sqlite3_api->libversion
|
|
||||||
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
|
||||||
#define sqlite3_malloc sqlite3_api->malloc
|
|
||||||
#define sqlite3_mprintf sqlite3_api->mprintf
|
|
||||||
#define sqlite3_open sqlite3_api->open
|
|
||||||
#define sqlite3_open16 sqlite3_api->open16
|
|
||||||
#define sqlite3_prepare sqlite3_api->prepare
|
|
||||||
#define sqlite3_prepare16 sqlite3_api->prepare16
|
|
||||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
|
||||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
|
||||||
#define sqlite3_profile sqlite3_api->profile
|
|
||||||
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
|
||||||
#define sqlite3_realloc sqlite3_api->realloc
|
|
||||||
#define sqlite3_reset sqlite3_api->reset
|
|
||||||
#define sqlite3_result_blob sqlite3_api->result_blob
|
|
||||||
#define sqlite3_result_double sqlite3_api->result_double
|
|
||||||
#define sqlite3_result_error sqlite3_api->result_error
|
|
||||||
#define sqlite3_result_error16 sqlite3_api->result_error16
|
|
||||||
#define sqlite3_result_int sqlite3_api->result_int
|
|
||||||
#define sqlite3_result_int64 sqlite3_api->result_int64
|
|
||||||
#define sqlite3_result_null sqlite3_api->result_null
|
|
||||||
#define sqlite3_result_text sqlite3_api->result_text
|
|
||||||
#define sqlite3_result_text16 sqlite3_api->result_text16
|
|
||||||
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
|
||||||
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
|
||||||
#define sqlite3_result_value sqlite3_api->result_value
|
|
||||||
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
|
||||||
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
|
||||||
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
|
||||||
#define sqlite3_snprintf sqlite3_api->xsnprintf
|
|
||||||
#define sqlite3_step sqlite3_api->step
|
|
||||||
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
|
||||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
|
||||||
#define sqlite3_total_changes sqlite3_api->total_changes
|
|
||||||
#define sqlite3_trace sqlite3_api->trace
|
|
||||||
#ifndef SQLITE_OMIT_DEPRECATED
|
|
||||||
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
|
||||||
#endif
|
|
||||||
#define sqlite3_update_hook sqlite3_api->update_hook
|
|
||||||
#define sqlite3_user_data sqlite3_api->user_data
|
|
||||||
#define sqlite3_value_blob sqlite3_api->value_blob
|
|
||||||
#define sqlite3_value_bytes sqlite3_api->value_bytes
|
|
||||||
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
|
|
||||||
#define sqlite3_value_double sqlite3_api->value_double
|
|
||||||
#define sqlite3_value_int sqlite3_api->value_int
|
|
||||||
#define sqlite3_value_int64 sqlite3_api->value_int64
|
|
||||||
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
|
|
||||||
#define sqlite3_value_text sqlite3_api->value_text
|
|
||||||
#define sqlite3_value_text16 sqlite3_api->value_text16
|
|
||||||
#define sqlite3_value_text16be sqlite3_api->value_text16be
|
|
||||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
|
||||||
#define sqlite3_value_type sqlite3_api->value_type
|
|
||||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
|
||||||
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
|
|
||||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
|
||||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
|
||||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
|
||||||
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
|
|
||||||
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
|
|
||||||
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
|
|
||||||
#define sqlite3_blob_close sqlite3_api->blob_close
|
|
||||||
#define sqlite3_blob_open sqlite3_api->blob_open
|
|
||||||
#define sqlite3_blob_read sqlite3_api->blob_read
|
|
||||||
#define sqlite3_blob_write sqlite3_api->blob_write
|
|
||||||
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
|
|
||||||
#define sqlite3_file_control sqlite3_api->file_control
|
|
||||||
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
|
|
||||||
#define sqlite3_memory_used sqlite3_api->memory_used
|
|
||||||
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
|
|
||||||
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
|
|
||||||
#define sqlite3_mutex_free sqlite3_api->mutex_free
|
|
||||||
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
|
|
||||||
#define sqlite3_mutex_try sqlite3_api->mutex_try
|
|
||||||
#define sqlite3_open_v2 sqlite3_api->open_v2
|
|
||||||
#define sqlite3_release_memory sqlite3_api->release_memory
|
|
||||||
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
|
|
||||||
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
|
|
||||||
#define sqlite3_sleep sqlite3_api->sleep
|
|
||||||
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
|
|
||||||
#define sqlite3_vfs_find sqlite3_api->vfs_find
|
|
||||||
#define sqlite3_vfs_register sqlite3_api->vfs_register
|
|
||||||
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
|
|
||||||
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
|
|
||||||
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
|
|
||||||
#define sqlite3_result_error_code sqlite3_api->result_error_code
|
|
||||||
#define sqlite3_test_control sqlite3_api->test_control
|
|
||||||
#define sqlite3_randomness sqlite3_api->randomness
|
|
||||||
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
|
|
||||||
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
|
|
||||||
#define sqlite3_limit sqlite3_api->limit
|
|
||||||
#define sqlite3_next_stmt sqlite3_api->next_stmt
|
|
||||||
#define sqlite3_sql sqlite3_api->sql
|
|
||||||
#define sqlite3_status sqlite3_api->status
|
|
||||||
#define sqlite3_backup_finish sqlite3_api->backup_finish
|
|
||||||
#define sqlite3_backup_init sqlite3_api->backup_init
|
|
||||||
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
|
|
||||||
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
|
|
||||||
#define sqlite3_backup_step sqlite3_api->backup_step
|
|
||||||
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
|
|
||||||
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
|
|
||||||
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
|
|
||||||
#define sqlite3_db_config sqlite3_api->db_config
|
|
||||||
#define sqlite3_db_mutex sqlite3_api->db_mutex
|
|
||||||
#define sqlite3_db_status sqlite3_api->db_status
|
|
||||||
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
|
|
||||||
#define sqlite3_log sqlite3_api->log
|
|
||||||
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
|
|
||||||
#define sqlite3_sourceid sqlite3_api->sourceid
|
|
||||||
#define sqlite3_stmt_status sqlite3_api->stmt_status
|
|
||||||
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
|
||||||
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
|
||||||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
|
||||||
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
|
||||||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
|
||||||
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
|
||||||
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
|
||||||
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
|
|
||||||
/* Version 3.7.16 and later */
|
|
||||||
#define sqlite3_close_v2 sqlite3_api->close_v2
|
|
||||||
#define sqlite3_db_filename sqlite3_api->db_filename
|
|
||||||
#define sqlite3_db_readonly sqlite3_api->db_readonly
|
|
||||||
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
|
|
||||||
#define sqlite3_errstr sqlite3_api->errstr
|
|
||||||
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
|
|
||||||
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
|
|
||||||
#define sqlite3_stricmp sqlite3_api->stricmp
|
|
||||||
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
|
||||||
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
|
||||||
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
|
||||||
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
|
|
||||||
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
|
||||||
/* Version 3.8.7 and later */
|
|
||||||
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
|
||||||
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
|
|
||||||
#define sqlite3_bind_text64 sqlite3_api->bind_text64
|
|
||||||
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
|
|
||||||
#define sqlite3_load_extension sqlite3_api->load_extension
|
|
||||||
#define sqlite3_malloc64 sqlite3_api->malloc64
|
|
||||||
#define sqlite3_msize sqlite3_api->msize
|
|
||||||
#define sqlite3_realloc64 sqlite3_api->realloc64
|
|
||||||
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
|
|
||||||
#define sqlite3_result_blob64 sqlite3_api->result_blob64
|
|
||||||
#define sqlite3_result_text64 sqlite3_api->result_text64
|
|
||||||
#define sqlite3_strglob sqlite3_api->strglob
|
|
||||||
/* Version 3.8.11 and later */
|
|
||||||
#define sqlite3_value_dup sqlite3_api->value_dup
|
|
||||||
#define sqlite3_value_free sqlite3_api->value_free
|
|
||||||
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
|
|
||||||
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
|
|
||||||
/* Version 3.9.0 and later */
|
|
||||||
#define sqlite3_value_subtype sqlite3_api->value_subtype
|
|
||||||
#define sqlite3_result_subtype sqlite3_api->result_subtype
|
|
||||||
/* Version 3.10.0 and later */
|
|
||||||
#define sqlite3_status64 sqlite3_api->status64
|
|
||||||
#define sqlite3_strlike sqlite3_api->strlike
|
|
||||||
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
|
|
||||||
/* Version 3.12.0 and later */
|
|
||||||
#define sqlite3_system_errno sqlite3_api->system_errno
|
|
||||||
/* Version 3.14.0 and later */
|
|
||||||
#define sqlite3_trace_v2 sqlite3_api->trace_v2
|
|
||||||
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
|
|
||||||
/* Version 3.18.0 and later */
|
|
||||||
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
|
|
||||||
/* Version 3.20.0 and later */
|
|
||||||
#define sqlite3_prepare_v3 sqlite3_api->prepare_v3
|
|
||||||
#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3
|
|
||||||
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
|
|
||||||
#define sqlite3_result_pointer sqlite3_api->result_pointer
|
|
||||||
#define sqlite3_value_pointer sqlite3_api->value_pointer
|
|
||||||
/* Version 3.22.0 and later */
|
|
||||||
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
|
|
||||||
#define sqlite3_value_nochange sqlite3_api->value_nochange
|
|
||||||
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
|
|
||||||
/* Version 3.24.0 and later */
|
|
||||||
#define sqlite3_keyword_count sqlite3_api->keyword_count
|
|
||||||
#define sqlite3_keyword_name sqlite3_api->keyword_name
|
|
||||||
#define sqlite3_keyword_check sqlite3_api->keyword_check
|
|
||||||
#define sqlite3_str_new sqlite3_api->str_new
|
|
||||||
#define sqlite3_str_finish sqlite3_api->str_finish
|
|
||||||
#define sqlite3_str_appendf sqlite3_api->str_appendf
|
|
||||||
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
|
|
||||||
#define sqlite3_str_append sqlite3_api->str_append
|
|
||||||
#define sqlite3_str_appendall sqlite3_api->str_appendall
|
|
||||||
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
|
|
||||||
#define sqlite3_str_reset sqlite3_api->str_reset
|
|
||||||
#define sqlite3_str_errcode sqlite3_api->str_errcode
|
|
||||||
#define sqlite3_str_length sqlite3_api->str_length
|
|
||||||
#define sqlite3_str_value sqlite3_api->str_value
|
|
||||||
/* Version 3.25.0 and later */
|
|
||||||
#define sqlite3_create_window_function sqlite3_api->create_window_function
|
|
||||||
/* Version 3.26.0 and later */
|
|
||||||
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
|
|
||||||
/* Version 3.28.0 and later */
|
|
||||||
#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain
|
|
||||||
#define sqlite3_value_frombind sqlite3_api->value_frombind
|
|
||||||
/* Version 3.30.0 and later */
|
|
||||||
#define sqlite3_drop_modules sqlite3_api->drop_modules
|
|
||||||
/* Version 3.31.0 and later */
|
|
||||||
#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64
|
|
||||||
#define sqlite3_uri_key sqlite3_api->uri_key
|
|
||||||
#define sqlite3_filename_database sqlite3_api->filename_database
|
|
||||||
#define sqlite3_filename_journal sqlite3_api->filename_journal
|
|
||||||
#define sqlite3_filename_wal sqlite3_api->filename_wal
|
|
||||||
/* Version 3.32.0 and later */
|
|
||||||
#define sqlite3_create_filename sqlite3_api->create_filename
|
|
||||||
#define sqlite3_free_filename sqlite3_api->free_filename
|
|
||||||
#define sqlite3_database_file_object sqlite3_api->database_file_object
|
|
||||||
/* Version 3.34.0 and later */
|
|
||||||
#define sqlite3_txn_state sqlite3_api->txn_state
|
|
||||||
/* Version 3.36.1 and later */
|
|
||||||
#define sqlite3_changes64 sqlite3_api->changes64
|
|
||||||
#define sqlite3_total_changes64 sqlite3_api->total_changes64
|
|
||||||
/* Version 3.37.0 and later */
|
|
||||||
#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages
|
|
||||||
/* Version 3.38.0 and later */
|
|
||||||
#define sqlite3_error_offset sqlite3_api->error_offset
|
|
||||||
#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
|
|
||||||
#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
|
|
||||||
#define sqlite3_vtab_in sqlite3_api->vtab_in
|
|
||||||
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
|
|
||||||
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
|
|
||||||
/* Version 3.39.0 and later */
|
|
||||||
#ifndef SQLITE_OMIT_DESERIALIZE
|
|
||||||
#define sqlite3_deserialize sqlite3_api->deserialize
|
|
||||||
#define sqlite3_serialize sqlite3_api->serialize
|
|
||||||
#endif
|
|
||||||
#define sqlite3_db_name sqlite3_api->db_name
|
|
||||||
/* Version 3.40.0 and later */
|
|
||||||
#define sqlite3_value_encoding sqlite3_api->value_encoding
|
|
||||||
/* Version 3.41.0 and later */
|
|
||||||
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
|
|
||||||
/* Version 3.43.0 and later */
|
|
||||||
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
|
|
||||||
/* Version 3.44.0 and later */
|
|
||||||
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
|
||||||
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
|
||||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
|
||||||
|
|
||||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
|
||||||
/* This case when the file really is being compiled as a loadable
|
|
||||||
** extension */
|
|
||||||
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
|
|
||||||
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
|
|
||||||
# define SQLITE_EXTENSION_INIT3 \
|
|
||||||
extern const sqlite3_api_routines *sqlite3_api;
|
|
||||||
#else
|
|
||||||
/* This case when the file is being statically linked into the
|
|
||||||
** application */
|
|
||||||
# define SQLITE_EXTENSION_INIT1 /*no-op*/
|
|
||||||
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
|
|
||||||
# define SQLITE_EXTENSION_INIT3 /*no-op*/
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* SQLITE3EXT_H */
|
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
#include <sqlite3ext.h>
|
|
||||||
SQLITE_EXTENSION_INIT1
|
|
||||||
|
|
||||||
/*
|
|
||||||
This SQLite extension is used only for testing purposes (npm test).
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void TestExtensionFunction(sqlite3_context* pCtx, int nVal, sqlite3_value** _) {
|
|
||||||
sqlite3_result_double(pCtx, (double)nVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
__declspec(dllexport)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int sqlite3_extension_init(sqlite3* db, char** pzErrMsg, const sqlite3_api_routines* pApi) {
|
|
||||||
SQLITE_EXTENSION_INIT2(pApi)
|
|
||||||
if (pzErrMsg != 0) *pzErrMsg = 0;
|
|
||||||
sqlite3_create_function(db, "testExtensionFunction", -1, SQLITE_UTF8, 0, TestExtensionFunction, 0, 0);
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
-90
@@ -1,90 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const util = require('./util');
|
|
||||||
const SqliteError = require('./sqlite-error');
|
|
||||||
|
|
||||||
let DEFAULT_ADDON;
|
|
||||||
|
|
||||||
function Database(filenameGiven, options) {
|
|
||||||
if (new.target == null) {
|
|
||||||
return new Database(filenameGiven, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply defaults
|
|
||||||
let buffer;
|
|
||||||
if (Buffer.isBuffer(filenameGiven)) {
|
|
||||||
buffer = filenameGiven;
|
|
||||||
filenameGiven = ':memory:';
|
|
||||||
}
|
|
||||||
if (filenameGiven == null) filenameGiven = '';
|
|
||||||
if (options == null) options = {};
|
|
||||||
|
|
||||||
// Validate arguments
|
|
||||||
if (typeof filenameGiven !== 'string') throw new TypeError('Expected first argument to be a string');
|
|
||||||
if (typeof options !== 'object') throw new TypeError('Expected second argument to be an options object');
|
|
||||||
if ('readOnly' in options) throw new TypeError('Misspelled option "readOnly" should be "readonly"');
|
|
||||||
if ('memory' in options) throw new TypeError('Option "memory" was removed in v7.0.0 (use ":memory:" filename instead)');
|
|
||||||
|
|
||||||
// Interpret options
|
|
||||||
const filename = filenameGiven.trim();
|
|
||||||
const anonymous = filename === '' || filename === ':memory:';
|
|
||||||
const readonly = util.getBooleanOption(options, 'readonly');
|
|
||||||
const fileMustExist = util.getBooleanOption(options, 'fileMustExist');
|
|
||||||
const timeout = 'timeout' in options ? options.timeout : 5000;
|
|
||||||
const verbose = 'verbose' in options ? options.verbose : null;
|
|
||||||
const nativeBinding = 'nativeBinding' in options ? options.nativeBinding : null;
|
|
||||||
|
|
||||||
// Validate interpreted options
|
|
||||||
if (readonly && anonymous && !buffer) throw new TypeError('In-memory/temporary databases cannot be readonly');
|
|
||||||
if (!Number.isInteger(timeout) || timeout < 0) throw new TypeError('Expected the "timeout" option to be a positive integer');
|
|
||||||
if (timeout > 0x7fffffff) throw new RangeError('Option "timeout" cannot be greater than 2147483647');
|
|
||||||
if (verbose != null && typeof verbose !== 'function') throw new TypeError('Expected the "verbose" option to be a function');
|
|
||||||
if (nativeBinding != null && typeof nativeBinding !== 'string' && typeof nativeBinding !== 'object') throw new TypeError('Expected the "nativeBinding" option to be a string or addon object');
|
|
||||||
|
|
||||||
// Load the native addon
|
|
||||||
let addon;
|
|
||||||
if (nativeBinding == null) {
|
|
||||||
addon = DEFAULT_ADDON || (DEFAULT_ADDON = require('bindings')('better_sqlite3.node'));
|
|
||||||
} else if (typeof nativeBinding === 'string') {
|
|
||||||
// See <https://webpack.js.org/api/module-variables/#__non_webpack_require__-webpack-specific>
|
|
||||||
const requireFunc = typeof __non_webpack_require__ === 'function' ? __non_webpack_require__ : require;
|
|
||||||
addon = requireFunc(path.resolve(nativeBinding).replace(/(\.node)?$/, '.node'));
|
|
||||||
} else {
|
|
||||||
// See <https://github.com/WiseLibs/better-sqlite3/issues/972>
|
|
||||||
addon = nativeBinding;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!addon.isInitialized) {
|
|
||||||
addon.setErrorConstructor(SqliteError);
|
|
||||||
addon.isInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the specified directory exists
|
|
||||||
if (!anonymous && !fs.existsSync(path.dirname(filename))) {
|
|
||||||
throw new TypeError('Cannot open database because the directory does not exist');
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperties(this, {
|
|
||||||
[util.cppdb]: { value: new addon.Database(filename, filenameGiven, anonymous, readonly, fileMustExist, timeout, verbose || null, buffer || null) },
|
|
||||||
...wrappers.getters,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const wrappers = require('./methods/wrappers');
|
|
||||||
Database.prototype.prepare = wrappers.prepare;
|
|
||||||
Database.prototype.transaction = require('./methods/transaction');
|
|
||||||
Database.prototype.pragma = require('./methods/pragma');
|
|
||||||
Database.prototype.backup = require('./methods/backup');
|
|
||||||
Database.prototype.serialize = require('./methods/serialize');
|
|
||||||
Database.prototype.function = require('./methods/function');
|
|
||||||
Database.prototype.aggregate = require('./methods/aggregate');
|
|
||||||
Database.prototype.table = require('./methods/table');
|
|
||||||
Database.prototype.loadExtension = wrappers.loadExtension;
|
|
||||||
Database.prototype.exec = wrappers.exec;
|
|
||||||
Database.prototype.close = wrappers.close;
|
|
||||||
Database.prototype.defaultSafeIntegers = wrappers.defaultSafeIntegers;
|
|
||||||
Database.prototype.unsafeMode = wrappers.unsafeMode;
|
|
||||||
Database.prototype[util.inspect] = require('./methods/inspect');
|
|
||||||
|
|
||||||
module.exports = Database;
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
module.exports = require('./database');
|
|
||||||
module.exports.SqliteError = require('./sqlite-error');
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const { getBooleanOption, cppdb } = require('../util');
|
|
||||||
|
|
||||||
module.exports = function defineAggregate(name, options) {
|
|
||||||
// Validate arguments
|
|
||||||
if (typeof name !== 'string') throw new TypeError('Expected first argument to be a string');
|
|
||||||
if (typeof options !== 'object' || options === null) throw new TypeError('Expected second argument to be an options object');
|
|
||||||
if (!name) throw new TypeError('User-defined function name cannot be an empty string');
|
|
||||||
|
|
||||||
// Interpret options
|
|
||||||
const start = 'start' in options ? options.start : null;
|
|
||||||
const step = getFunctionOption(options, 'step', true);
|
|
||||||
const inverse = getFunctionOption(options, 'inverse', false);
|
|
||||||
const result = getFunctionOption(options, 'result', false);
|
|
||||||
const safeIntegers = 'safeIntegers' in options ? +getBooleanOption(options, 'safeIntegers') : 2;
|
|
||||||
const deterministic = getBooleanOption(options, 'deterministic');
|
|
||||||
const directOnly = getBooleanOption(options, 'directOnly');
|
|
||||||
const varargs = getBooleanOption(options, 'varargs');
|
|
||||||
let argCount = -1;
|
|
||||||
|
|
||||||
// Determine argument count
|
|
||||||
if (!varargs) {
|
|
||||||
argCount = Math.max(getLength(step), inverse ? getLength(inverse) : 0);
|
|
||||||
if (argCount > 0) argCount -= 1;
|
|
||||||
if (argCount > 100) throw new RangeError('User-defined functions cannot have more than 100 arguments');
|
|
||||||
}
|
|
||||||
|
|
||||||
this[cppdb].aggregate(start, step, inverse, result, name, argCount, safeIntegers, deterministic, directOnly);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getFunctionOption = (options, key, required) => {
|
|
||||||
const value = key in options ? options[key] : null;
|
|
||||||
if (typeof value === 'function') return value;
|
|
||||||
if (value != null) throw new TypeError(`Expected the "${key}" option to be a function`);
|
|
||||||
if (required) throw new TypeError(`Missing required option "${key}"`);
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getLength = ({ length }) => {
|
|
||||||
if (Number.isInteger(length) && length >= 0) return length;
|
|
||||||
throw new TypeError('Expected function.length to be a positive integer');
|
|
||||||
};
|
|
||||||
-67
@@ -1,67 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const { promisify } = require('util');
|
|
||||||
const { cppdb } = require('../util');
|
|
||||||
const fsAccess = promisify(fs.access);
|
|
||||||
|
|
||||||
module.exports = async function backup(filename, options) {
|
|
||||||
if (options == null) options = {};
|
|
||||||
|
|
||||||
// Validate arguments
|
|
||||||
if (typeof filename !== 'string') throw new TypeError('Expected first argument to be a string');
|
|
||||||
if (typeof options !== 'object') throw new TypeError('Expected second argument to be an options object');
|
|
||||||
|
|
||||||
// Interpret options
|
|
||||||
filename = filename.trim();
|
|
||||||
const attachedName = 'attached' in options ? options.attached : 'main';
|
|
||||||
const handler = 'progress' in options ? options.progress : null;
|
|
||||||
|
|
||||||
// Validate interpreted options
|
|
||||||
if (!filename) throw new TypeError('Backup filename cannot be an empty string');
|
|
||||||
if (filename === ':memory:') throw new TypeError('Invalid backup filename ":memory:"');
|
|
||||||
if (typeof attachedName !== 'string') throw new TypeError('Expected the "attached" option to be a string');
|
|
||||||
if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
|
|
||||||
if (handler != null && typeof handler !== 'function') throw new TypeError('Expected the "progress" option to be a function');
|
|
||||||
|
|
||||||
// Make sure the specified directory exists
|
|
||||||
await fsAccess(path.dirname(filename)).catch(() => {
|
|
||||||
throw new TypeError('Cannot save backup because the directory does not exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
const isNewFile = await fsAccess(filename).then(() => false, () => true);
|
|
||||||
return runBackup(this[cppdb].backup(this, attachedName, filename, isNewFile), handler || null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const runBackup = (backup, handler) => {
|
|
||||||
let rate = 0;
|
|
||||||
let useDefault = true;
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
setImmediate(function step() {
|
|
||||||
try {
|
|
||||||
const progress = backup.transfer(rate);
|
|
||||||
if (!progress.remainingPages) {
|
|
||||||
backup.close();
|
|
||||||
resolve(progress);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (useDefault) {
|
|
||||||
useDefault = false;
|
|
||||||
rate = 100;
|
|
||||||
}
|
|
||||||
if (handler) {
|
|
||||||
const ret = handler(progress);
|
|
||||||
if (ret !== undefined) {
|
|
||||||
if (typeof ret === 'number' && ret === ret) rate = Math.max(0, Math.min(0x7fffffff, Math.round(ret)));
|
|
||||||
else throw new TypeError('Expected progress callback to return a number or undefined');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setImmediate(step);
|
|
||||||
} catch (err) {
|
|
||||||
backup.close();
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
-31
@@ -1,31 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const { getBooleanOption, cppdb } = require('../util');
|
|
||||||
|
|
||||||
module.exports = function defineFunction(name, options, fn) {
|
|
||||||
// Apply defaults
|
|
||||||
if (options == null) options = {};
|
|
||||||
if (typeof options === 'function') { fn = options; options = {}; }
|
|
||||||
|
|
||||||
// Validate arguments
|
|
||||||
if (typeof name !== 'string') throw new TypeError('Expected first argument to be a string');
|
|
||||||
if (typeof fn !== 'function') throw new TypeError('Expected last argument to be a function');
|
|
||||||
if (typeof options !== 'object') throw new TypeError('Expected second argument to be an options object');
|
|
||||||
if (!name) throw new TypeError('User-defined function name cannot be an empty string');
|
|
||||||
|
|
||||||
// Interpret options
|
|
||||||
const safeIntegers = 'safeIntegers' in options ? +getBooleanOption(options, 'safeIntegers') : 2;
|
|
||||||
const deterministic = getBooleanOption(options, 'deterministic');
|
|
||||||
const directOnly = getBooleanOption(options, 'directOnly');
|
|
||||||
const varargs = getBooleanOption(options, 'varargs');
|
|
||||||
let argCount = -1;
|
|
||||||
|
|
||||||
// Determine argument count
|
|
||||||
if (!varargs) {
|
|
||||||
argCount = fn.length;
|
|
||||||
if (!Number.isInteger(argCount) || argCount < 0) throw new TypeError('Expected function.length to be a positive integer');
|
|
||||||
if (argCount > 100) throw new RangeError('User-defined functions cannot have more than 100 arguments');
|
|
||||||
}
|
|
||||||
|
|
||||||
this[cppdb].function(fn, name, argCount, safeIntegers, deterministic, directOnly);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
-7
@@ -1,7 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const DatabaseInspection = function Database() {};
|
|
||||||
|
|
||||||
module.exports = function inspect(depth, opts) {
|
|
||||||
return Object.assign(new DatabaseInspection(), this);
|
|
||||||
};
|
|
||||||
|
|
||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const { getBooleanOption, cppdb } = require('../util');
|
|
||||||
|
|
||||||
module.exports = function pragma(source, options) {
|
|
||||||
if (options == null) options = {};
|
|
||||||
if (typeof source !== 'string') throw new TypeError('Expected first argument to be a string');
|
|
||||||
if (typeof options !== 'object') throw new TypeError('Expected second argument to be an options object');
|
|
||||||
const simple = getBooleanOption(options, 'simple');
|
|
||||||
|
|
||||||
const stmt = this[cppdb].prepare(`PRAGMA ${source}`, this, true);
|
|
||||||
return simple ? stmt.pluck().get() : stmt.all();
|
|
||||||
};
|
|
||||||
-16
@@ -1,16 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const { cppdb } = require('../util');
|
|
||||||
|
|
||||||
module.exports = function serialize(options) {
|
|
||||||
if (options == null) options = {};
|
|
||||||
|
|
||||||
// Validate arguments
|
|
||||||
if (typeof options !== 'object') throw new TypeError('Expected first argument to be an options object');
|
|
||||||
|
|
||||||
// Interpret and validate options
|
|
||||||
const attachedName = 'attached' in options ? options.attached : 'main';
|
|
||||||
if (typeof attachedName !== 'string') throw new TypeError('Expected the "attached" option to be a string');
|
|
||||||
if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
|
|
||||||
|
|
||||||
return this[cppdb].serialize(attachedName);
|
|
||||||
};
|
|
||||||
-189
@@ -1,189 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const { cppdb } = require('../util');
|
|
||||||
|
|
||||||
module.exports = function defineTable(name, factory) {
|
|
||||||
// Validate arguments
|
|
||||||
if (typeof name !== 'string') throw new TypeError('Expected first argument to be a string');
|
|
||||||
if (!name) throw new TypeError('Virtual table module name cannot be an empty string');
|
|
||||||
|
|
||||||
// Determine whether the module is eponymous-only or not
|
|
||||||
let eponymous = false;
|
|
||||||
if (typeof factory === 'object' && factory !== null) {
|
|
||||||
eponymous = true;
|
|
||||||
factory = defer(parseTableDefinition(factory, 'used', name));
|
|
||||||
} else {
|
|
||||||
if (typeof factory !== 'function') throw new TypeError('Expected second argument to be a function or a table definition object');
|
|
||||||
factory = wrapFactory(factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
this[cppdb].table(factory, name, eponymous);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
function wrapFactory(factory) {
|
|
||||||
return function virtualTableFactory(moduleName, databaseName, tableName, ...args) {
|
|
||||||
const thisObject = {
|
|
||||||
module: moduleName,
|
|
||||||
database: databaseName,
|
|
||||||
table: tableName,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generate a new table definition by invoking the factory
|
|
||||||
const def = apply.call(factory, thisObject, args);
|
|
||||||
if (typeof def !== 'object' || def === null) {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" did not return a table definition object`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseTableDefinition(def, 'returned', moduleName);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseTableDefinition(def, verb, moduleName) {
|
|
||||||
// Validate required properties
|
|
||||||
if (!hasOwnProperty.call(def, 'rows')) {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition without a "rows" property`);
|
|
||||||
}
|
|
||||||
if (!hasOwnProperty.call(def, 'columns')) {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition without a "columns" property`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate "rows" property
|
|
||||||
const rows = def.rows;
|
|
||||||
if (typeof rows !== 'function' || Object.getPrototypeOf(rows) !== GeneratorFunctionPrototype) {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "rows" property (should be a generator function)`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate "columns" property
|
|
||||||
let columns = def.columns;
|
|
||||||
if (!Array.isArray(columns) || !(columns = [...columns]).every(x => typeof x === 'string')) {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "columns" property (should be an array of strings)`);
|
|
||||||
}
|
|
||||||
if (columns.length !== new Set(columns).size) {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with duplicate column names`);
|
|
||||||
}
|
|
||||||
if (!columns.length) {
|
|
||||||
throw new RangeError(`Virtual table module "${moduleName}" ${verb} a table definition with zero columns`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate "parameters" property
|
|
||||||
let parameters;
|
|
||||||
if (hasOwnProperty.call(def, 'parameters')) {
|
|
||||||
parameters = def.parameters;
|
|
||||||
if (!Array.isArray(parameters) || !(parameters = [...parameters]).every(x => typeof x === 'string')) {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "parameters" property (should be an array of strings)`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parameters = inferParameters(rows);
|
|
||||||
}
|
|
||||||
if (parameters.length !== new Set(parameters).size) {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with duplicate parameter names`);
|
|
||||||
}
|
|
||||||
if (parameters.length > 32) {
|
|
||||||
throw new RangeError(`Virtual table module "${moduleName}" ${verb} a table definition with more than the maximum number of 32 parameters`);
|
|
||||||
}
|
|
||||||
for (const parameter of parameters) {
|
|
||||||
if (columns.includes(parameter)) {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with column "${parameter}" which was ambiguously defined as both a column and parameter`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate "safeIntegers" option
|
|
||||||
let safeIntegers = 2;
|
|
||||||
if (hasOwnProperty.call(def, 'safeIntegers')) {
|
|
||||||
const bool = def.safeIntegers;
|
|
||||||
if (typeof bool !== 'boolean') {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "safeIntegers" property (should be a boolean)`);
|
|
||||||
}
|
|
||||||
safeIntegers = +bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate "directOnly" option
|
|
||||||
let directOnly = false;
|
|
||||||
if (hasOwnProperty.call(def, 'directOnly')) {
|
|
||||||
directOnly = def.directOnly;
|
|
||||||
if (typeof directOnly !== 'boolean') {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "directOnly" property (should be a boolean)`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate SQL for the virtual table definition
|
|
||||||
const columnDefinitions = [
|
|
||||||
...parameters.map(identifier).map(str => `${str} HIDDEN`),
|
|
||||||
...columns.map(identifier),
|
|
||||||
];
|
|
||||||
return [
|
|
||||||
`CREATE TABLE x(${columnDefinitions.join(', ')});`,
|
|
||||||
wrapGenerator(rows, new Map(columns.map((x, i) => [x, parameters.length + i])), moduleName),
|
|
||||||
parameters,
|
|
||||||
safeIntegers,
|
|
||||||
directOnly,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function wrapGenerator(generator, columnMap, moduleName) {
|
|
||||||
return function* virtualTable(...args) {
|
|
||||||
/*
|
|
||||||
We must defensively clone any buffers in the arguments, because
|
|
||||||
otherwise the generator could mutate one of them, which would cause
|
|
||||||
us to return incorrect values for hidden columns, potentially
|
|
||||||
corrupting the database.
|
|
||||||
*/
|
|
||||||
const output = args.map(x => Buffer.isBuffer(x) ? Buffer.from(x) : x);
|
|
||||||
for (let i = 0; i < columnMap.size; ++i) {
|
|
||||||
output.push(null); // Fill with nulls to prevent gaps in array (v8 optimization)
|
|
||||||
}
|
|
||||||
for (const row of generator(...args)) {
|
|
||||||
if (Array.isArray(row)) {
|
|
||||||
extractRowArray(row, output, columnMap.size, moduleName);
|
|
||||||
yield output;
|
|
||||||
} else if (typeof row === 'object' && row !== null) {
|
|
||||||
extractRowObject(row, output, columnMap, moduleName);
|
|
||||||
yield output;
|
|
||||||
} else {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" yielded something that isn't a valid row object`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractRowArray(row, output, columnCount, moduleName) {
|
|
||||||
if (row.length !== columnCount) {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" yielded a row with an incorrect number of columns`);
|
|
||||||
}
|
|
||||||
const offset = output.length - columnCount;
|
|
||||||
for (let i = 0; i < columnCount; ++i) {
|
|
||||||
output[i + offset] = row[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractRowObject(row, output, columnMap, moduleName) {
|
|
||||||
let count = 0;
|
|
||||||
for (const key of Object.keys(row)) {
|
|
||||||
const index = columnMap.get(key);
|
|
||||||
if (index === undefined) {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" yielded a row with an undeclared column "${key}"`);
|
|
||||||
}
|
|
||||||
output[index] = row[key];
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
if (count !== columnMap.size) {
|
|
||||||
throw new TypeError(`Virtual table module "${moduleName}" yielded a row with missing columns`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function inferParameters({ length }) {
|
|
||||||
if (!Number.isInteger(length) || length < 0) {
|
|
||||||
throw new TypeError('Expected function.length to be a positive integer');
|
|
||||||
}
|
|
||||||
const params = [];
|
|
||||||
for (let i = 0; i < length; ++i) {
|
|
||||||
params.push(`$${i + 1}`);
|
|
||||||
}
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { hasOwnProperty } = Object.prototype;
|
|
||||||
const { apply } = Function.prototype;
|
|
||||||
const GeneratorFunctionPrototype = Object.getPrototypeOf(function*(){});
|
|
||||||
const identifier = str => `"${str.replace(/"/g, '""')}"`;
|
|
||||||
const defer = x => () => x;
|
|
||||||
-78
@@ -1,78 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const { cppdb } = require('../util');
|
|
||||||
const controllers = new WeakMap();
|
|
||||||
|
|
||||||
module.exports = function transaction(fn) {
|
|
||||||
if (typeof fn !== 'function') throw new TypeError('Expected first argument to be a function');
|
|
||||||
|
|
||||||
const db = this[cppdb];
|
|
||||||
const controller = getController(db, this);
|
|
||||||
const { apply } = Function.prototype;
|
|
||||||
|
|
||||||
// Each version of the transaction function has these same properties
|
|
||||||
const properties = {
|
|
||||||
default: { value: wrapTransaction(apply, fn, db, controller.default) },
|
|
||||||
deferred: { value: wrapTransaction(apply, fn, db, controller.deferred) },
|
|
||||||
immediate: { value: wrapTransaction(apply, fn, db, controller.immediate) },
|
|
||||||
exclusive: { value: wrapTransaction(apply, fn, db, controller.exclusive) },
|
|
||||||
database: { value: this, enumerable: true },
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.defineProperties(properties.default.value, properties);
|
|
||||||
Object.defineProperties(properties.deferred.value, properties);
|
|
||||||
Object.defineProperties(properties.immediate.value, properties);
|
|
||||||
Object.defineProperties(properties.exclusive.value, properties);
|
|
||||||
|
|
||||||
// Return the default version of the transaction function
|
|
||||||
return properties.default.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return the database's cached transaction controller, or create a new one
|
|
||||||
const getController = (db, self) => {
|
|
||||||
let controller = controllers.get(db);
|
|
||||||
if (!controller) {
|
|
||||||
const shared = {
|
|
||||||
commit: db.prepare('COMMIT', self, false),
|
|
||||||
rollback: db.prepare('ROLLBACK', self, false),
|
|
||||||
savepoint: db.prepare('SAVEPOINT `\t_bs3.\t`', self, false),
|
|
||||||
release: db.prepare('RELEASE `\t_bs3.\t`', self, false),
|
|
||||||
rollbackTo: db.prepare('ROLLBACK TO `\t_bs3.\t`', self, false),
|
|
||||||
};
|
|
||||||
controllers.set(db, controller = {
|
|
||||||
default: Object.assign({ begin: db.prepare('BEGIN', self, false) }, shared),
|
|
||||||
deferred: Object.assign({ begin: db.prepare('BEGIN DEFERRED', self, false) }, shared),
|
|
||||||
immediate: Object.assign({ begin: db.prepare('BEGIN IMMEDIATE', self, false) }, shared),
|
|
||||||
exclusive: Object.assign({ begin: db.prepare('BEGIN EXCLUSIVE', self, false) }, shared),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return controller;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return a new transaction function by wrapping the given function
|
|
||||||
const wrapTransaction = (apply, fn, db, { begin, commit, rollback, savepoint, release, rollbackTo }) => function sqliteTransaction() {
|
|
||||||
let before, after, undo;
|
|
||||||
if (db.inTransaction) {
|
|
||||||
before = savepoint;
|
|
||||||
after = release;
|
|
||||||
undo = rollbackTo;
|
|
||||||
} else {
|
|
||||||
before = begin;
|
|
||||||
after = commit;
|
|
||||||
undo = rollback;
|
|
||||||
}
|
|
||||||
before.run();
|
|
||||||
try {
|
|
||||||
const result = apply.call(fn, this, arguments);
|
|
||||||
if (result && typeof result.then === 'function') {
|
|
||||||
throw new TypeError('Transaction function cannot return a promise');
|
|
||||||
}
|
|
||||||
after.run();
|
|
||||||
return result;
|
|
||||||
} catch (ex) {
|
|
||||||
if (db.inTransaction) {
|
|
||||||
undo.run();
|
|
||||||
if (undo !== rollback) after.run();
|
|
||||||
}
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
-54
@@ -1,54 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const { cppdb } = require('../util');
|
|
||||||
|
|
||||||
exports.prepare = function prepare(sql) {
|
|
||||||
return this[cppdb].prepare(sql, this, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.exec = function exec(sql) {
|
|
||||||
this[cppdb].exec(sql);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.close = function close() {
|
|
||||||
this[cppdb].close();
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.loadExtension = function loadExtension(...args) {
|
|
||||||
this[cppdb].loadExtension(...args);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.defaultSafeIntegers = function defaultSafeIntegers(...args) {
|
|
||||||
this[cppdb].defaultSafeIntegers(...args);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.unsafeMode = function unsafeMode(...args) {
|
|
||||||
this[cppdb].unsafeMode(...args);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.getters = {
|
|
||||||
name: {
|
|
||||||
get: function name() { return this[cppdb].name; },
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
open: {
|
|
||||||
get: function open() { return this[cppdb].open; },
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
inTransaction: {
|
|
||||||
get: function inTransaction() { return this[cppdb].inTransaction; },
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
readonly: {
|
|
||||||
get: function readonly() { return this[cppdb].readonly; },
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
memory: {
|
|
||||||
get: function memory() { return this[cppdb].memory; },
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const descriptor = { value: 'SqliteError', writable: true, enumerable: false, configurable: true };
|
|
||||||
|
|
||||||
function SqliteError(message, code) {
|
|
||||||
if (new.target !== SqliteError) {
|
|
||||||
return new SqliteError(message, code);
|
|
||||||
}
|
|
||||||
if (typeof code !== 'string') {
|
|
||||||
throw new TypeError('Expected second argument to be a string');
|
|
||||||
}
|
|
||||||
Error.call(this, message);
|
|
||||||
descriptor.value = '' + message;
|
|
||||||
Object.defineProperty(this, 'message', descriptor);
|
|
||||||
Error.captureStackTrace(this, SqliteError);
|
|
||||||
this.code = code;
|
|
||||||
}
|
|
||||||
Object.setPrototypeOf(SqliteError, Error);
|
|
||||||
Object.setPrototypeOf(SqliteError.prototype, Error.prototype);
|
|
||||||
Object.defineProperty(SqliteError.prototype, 'name', descriptor);
|
|
||||||
module.exports = SqliteError;
|
|
||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
exports.getBooleanOption = (options, key) => {
|
|
||||||
let value = false;
|
|
||||||
if (key in options && typeof (value = options[key]) !== 'boolean') {
|
|
||||||
throw new TypeError(`Expected the "${key}" option to be a boolean`);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.cppdb = Symbol();
|
|
||||||
exports.inspect = Symbol.for('nodejs.util.inspect.custom');
|
|
||||||
-54
@@ -1,54 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "better-sqlite3",
|
|
||||||
"version": "11.10.0",
|
|
||||||
"description": "The fastest and simplest library for SQLite in Node.js.",
|
|
||||||
"homepage": "http://github.com/WiseLibs/better-sqlite3",
|
|
||||||
"author": "Joshua Wise <joshuathomaswise@gmail.com>",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git://github.com/WiseLibs/better-sqlite3.git"
|
|
||||||
},
|
|
||||||
"main": "lib/index.js",
|
|
||||||
"files": [
|
|
||||||
"binding.gyp",
|
|
||||||
"src/*.[ch]pp",
|
|
||||||
"lib/**",
|
|
||||||
"deps/**"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"bindings": "^1.5.0",
|
|
||||||
"prebuild-install": "^7.1.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"chai": "^4.3.8",
|
|
||||||
"cli-color": "^2.0.3",
|
|
||||||
"fs-extra": "^11.1.1",
|
|
||||||
"mocha": "^10.2.0",
|
|
||||||
"nodemark": "^0.3.0",
|
|
||||||
"prebuild": "^13.0.1",
|
|
||||||
"sqlite": "^5.0.1",
|
|
||||||
"sqlite3": "^5.1.6"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"install": "prebuild-install || node-gyp rebuild --release",
|
|
||||||
"build-release": "node-gyp rebuild --release",
|
|
||||||
"build-debug": "node-gyp rebuild --debug",
|
|
||||||
"rebuild-release": "npm run lzz && npm run build-release",
|
|
||||||
"rebuild-debug": "npm run lzz && npm run build-debug",
|
|
||||||
"test": "mocha --exit --slow=75 --timeout=5000",
|
|
||||||
"benchmark": "node benchmark",
|
|
||||||
"download": "bash ./deps/download.sh",
|
|
||||||
"lzz": "lzz -hx hpp -sx cpp -k BETTER_SQLITE3 -d -hl -sl -e ./src/better_sqlite3.lzz"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"keywords": [
|
|
||||||
"sql",
|
|
||||||
"sqlite",
|
|
||||||
"sqlite3",
|
|
||||||
"transactions",
|
|
||||||
"user-defined functions",
|
|
||||||
"aggregate functions",
|
|
||||||
"window functions",
|
|
||||||
"database"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
-2186
File diff suppressed because it is too large
Load Diff
-1036
File diff suppressed because it is too large
Load Diff
-22
@@ -1,22 +0,0 @@
|
|||||||
(The MIT License)
|
|
||||||
|
|
||||||
Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net>
|
|
||||||
|
|
||||||
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.
|
|
||||||
-98
@@ -1,98 +0,0 @@
|
|||||||
node-bindings
|
|
||||||
=============
|
|
||||||
### Helper module for loading your native module's `.node` file
|
|
||||||
|
|
||||||
This is a helper module for authors of Node.js native addon modules.
|
|
||||||
It is basically the "swiss army knife" of `require()`ing your native module's
|
|
||||||
`.node` file.
|
|
||||||
|
|
||||||
Throughout the course of Node's native addon history, addons have ended up being
|
|
||||||
compiled in a variety of different places, depending on which build tool and which
|
|
||||||
version of node was used. To make matters worse, now the `gyp` build tool can
|
|
||||||
produce either a __Release__ or __Debug__ build, each being built into different
|
|
||||||
locations.
|
|
||||||
|
|
||||||
This module checks _all_ the possible locations that a native addon would be built
|
|
||||||
at, and returns the first one that loads successfully.
|
|
||||||
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
Install with `npm`:
|
|
||||||
|
|
||||||
``` bash
|
|
||||||
$ npm install --save bindings
|
|
||||||
```
|
|
||||||
|
|
||||||
Or add it to the `"dependencies"` section of your `package.json` file.
|
|
||||||
|
|
||||||
|
|
||||||
Example
|
|
||||||
-------
|
|
||||||
|
|
||||||
`require()`ing the proper bindings file for the current node version, platform
|
|
||||||
and architecture is as simple as:
|
|
||||||
|
|
||||||
``` js
|
|
||||||
var bindings = require('bindings')('binding.node')
|
|
||||||
|
|
||||||
// Use your bindings defined in your C files
|
|
||||||
bindings.your_c_function()
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Nice Error Output
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
When the `.node` file could not be loaded, `node-bindings` throws an Error with
|
|
||||||
a nice error message telling you exactly what was tried. You can also check the
|
|
||||||
`err.tries` Array property.
|
|
||||||
|
|
||||||
```
|
|
||||||
Error: Could not load the bindings file. Tried:
|
|
||||||
→ /Users/nrajlich/ref/build/binding.node
|
|
||||||
→ /Users/nrajlich/ref/build/Debug/binding.node
|
|
||||||
→ /Users/nrajlich/ref/build/Release/binding.node
|
|
||||||
→ /Users/nrajlich/ref/out/Debug/binding.node
|
|
||||||
→ /Users/nrajlich/ref/Debug/binding.node
|
|
||||||
→ /Users/nrajlich/ref/out/Release/binding.node
|
|
||||||
→ /Users/nrajlich/ref/Release/binding.node
|
|
||||||
→ /Users/nrajlich/ref/build/default/binding.node
|
|
||||||
→ /Users/nrajlich/ref/compiled/0.8.2/darwin/x64/binding.node
|
|
||||||
at bindings (/Users/nrajlich/ref/node_modules/bindings/bindings.js:84:13)
|
|
||||||
at Object.<anonymous> (/Users/nrajlich/ref/lib/ref.js:5:47)
|
|
||||||
at Module._compile (module.js:449:26)
|
|
||||||
at Object.Module._extensions..js (module.js:467:10)
|
|
||||||
at Module.load (module.js:356:32)
|
|
||||||
at Function.Module._load (module.js:312:12)
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
The searching for the `.node` file will originate from the first directory in which has a `package.json` file is found.
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
|
|
||||||
(The MIT License)
|
|
||||||
|
|
||||||
Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net>
|
|
||||||
|
|
||||||
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.
|
|
||||||
-221
@@ -1,221 +0,0 @@
|
|||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var fs = require('fs'),
|
|
||||||
path = require('path'),
|
|
||||||
fileURLToPath = require('file-uri-to-path'),
|
|
||||||
join = path.join,
|
|
||||||
dirname = path.dirname,
|
|
||||||
exists =
|
|
||||||
(fs.accessSync &&
|
|
||||||
function(path) {
|
|
||||||
try {
|
|
||||||
fs.accessSync(path);
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}) ||
|
|
||||||
fs.existsSync ||
|
|
||||||
path.existsSync,
|
|
||||||
defaults = {
|
|
||||||
arrow: process.env.NODE_BINDINGS_ARROW || ' → ',
|
|
||||||
compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled',
|
|
||||||
platform: process.platform,
|
|
||||||
arch: process.arch,
|
|
||||||
nodePreGyp:
|
|
||||||
'node-v' +
|
|
||||||
process.versions.modules +
|
|
||||||
'-' +
|
|
||||||
process.platform +
|
|
||||||
'-' +
|
|
||||||
process.arch,
|
|
||||||
version: process.versions.node,
|
|
||||||
bindings: 'bindings.node',
|
|
||||||
try: [
|
|
||||||
// node-gyp's linked version in the "build" dir
|
|
||||||
['module_root', 'build', 'bindings'],
|
|
||||||
// node-waf and gyp_addon (a.k.a node-gyp)
|
|
||||||
['module_root', 'build', 'Debug', 'bindings'],
|
|
||||||
['module_root', 'build', 'Release', 'bindings'],
|
|
||||||
// Debug files, for development (legacy behavior, remove for node v0.9)
|
|
||||||
['module_root', 'out', 'Debug', 'bindings'],
|
|
||||||
['module_root', 'Debug', 'bindings'],
|
|
||||||
// Release files, but manually compiled (legacy behavior, remove for node v0.9)
|
|
||||||
['module_root', 'out', 'Release', 'bindings'],
|
|
||||||
['module_root', 'Release', 'bindings'],
|
|
||||||
// Legacy from node-waf, node <= 0.4.x
|
|
||||||
['module_root', 'build', 'default', 'bindings'],
|
|
||||||
// Production "Release" buildtype binary (meh...)
|
|
||||||
['module_root', 'compiled', 'version', 'platform', 'arch', 'bindings'],
|
|
||||||
// node-qbs builds
|
|
||||||
['module_root', 'addon-build', 'release', 'install-root', 'bindings'],
|
|
||||||
['module_root', 'addon-build', 'debug', 'install-root', 'bindings'],
|
|
||||||
['module_root', 'addon-build', 'default', 'install-root', 'bindings'],
|
|
||||||
// node-pre-gyp path ./lib/binding/{node_abi}-{platform}-{arch}
|
|
||||||
['module_root', 'lib', 'binding', 'nodePreGyp', 'bindings']
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main `bindings()` function loads the compiled bindings for a given module.
|
|
||||||
* It uses V8's Error API to determine the parent filename that this function is
|
|
||||||
* being invoked from, which is then used to find the root directory.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function bindings(opts) {
|
|
||||||
// Argument surgery
|
|
||||||
if (typeof opts == 'string') {
|
|
||||||
opts = { bindings: opts };
|
|
||||||
} else if (!opts) {
|
|
||||||
opts = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// maps `defaults` onto `opts` object
|
|
||||||
Object.keys(defaults).map(function(i) {
|
|
||||||
if (!(i in opts)) opts[i] = defaults[i];
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get the module root
|
|
||||||
if (!opts.module_root) {
|
|
||||||
opts.module_root = exports.getRoot(exports.getFileName());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the given bindings name ends with .node
|
|
||||||
if (path.extname(opts.bindings) != '.node') {
|
|
||||||
opts.bindings += '.node';
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/webpack/webpack/issues/4175#issuecomment-342931035
|
|
||||||
var requireFunc =
|
|
||||||
typeof __webpack_require__ === 'function'
|
|
||||||
? __non_webpack_require__
|
|
||||||
: require;
|
|
||||||
|
|
||||||
var tries = [],
|
|
||||||
i = 0,
|
|
||||||
l = opts.try.length,
|
|
||||||
n,
|
|
||||||
b,
|
|
||||||
err;
|
|
||||||
|
|
||||||
for (; i < l; i++) {
|
|
||||||
n = join.apply(
|
|
||||||
null,
|
|
||||||
opts.try[i].map(function(p) {
|
|
||||||
return opts[p] || p;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
tries.push(n);
|
|
||||||
try {
|
|
||||||
b = opts.path ? requireFunc.resolve(n) : requireFunc(n);
|
|
||||||
if (!opts.path) {
|
|
||||||
b.path = n;
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
} catch (e) {
|
|
||||||
if (e.code !== 'MODULE_NOT_FOUND' &&
|
|
||||||
e.code !== 'QUALIFIED_PATH_RESOLUTION_FAILED' &&
|
|
||||||
!/not find/i.test(e.message)) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = new Error(
|
|
||||||
'Could not locate the bindings file. Tried:\n' +
|
|
||||||
tries
|
|
||||||
.map(function(a) {
|
|
||||||
return opts.arrow + a;
|
|
||||||
})
|
|
||||||
.join('\n')
|
|
||||||
);
|
|
||||||
err.tries = tries;
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
module.exports = exports = bindings;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the filename of the JavaScript file that invokes this function.
|
|
||||||
* Used to help find the root directory of a module.
|
|
||||||
* Optionally accepts an filename argument to skip when searching for the invoking filename
|
|
||||||
*/
|
|
||||||
|
|
||||||
exports.getFileName = function getFileName(calling_file) {
|
|
||||||
var origPST = Error.prepareStackTrace,
|
|
||||||
origSTL = Error.stackTraceLimit,
|
|
||||||
dummy = {},
|
|
||||||
fileName;
|
|
||||||
|
|
||||||
Error.stackTraceLimit = 10;
|
|
||||||
|
|
||||||
Error.prepareStackTrace = function(e, st) {
|
|
||||||
for (var i = 0, l = st.length; i < l; i++) {
|
|
||||||
fileName = st[i].getFileName();
|
|
||||||
if (fileName !== __filename) {
|
|
||||||
if (calling_file) {
|
|
||||||
if (fileName !== calling_file) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// run the 'prepareStackTrace' function above
|
|
||||||
Error.captureStackTrace(dummy);
|
|
||||||
dummy.stack;
|
|
||||||
|
|
||||||
// cleanup
|
|
||||||
Error.prepareStackTrace = origPST;
|
|
||||||
Error.stackTraceLimit = origSTL;
|
|
||||||
|
|
||||||
// handle filename that starts with "file://"
|
|
||||||
var fileSchema = 'file://';
|
|
||||||
if (fileName.indexOf(fileSchema) === 0) {
|
|
||||||
fileName = fileURLToPath(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileName;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the root directory of a module, given an arbitrary filename
|
|
||||||
* somewhere in the module tree. The "root directory" is the directory
|
|
||||||
* containing the `package.json` file.
|
|
||||||
*
|
|
||||||
* In: /home/nate/node-native-module/lib/index.js
|
|
||||||
* Out: /home/nate/node-native-module
|
|
||||||
*/
|
|
||||||
|
|
||||||
exports.getRoot = function getRoot(file) {
|
|
||||||
var dir = dirname(file),
|
|
||||||
prev;
|
|
||||||
while (true) {
|
|
||||||
if (dir === '.') {
|
|
||||||
// Avoids an infinite loop in rare cases, like the REPL
|
|
||||||
dir = process.cwd();
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
exists(join(dir, 'package.json')) ||
|
|
||||||
exists(join(dir, 'node_modules'))
|
|
||||||
) {
|
|
||||||
// Found the 'package.json' file or 'node_modules' dir; we're done
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
if (prev === dir) {
|
|
||||||
// Got to the top
|
|
||||||
throw new Error(
|
|
||||||
'Could not find module root given file: "' +
|
|
||||||
file +
|
|
||||||
'". Do you have a `package.json` file? '
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Try the parent dir next
|
|
||||||
prev = dir;
|
|
||||||
dir = join(dir, '..');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
-28
@@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "bindings",
|
|
||||||
"description": "Helper module for loading your native module's .node file",
|
|
||||||
"keywords": [
|
|
||||||
"native",
|
|
||||||
"addon",
|
|
||||||
"bindings",
|
|
||||||
"gyp",
|
|
||||||
"waf",
|
|
||||||
"c",
|
|
||||||
"c++"
|
|
||||||
],
|
|
||||||
"version": "1.5.0",
|
|
||||||
"author": "Nathan Rajlich <nathan@tootallnate.net> (http://tootallnate.net)",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git://github.com/TooTallNate/node-bindings.git"
|
|
||||||
},
|
|
||||||
"main": "./bindings.js",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/TooTallNate/node-bindings/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/TooTallNate/node-bindings",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"file-uri-to-path": "1.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
sudo: false
|
|
||||||
arch:
|
|
||||||
- amd64
|
|
||||||
- ppc64le
|
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- '6'
|
|
||||||
- '8'
|
|
||||||
- '10'
|
|
||||||
- '12'
|
|
||||||
- '14'
|
|
||||||
- '15'
|
|
||||||
- lts/*
|
|
||||||
notifications:
|
|
||||||
email:
|
|
||||||
- rod@vagg.org
|
|
||||||
- matteo.collina@gmail.com
|
|
||||||
-396
@@ -1,396 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
const { Buffer } = require('buffer')
|
|
||||||
const symbol = Symbol.for('BufferList')
|
|
||||||
|
|
||||||
function BufferList (buf) {
|
|
||||||
if (!(this instanceof BufferList)) {
|
|
||||||
return new BufferList(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList._init.call(this, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList._init = function _init (buf) {
|
|
||||||
Object.defineProperty(this, symbol, { value: true })
|
|
||||||
|
|
||||||
this._bufs = []
|
|
||||||
this.length = 0
|
|
||||||
|
|
||||||
if (buf) {
|
|
||||||
this.append(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype._new = function _new (buf) {
|
|
||||||
return new BufferList(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype._offset = function _offset (offset) {
|
|
||||||
if (offset === 0) {
|
|
||||||
return [0, 0]
|
|
||||||
}
|
|
||||||
|
|
||||||
let tot = 0
|
|
||||||
|
|
||||||
for (let i = 0; i < this._bufs.length; i++) {
|
|
||||||
const _t = tot + this._bufs[i].length
|
|
||||||
if (offset < _t || i === this._bufs.length - 1) {
|
|
||||||
return [i, offset - tot]
|
|
||||||
}
|
|
||||||
tot = _t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype._reverseOffset = function (blOffset) {
|
|
||||||
const bufferId = blOffset[0]
|
|
||||||
let offset = blOffset[1]
|
|
||||||
|
|
||||||
for (let i = 0; i < bufferId; i++) {
|
|
||||||
offset += this._bufs[i].length
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype.get = function get (index) {
|
|
||||||
if (index > this.length || index < 0) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const offset = this._offset(index)
|
|
||||||
|
|
||||||
return this._bufs[offset[0]][offset[1]]
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype.slice = function slice (start, end) {
|
|
||||||
if (typeof start === 'number' && start < 0) {
|
|
||||||
start += this.length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof end === 'number' && end < 0) {
|
|
||||||
end += this.length
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.copy(null, 0, start, end)
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype.copy = function copy (dst, dstStart, srcStart, srcEnd) {
|
|
||||||
if (typeof srcStart !== 'number' || srcStart < 0) {
|
|
||||||
srcStart = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof srcEnd !== 'number' || srcEnd > this.length) {
|
|
||||||
srcEnd = this.length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (srcStart >= this.length) {
|
|
||||||
return dst || Buffer.alloc(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (srcEnd <= 0) {
|
|
||||||
return dst || Buffer.alloc(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
const copy = !!dst
|
|
||||||
const off = this._offset(srcStart)
|
|
||||||
const len = srcEnd - srcStart
|
|
||||||
let bytes = len
|
|
||||||
let bufoff = (copy && dstStart) || 0
|
|
||||||
let start = off[1]
|
|
||||||
|
|
||||||
// copy/slice everything
|
|
||||||
if (srcStart === 0 && srcEnd === this.length) {
|
|
||||||
if (!copy) {
|
|
||||||
// slice, but full concat if multiple buffers
|
|
||||||
return this._bufs.length === 1
|
|
||||||
? this._bufs[0]
|
|
||||||
: Buffer.concat(this._bufs, this.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy, need to copy individual buffers
|
|
||||||
for (let i = 0; i < this._bufs.length; i++) {
|
|
||||||
this._bufs[i].copy(dst, bufoff)
|
|
||||||
bufoff += this._bufs[i].length
|
|
||||||
}
|
|
||||||
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// easy, cheap case where it's a subset of one of the buffers
|
|
||||||
if (bytes <= this._bufs[off[0]].length - start) {
|
|
||||||
return copy
|
|
||||||
? this._bufs[off[0]].copy(dst, dstStart, start, start + bytes)
|
|
||||||
: this._bufs[off[0]].slice(start, start + bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!copy) {
|
|
||||||
// a slice, we need something to copy in to
|
|
||||||
dst = Buffer.allocUnsafe(len)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = off[0]; i < this._bufs.length; i++) {
|
|
||||||
const l = this._bufs[i].length - start
|
|
||||||
|
|
||||||
if (bytes > l) {
|
|
||||||
this._bufs[i].copy(dst, bufoff, start)
|
|
||||||
bufoff += l
|
|
||||||
} else {
|
|
||||||
this._bufs[i].copy(dst, bufoff, start, start + bytes)
|
|
||||||
bufoff += l
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes -= l
|
|
||||||
|
|
||||||
if (start) {
|
|
||||||
start = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// safeguard so that we don't return uninitialized memory
|
|
||||||
if (dst.length > bufoff) return dst.slice(0, bufoff)
|
|
||||||
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype.shallowSlice = function shallowSlice (start, end) {
|
|
||||||
start = start || 0
|
|
||||||
end = typeof end !== 'number' ? this.length : end
|
|
||||||
|
|
||||||
if (start < 0) {
|
|
||||||
start += this.length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end < 0) {
|
|
||||||
end += this.length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start === end) {
|
|
||||||
return this._new()
|
|
||||||
}
|
|
||||||
|
|
||||||
const startOffset = this._offset(start)
|
|
||||||
const endOffset = this._offset(end)
|
|
||||||
const buffers = this._bufs.slice(startOffset[0], endOffset[0] + 1)
|
|
||||||
|
|
||||||
if (endOffset[1] === 0) {
|
|
||||||
buffers.pop()
|
|
||||||
} else {
|
|
||||||
buffers[buffers.length - 1] = buffers[buffers.length - 1].slice(0, endOffset[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startOffset[1] !== 0) {
|
|
||||||
buffers[0] = buffers[0].slice(startOffset[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._new(buffers)
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype.toString = function toString (encoding, start, end) {
|
|
||||||
return this.slice(start, end).toString(encoding)
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype.consume = function consume (bytes) {
|
|
||||||
// first, normalize the argument, in accordance with how Buffer does it
|
|
||||||
bytes = Math.trunc(bytes)
|
|
||||||
// do nothing if not a positive number
|
|
||||||
if (Number.isNaN(bytes) || bytes <= 0) return this
|
|
||||||
|
|
||||||
while (this._bufs.length) {
|
|
||||||
if (bytes >= this._bufs[0].length) {
|
|
||||||
bytes -= this._bufs[0].length
|
|
||||||
this.length -= this._bufs[0].length
|
|
||||||
this._bufs.shift()
|
|
||||||
} else {
|
|
||||||
this._bufs[0] = this._bufs[0].slice(bytes)
|
|
||||||
this.length -= bytes
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype.duplicate = function duplicate () {
|
|
||||||
const copy = this._new()
|
|
||||||
|
|
||||||
for (let i = 0; i < this._bufs.length; i++) {
|
|
||||||
copy.append(this._bufs[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
return copy
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype.append = function append (buf) {
|
|
||||||
if (buf == null) {
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf.buffer) {
|
|
||||||
// append a view of the underlying ArrayBuffer
|
|
||||||
this._appendBuffer(Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength))
|
|
||||||
} else if (Array.isArray(buf)) {
|
|
||||||
for (let i = 0; i < buf.length; i++) {
|
|
||||||
this.append(buf[i])
|
|
||||||
}
|
|
||||||
} else if (this._isBufferList(buf)) {
|
|
||||||
// unwrap argument into individual BufferLists
|
|
||||||
for (let i = 0; i < buf._bufs.length; i++) {
|
|
||||||
this.append(buf._bufs[i])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// coerce number arguments to strings, since Buffer(number) does
|
|
||||||
// uninitialized memory allocation
|
|
||||||
if (typeof buf === 'number') {
|
|
||||||
buf = buf.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
this._appendBuffer(Buffer.from(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype._appendBuffer = function appendBuffer (buf) {
|
|
||||||
this._bufs.push(buf)
|
|
||||||
this.length += buf.length
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype.indexOf = function (search, offset, encoding) {
|
|
||||||
if (encoding === undefined && typeof offset === 'string') {
|
|
||||||
encoding = offset
|
|
||||||
offset = undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof search === 'function' || Array.isArray(search)) {
|
|
||||||
throw new TypeError('The "value" argument must be one of type string, Buffer, BufferList, or Uint8Array.')
|
|
||||||
} else if (typeof search === 'number') {
|
|
||||||
search = Buffer.from([search])
|
|
||||||
} else if (typeof search === 'string') {
|
|
||||||
search = Buffer.from(search, encoding)
|
|
||||||
} else if (this._isBufferList(search)) {
|
|
||||||
search = search.slice()
|
|
||||||
} else if (Array.isArray(search.buffer)) {
|
|
||||||
search = Buffer.from(search.buffer, search.byteOffset, search.byteLength)
|
|
||||||
} else if (!Buffer.isBuffer(search)) {
|
|
||||||
search = Buffer.from(search)
|
|
||||||
}
|
|
||||||
|
|
||||||
offset = Number(offset || 0)
|
|
||||||
|
|
||||||
if (isNaN(offset)) {
|
|
||||||
offset = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset < 0) {
|
|
||||||
offset = this.length + offset
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset < 0) {
|
|
||||||
offset = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if (search.length === 0) {
|
|
||||||
return offset > this.length ? this.length : offset
|
|
||||||
}
|
|
||||||
|
|
||||||
const blOffset = this._offset(offset)
|
|
||||||
let blIndex = blOffset[0] // index of which internal buffer we're working on
|
|
||||||
let buffOffset = blOffset[1] // offset of the internal buffer we're working on
|
|
||||||
|
|
||||||
// scan over each buffer
|
|
||||||
for (; blIndex < this._bufs.length; blIndex++) {
|
|
||||||
const buff = this._bufs[blIndex]
|
|
||||||
|
|
||||||
while (buffOffset < buff.length) {
|
|
||||||
const availableWindow = buff.length - buffOffset
|
|
||||||
|
|
||||||
if (availableWindow >= search.length) {
|
|
||||||
const nativeSearchResult = buff.indexOf(search, buffOffset)
|
|
||||||
|
|
||||||
if (nativeSearchResult !== -1) {
|
|
||||||
return this._reverseOffset([blIndex, nativeSearchResult])
|
|
||||||
}
|
|
||||||
|
|
||||||
buffOffset = buff.length - search.length + 1 // end of native search window
|
|
||||||
} else {
|
|
||||||
const revOffset = this._reverseOffset([blIndex, buffOffset])
|
|
||||||
|
|
||||||
if (this._match(revOffset, search)) {
|
|
||||||
return revOffset
|
|
||||||
}
|
|
||||||
|
|
||||||
buffOffset++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffOffset = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.prototype._match = function (offset, search) {
|
|
||||||
if (this.length - offset < search.length) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let searchOffset = 0; searchOffset < search.length; searchOffset++) {
|
|
||||||
if (this.get(offset + searchOffset) !== search[searchOffset]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
;(function () {
|
|
||||||
const methods = {
|
|
||||||
readDoubleBE: 8,
|
|
||||||
readDoubleLE: 8,
|
|
||||||
readFloatBE: 4,
|
|
||||||
readFloatLE: 4,
|
|
||||||
readInt32BE: 4,
|
|
||||||
readInt32LE: 4,
|
|
||||||
readUInt32BE: 4,
|
|
||||||
readUInt32LE: 4,
|
|
||||||
readInt16BE: 2,
|
|
||||||
readInt16LE: 2,
|
|
||||||
readUInt16BE: 2,
|
|
||||||
readUInt16LE: 2,
|
|
||||||
readInt8: 1,
|
|
||||||
readUInt8: 1,
|
|
||||||
readIntBE: null,
|
|
||||||
readIntLE: null,
|
|
||||||
readUIntBE: null,
|
|
||||||
readUIntLE: null
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const m in methods) {
|
|
||||||
(function (m) {
|
|
||||||
if (methods[m] === null) {
|
|
||||||
BufferList.prototype[m] = function (offset, byteLength) {
|
|
||||||
return this.slice(offset, offset + byteLength)[m](0, byteLength)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
BufferList.prototype[m] = function (offset = 0) {
|
|
||||||
return this.slice(offset, offset + methods[m])[m](0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}(m))
|
|
||||||
}
|
|
||||||
}())
|
|
||||||
|
|
||||||
// Used internally by the class and also as an indicator of this object being
|
|
||||||
// a `BufferList`. It's not possible to use `instanceof BufferList` in a browser
|
|
||||||
// environment because there could be multiple different copies of the
|
|
||||||
// BufferList class and some `BufferList`s might be `BufferList`s.
|
|
||||||
BufferList.prototype._isBufferList = function _isBufferList (b) {
|
|
||||||
return b instanceof BufferList || BufferList.isBufferList(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList.isBufferList = function isBufferList (b) {
|
|
||||||
return b != null && b[symbol]
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = BufferList
|
|
||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
=====================
|
|
||||||
|
|
||||||
Copyright (c) 2013-2019 bl contributors
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
*bl contributors listed at <https://github.com/rvagg/bl#contributors>*
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
-247
@@ -1,247 +0,0 @@
|
|||||||
# bl *(BufferList)*
|
|
||||||
|
|
||||||
[](https://travis-ci.com/rvagg/bl/)
|
|
||||||
|
|
||||||
**A Node.js Buffer list collector, reader and streamer thingy.**
|
|
||||||
|
|
||||||
[](https://nodei.co/npm/bl/)
|
|
||||||
|
|
||||||
**bl** is a storage object for collections of Node Buffers, exposing them with the main Buffer readable API. Also works as a duplex stream so you can collect buffers from a stream that emits them and emit buffers to a stream that consumes them!
|
|
||||||
|
|
||||||
The original buffers are kept intact and copies are only done as necessary. Any reads that require the use of a single original buffer will return a slice of that buffer only (which references the same memory as the original buffer). Reads that span buffers perform concatenation as required and return the results transparently.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const { BufferList } = require('bl')
|
|
||||||
|
|
||||||
const bl = new BufferList()
|
|
||||||
bl.append(Buffer.from('abcd'))
|
|
||||||
bl.append(Buffer.from('efg'))
|
|
||||||
bl.append('hi') // bl will also accept & convert Strings
|
|
||||||
bl.append(Buffer.from('j'))
|
|
||||||
bl.append(Buffer.from([ 0x3, 0x4 ]))
|
|
||||||
|
|
||||||
console.log(bl.length) // 12
|
|
||||||
|
|
||||||
console.log(bl.slice(0, 10).toString('ascii')) // 'abcdefghij'
|
|
||||||
console.log(bl.slice(3, 10).toString('ascii')) // 'defghij'
|
|
||||||
console.log(bl.slice(3, 6).toString('ascii')) // 'def'
|
|
||||||
console.log(bl.slice(3, 8).toString('ascii')) // 'defgh'
|
|
||||||
console.log(bl.slice(5, 10).toString('ascii')) // 'fghij'
|
|
||||||
|
|
||||||
console.log(bl.indexOf('def')) // 3
|
|
||||||
console.log(bl.indexOf('asdf')) // -1
|
|
||||||
|
|
||||||
// or just use toString!
|
|
||||||
console.log(bl.toString()) // 'abcdefghij\u0003\u0004'
|
|
||||||
console.log(bl.toString('ascii', 3, 8)) // 'defgh'
|
|
||||||
console.log(bl.toString('ascii', 5, 10)) // 'fghij'
|
|
||||||
|
|
||||||
// other standard Buffer readables
|
|
||||||
console.log(bl.readUInt16BE(10)) // 0x0304
|
|
||||||
console.log(bl.readUInt16LE(10)) // 0x0403
|
|
||||||
```
|
|
||||||
|
|
||||||
Give it a callback in the constructor and use it just like **[concat-stream](https://github.com/maxogden/node-concat-stream)**:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const { BufferListStream } = require('bl')
|
|
||||||
const fs = require('fs')
|
|
||||||
|
|
||||||
fs.createReadStream('README.md')
|
|
||||||
.pipe(BufferListStream((err, data) => { // note 'new' isn't strictly required
|
|
||||||
// `data` is a complete Buffer object containing the full data
|
|
||||||
console.log(data.toString())
|
|
||||||
}))
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that when you use the *callback* method like this, the resulting `data` parameter is a concatenation of all `Buffer` objects in the list. If you want to avoid the overhead of this concatenation (in cases of extreme performance consciousness), then avoid the *callback* method and just listen to `'end'` instead, like a standard Stream.
|
|
||||||
|
|
||||||
Or to fetch a URL using [hyperquest](https://github.com/substack/hyperquest) (should work with [request](http://github.com/mikeal/request) and even plain Node http too!):
|
|
||||||
|
|
||||||
```js
|
|
||||||
const hyperquest = require('hyperquest')
|
|
||||||
const { BufferListStream } = require('bl')
|
|
||||||
|
|
||||||
const url = 'https://raw.github.com/rvagg/bl/master/README.md'
|
|
||||||
|
|
||||||
hyperquest(url).pipe(BufferListStream((err, data) => {
|
|
||||||
console.log(data.toString())
|
|
||||||
}))
|
|
||||||
```
|
|
||||||
|
|
||||||
Or, use it as a readable stream to recompose a list of Buffers to an output source:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const { BufferListStream } = require('bl')
|
|
||||||
const fs = require('fs')
|
|
||||||
|
|
||||||
var bl = new BufferListStream()
|
|
||||||
bl.append(Buffer.from('abcd'))
|
|
||||||
bl.append(Buffer.from('efg'))
|
|
||||||
bl.append(Buffer.from('hi'))
|
|
||||||
bl.append(Buffer.from('j'))
|
|
||||||
|
|
||||||
bl.pipe(fs.createWriteStream('gibberish.txt'))
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
* <a href="#ctor"><code><b>new BufferList([ buf ])</b></code></a>
|
|
||||||
* <a href="#isBufferList"><code><b>BufferList.isBufferList(obj)</b></code></a>
|
|
||||||
* <a href="#length"><code>bl.<b>length</b></code></a>
|
|
||||||
* <a href="#append"><code>bl.<b>append(buffer)</b></code></a>
|
|
||||||
* <a href="#get"><code>bl.<b>get(index)</b></code></a>
|
|
||||||
* <a href="#indexOf"><code>bl.<b>indexOf(value[, byteOffset][, encoding])</b></code></a>
|
|
||||||
* <a href="#slice"><code>bl.<b>slice([ start[, end ] ])</b></code></a>
|
|
||||||
* <a href="#shallowSlice"><code>bl.<b>shallowSlice([ start[, end ] ])</b></code></a>
|
|
||||||
* <a href="#copy"><code>bl.<b>copy(dest, [ destStart, [ srcStart [, srcEnd ] ] ])</b></code></a>
|
|
||||||
* <a href="#duplicate"><code>bl.<b>duplicate()</b></code></a>
|
|
||||||
* <a href="#consume"><code>bl.<b>consume(bytes)</b></code></a>
|
|
||||||
* <a href="#toString"><code>bl.<b>toString([encoding, [ start, [ end ]]])</b></code></a>
|
|
||||||
* <a href="#readXX"><code>bl.<b>readDoubleBE()</b></code>, <code>bl.<b>readDoubleLE()</b></code>, <code>bl.<b>readFloatBE()</b></code>, <code>bl.<b>readFloatLE()</b></code>, <code>bl.<b>readInt32BE()</b></code>, <code>bl.<b>readInt32LE()</b></code>, <code>bl.<b>readUInt32BE()</b></code>, <code>bl.<b>readUInt32LE()</b></code>, <code>bl.<b>readInt16BE()</b></code>, <code>bl.<b>readInt16LE()</b></code>, <code>bl.<b>readUInt16BE()</b></code>, <code>bl.<b>readUInt16LE()</b></code>, <code>bl.<b>readInt8()</b></code>, <code>bl.<b>readUInt8()</b></code></a>
|
|
||||||
* <a href="#ctorStream"><code><b>new BufferListStream([ callback ])</b></code></a>
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="ctor"></a>
|
|
||||||
### new BufferList([ Buffer | Buffer array | BufferList | BufferList array | String ])
|
|
||||||
No arguments are _required_ for the constructor, but you can initialise the list by passing in a single `Buffer` object or an array of `Buffer` objects.
|
|
||||||
|
|
||||||
`new` is not strictly required, if you don't instantiate a new object, it will be done automatically for you so you can create a new instance simply with:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const { BufferList } = require('bl')
|
|
||||||
const bl = BufferList()
|
|
||||||
|
|
||||||
// equivalent to:
|
|
||||||
|
|
||||||
const { BufferList } = require('bl')
|
|
||||||
const bl = new BufferList()
|
|
||||||
```
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="isBufferList"></a>
|
|
||||||
### BufferList.isBufferList(obj)
|
|
||||||
Determines if the passed object is a `BufferList`. It will return `true` if the passed object is an instance of `BufferList` **or** `BufferListStream` and `false` otherwise.
|
|
||||||
|
|
||||||
N.B. this won't return `true` for `BufferList` or `BufferListStream` instances created by versions of this library before this static method was added.
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="length"></a>
|
|
||||||
### bl.length
|
|
||||||
Get the length of the list in bytes. This is the sum of the lengths of all of the buffers contained in the list, minus any initial offset for a semi-consumed buffer at the beginning. Should accurately represent the total number of bytes that can be read from the list.
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="append"></a>
|
|
||||||
### bl.append(Buffer | Buffer array | BufferList | BufferList array | String)
|
|
||||||
`append(buffer)` adds an additional buffer or BufferList to the internal list. `this` is returned so it can be chained.
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="get"></a>
|
|
||||||
### bl.get(index)
|
|
||||||
`get()` will return the byte at the specified index.
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="indexOf"></a>
|
|
||||||
### bl.indexOf(value[, byteOffset][, encoding])
|
|
||||||
`get()` will return the byte at the specified index.
|
|
||||||
`indexOf()` method returns the first index at which a given element can be found in the BufferList, or -1 if it is not present.
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="slice"></a>
|
|
||||||
### bl.slice([ start, [ end ] ])
|
|
||||||
`slice()` returns a new `Buffer` object containing the bytes within the range specified. Both `start` and `end` are optional and will default to the beginning and end of the list respectively.
|
|
||||||
|
|
||||||
If the requested range spans a single internal buffer then a slice of that buffer will be returned which shares the original memory range of that Buffer. If the range spans multiple buffers then copy operations will likely occur to give you a uniform Buffer.
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="shallowSlice"></a>
|
|
||||||
### bl.shallowSlice([ start, [ end ] ])
|
|
||||||
`shallowSlice()` returns a new `BufferList` object containing the bytes within the range specified. Both `start` and `end` are optional and will default to the beginning and end of the list respectively.
|
|
||||||
|
|
||||||
No copies will be performed. All buffers in the result share memory with the original list.
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="copy"></a>
|
|
||||||
### bl.copy(dest, [ destStart, [ srcStart [, srcEnd ] ] ])
|
|
||||||
`copy()` copies the content of the list in the `dest` buffer, starting from `destStart` and containing the bytes within the range specified with `srcStart` to `srcEnd`. `destStart`, `start` and `end` are optional and will default to the beginning of the `dest` buffer, and the beginning and end of the list respectively.
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="duplicate"></a>
|
|
||||||
### bl.duplicate()
|
|
||||||
`duplicate()` performs a **shallow-copy** of the list. The internal Buffers remains the same, so if you change the underlying Buffers, the change will be reflected in both the original and the duplicate. This method is needed if you want to call `consume()` or `pipe()` and still keep the original list.Example:
|
|
||||||
|
|
||||||
```js
|
|
||||||
var bl = new BufferListStream()
|
|
||||||
|
|
||||||
bl.append('hello')
|
|
||||||
bl.append(' world')
|
|
||||||
bl.append('\n')
|
|
||||||
|
|
||||||
bl.duplicate().pipe(process.stdout, { end: false })
|
|
||||||
|
|
||||||
console.log(bl.toString())
|
|
||||||
```
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="consume"></a>
|
|
||||||
### bl.consume(bytes)
|
|
||||||
`consume()` will shift bytes *off the start of the list*. The number of bytes consumed don't need to line up with the sizes of the internal Buffers—initial offsets will be calculated accordingly in order to give you a consistent view of the data.
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="toString"></a>
|
|
||||||
### bl.toString([encoding, [ start, [ end ]]])
|
|
||||||
`toString()` will return a string representation of the buffer. The optional `start` and `end` arguments are passed on to `slice()`, while the `encoding` is passed on to `toString()` of the resulting Buffer. See the [Buffer#toString()](http://nodejs.org/docs/latest/api/buffer.html#buffer_buf_tostring_encoding_start_end) documentation for more information.
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="readXX"></a>
|
|
||||||
### bl.readDoubleBE(), bl.readDoubleLE(), bl.readFloatBE(), bl.readFloatLE(), bl.readInt32BE(), bl.readInt32LE(), bl.readUInt32BE(), bl.readUInt32LE(), bl.readInt16BE(), bl.readInt16LE(), bl.readUInt16BE(), bl.readUInt16LE(), bl.readInt8(), bl.readUInt8()
|
|
||||||
|
|
||||||
All of the standard byte-reading methods of the `Buffer` interface are implemented and will operate across internal Buffer boundaries transparently.
|
|
||||||
|
|
||||||
See the <b><code>[Buffer](http://nodejs.org/docs/latest/api/buffer.html)</code></b> documentation for how these work.
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
<a name="ctorStream"></a>
|
|
||||||
### new BufferListStream([ callback | Buffer | Buffer array | BufferList | BufferList array | String ])
|
|
||||||
**BufferListStream** is a Node **[Duplex Stream](http://nodejs.org/docs/latest/api/stream.html#stream_class_stream_duplex)**, so it can be read from and written to like a standard Node stream. You can also `pipe()` to and from a **BufferListStream** instance.
|
|
||||||
|
|
||||||
The constructor takes an optional callback, if supplied, the callback will be called with an error argument followed by a reference to the **bl** instance, when `bl.end()` is called (i.e. from a piped stream). This is a convenient method of collecting the entire contents of a stream, particularly when the stream is *chunky*, such as a network stream.
|
|
||||||
|
|
||||||
Normally, no arguments are required for the constructor, but you can initialise the list by passing in a single `Buffer` object or an array of `Buffer` object.
|
|
||||||
|
|
||||||
`new` is not strictly required, if you don't instantiate a new object, it will be done automatically for you so you can create a new instance simply with:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const { BufferListStream } = require('bl')
|
|
||||||
const bl = BufferListStream()
|
|
||||||
|
|
||||||
// equivalent to:
|
|
||||||
|
|
||||||
const { BufferListStream } = require('bl')
|
|
||||||
const bl = new BufferListStream()
|
|
||||||
```
|
|
||||||
|
|
||||||
N.B. For backwards compatibility reasons, `BufferListStream` is the **default** export when you `require('bl')`:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const { BufferListStream } = require('bl')
|
|
||||||
// equivalent to:
|
|
||||||
const BufferListStream = require('bl')
|
|
||||||
```
|
|
||||||
|
|
||||||
--------------------------------------------------------
|
|
||||||
|
|
||||||
## Contributors
|
|
||||||
|
|
||||||
**bl** is brought to you by the following hackers:
|
|
||||||
|
|
||||||
* [Rod Vagg](https://github.com/rvagg)
|
|
||||||
* [Matteo Collina](https://github.com/mcollina)
|
|
||||||
* [Jarett Cruger](https://github.com/jcrugzz)
|
|
||||||
|
|
||||||
<a name="license"></a>
|
|
||||||
## License & copyright
|
|
||||||
|
|
||||||
Copyright (c) 2013-2019 bl contributors (listed above).
|
|
||||||
|
|
||||||
bl is licensed under the MIT license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE.md file for more details.
|
|
||||||
-84
@@ -1,84 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
const DuplexStream = require('readable-stream').Duplex
|
|
||||||
const inherits = require('inherits')
|
|
||||||
const BufferList = require('./BufferList')
|
|
||||||
|
|
||||||
function BufferListStream (callback) {
|
|
||||||
if (!(this instanceof BufferListStream)) {
|
|
||||||
return new BufferListStream(callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof callback === 'function') {
|
|
||||||
this._callback = callback
|
|
||||||
|
|
||||||
const piper = function piper (err) {
|
|
||||||
if (this._callback) {
|
|
||||||
this._callback(err)
|
|
||||||
this._callback = null
|
|
||||||
}
|
|
||||||
}.bind(this)
|
|
||||||
|
|
||||||
this.on('pipe', function onPipe (src) {
|
|
||||||
src.on('error', piper)
|
|
||||||
})
|
|
||||||
this.on('unpipe', function onUnpipe (src) {
|
|
||||||
src.removeListener('error', piper)
|
|
||||||
})
|
|
||||||
|
|
||||||
callback = null
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferList._init.call(this, callback)
|
|
||||||
DuplexStream.call(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
inherits(BufferListStream, DuplexStream)
|
|
||||||
Object.assign(BufferListStream.prototype, BufferList.prototype)
|
|
||||||
|
|
||||||
BufferListStream.prototype._new = function _new (callback) {
|
|
||||||
return new BufferListStream(callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferListStream.prototype._write = function _write (buf, encoding, callback) {
|
|
||||||
this._appendBuffer(buf)
|
|
||||||
|
|
||||||
if (typeof callback === 'function') {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferListStream.prototype._read = function _read (size) {
|
|
||||||
if (!this.length) {
|
|
||||||
return this.push(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
size = Math.min(size, this.length)
|
|
||||||
this.push(this.slice(0, size))
|
|
||||||
this.consume(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferListStream.prototype.end = function end (chunk) {
|
|
||||||
DuplexStream.prototype.end.call(this, chunk)
|
|
||||||
|
|
||||||
if (this._callback) {
|
|
||||||
this._callback(null, this.slice())
|
|
||||||
this._callback = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferListStream.prototype._destroy = function _destroy (err, cb) {
|
|
||||||
this._bufs.length = 0
|
|
||||||
this.length = 0
|
|
||||||
cb(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferListStream.prototype._isBufferList = function _isBufferList (b) {
|
|
||||||
return b instanceof BufferListStream || b instanceof BufferList || BufferListStream.isBufferList(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferListStream.isBufferList = BufferList.isBufferList
|
|
||||||
|
|
||||||
module.exports = BufferListStream
|
|
||||||
module.exports.BufferListStream = BufferListStream
|
|
||||||
module.exports.BufferList = BufferList
|
|
||||||
-37
@@ -1,37 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "bl",
|
|
||||||
"version": "4.1.0",
|
|
||||||
"description": "Buffer List: collect buffers and access with a standard readable Buffer interface, streamable too!",
|
|
||||||
"license": "MIT",
|
|
||||||
"main": "bl.js",
|
|
||||||
"scripts": {
|
|
||||||
"lint": "standard *.js test/*.js",
|
|
||||||
"test": "npm run lint && node test/test.js | faucet"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/rvagg/bl.git"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/rvagg/bl",
|
|
||||||
"authors": [
|
|
||||||
"Rod Vagg <rod@vagg.org> (https://github.com/rvagg)",
|
|
||||||
"Matteo Collina <matteo.collina@gmail.com> (https://github.com/mcollina)",
|
|
||||||
"Jarett Cruger <jcrugzz@gmail.com> (https://github.com/jcrugzz)"
|
|
||||||
],
|
|
||||||
"keywords": [
|
|
||||||
"buffer",
|
|
||||||
"buffers",
|
|
||||||
"stream",
|
|
||||||
"awesomesauce"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"buffer": "^5.5.0",
|
|
||||||
"inherits": "^2.0.4",
|
|
||||||
"readable-stream": "^3.4.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"faucet": "~0.0.1",
|
|
||||||
"standard": "^14.3.0",
|
|
||||||
"tape": "^4.11.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
const tape = require('tape')
|
|
||||||
const { BufferList, BufferListStream } = require('../')
|
|
||||||
const { Buffer } = require('buffer')
|
|
||||||
|
|
||||||
tape('convert from BufferList to BufferListStream', (t) => {
|
|
||||||
const data = Buffer.from(`TEST-${Date.now()}`)
|
|
||||||
const bl = new BufferList(data)
|
|
||||||
const bls = new BufferListStream(bl)
|
|
||||||
t.ok(bl.slice().equals(bls.slice()))
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('convert from BufferListStream to BufferList', (t) => {
|
|
||||||
const data = Buffer.from(`TEST-${Date.now()}`)
|
|
||||||
const bls = new BufferListStream(data)
|
|
||||||
const bl = new BufferList(bls)
|
|
||||||
t.ok(bl.slice().equals(bls.slice()))
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
-492
@@ -1,492 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
const tape = require('tape')
|
|
||||||
const BufferList = require('../')
|
|
||||||
const { Buffer } = require('buffer')
|
|
||||||
|
|
||||||
tape('indexOf single byte needle', (t) => {
|
|
||||||
const bl = new BufferList(['abcdefg', 'abcdefg', '12345'])
|
|
||||||
|
|
||||||
t.equal(bl.indexOf('e'), 4)
|
|
||||||
t.equal(bl.indexOf('e', 5), 11)
|
|
||||||
t.equal(bl.indexOf('e', 12), -1)
|
|
||||||
t.equal(bl.indexOf('5'), 18)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('indexOf multiple byte needle', (t) => {
|
|
||||||
const bl = new BufferList(['abcdefg', 'abcdefg'])
|
|
||||||
|
|
||||||
t.equal(bl.indexOf('ef'), 4)
|
|
||||||
t.equal(bl.indexOf('ef', 5), 11)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('indexOf multiple byte needles across buffer boundaries', (t) => {
|
|
||||||
const bl = new BufferList(['abcdefg', 'abcdefg'])
|
|
||||||
|
|
||||||
t.equal(bl.indexOf('fgabc'), 5)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('indexOf takes a Uint8Array search', (t) => {
|
|
||||||
const bl = new BufferList(['abcdefg', 'abcdefg'])
|
|
||||||
const search = new Uint8Array([102, 103, 97, 98, 99]) // fgabc
|
|
||||||
|
|
||||||
t.equal(bl.indexOf(search), 5)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('indexOf takes a buffer list search', (t) => {
|
|
||||||
const bl = new BufferList(['abcdefg', 'abcdefg'])
|
|
||||||
const search = new BufferList('fgabc')
|
|
||||||
|
|
||||||
t.equal(bl.indexOf(search), 5)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('indexOf a zero byte needle', (t) => {
|
|
||||||
const b = new BufferList('abcdef')
|
|
||||||
const bufEmpty = Buffer.from('')
|
|
||||||
|
|
||||||
t.equal(b.indexOf(''), 0)
|
|
||||||
t.equal(b.indexOf('', 1), 1)
|
|
||||||
t.equal(b.indexOf('', b.length + 1), b.length)
|
|
||||||
t.equal(b.indexOf('', Infinity), b.length)
|
|
||||||
t.equal(b.indexOf(bufEmpty), 0)
|
|
||||||
t.equal(b.indexOf(bufEmpty, 1), 1)
|
|
||||||
t.equal(b.indexOf(bufEmpty, b.length + 1), b.length)
|
|
||||||
t.equal(b.indexOf(bufEmpty, Infinity), b.length)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('indexOf buffers smaller and larger than the needle', (t) => {
|
|
||||||
const bl = new BufferList(['abcdefg', 'a', 'bcdefg', 'a', 'bcfgab'])
|
|
||||||
|
|
||||||
t.equal(bl.indexOf('fgabc'), 5)
|
|
||||||
t.equal(bl.indexOf('fgabc', 6), 12)
|
|
||||||
t.equal(bl.indexOf('fgabc', 13), -1)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
// only present in node 6+
|
|
||||||
;(process.version.substr(1).split('.')[0] >= 6) && tape('indexOf latin1 and binary encoding', (t) => {
|
|
||||||
const b = new BufferList('abcdef')
|
|
||||||
|
|
||||||
// test latin1 encoding
|
|
||||||
t.equal(
|
|
||||||
new BufferList(Buffer.from(b.toString('latin1'), 'latin1'))
|
|
||||||
.indexOf('d', 0, 'latin1'),
|
|
||||||
3
|
|
||||||
)
|
|
||||||
t.equal(
|
|
||||||
new BufferList(Buffer.from(b.toString('latin1'), 'latin1'))
|
|
||||||
.indexOf(Buffer.from('d', 'latin1'), 0, 'latin1'),
|
|
||||||
3
|
|
||||||
)
|
|
||||||
t.equal(
|
|
||||||
new BufferList(Buffer.from('aa\u00e8aa', 'latin1'))
|
|
||||||
.indexOf('\u00e8', 'latin1'),
|
|
||||||
2
|
|
||||||
)
|
|
||||||
t.equal(
|
|
||||||
new BufferList(Buffer.from('\u00e8', 'latin1'))
|
|
||||||
.indexOf('\u00e8', 'latin1'),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
t.equal(
|
|
||||||
new BufferList(Buffer.from('\u00e8', 'latin1'))
|
|
||||||
.indexOf(Buffer.from('\u00e8', 'latin1'), 'latin1'),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
|
|
||||||
// test binary encoding
|
|
||||||
t.equal(
|
|
||||||
new BufferList(Buffer.from(b.toString('binary'), 'binary'))
|
|
||||||
.indexOf('d', 0, 'binary'),
|
|
||||||
3
|
|
||||||
)
|
|
||||||
t.equal(
|
|
||||||
new BufferList(Buffer.from(b.toString('binary'), 'binary'))
|
|
||||||
.indexOf(Buffer.from('d', 'binary'), 0, 'binary'),
|
|
||||||
3
|
|
||||||
)
|
|
||||||
t.equal(
|
|
||||||
new BufferList(Buffer.from('aa\u00e8aa', 'binary'))
|
|
||||||
.indexOf('\u00e8', 'binary'),
|
|
||||||
2
|
|
||||||
)
|
|
||||||
t.equal(
|
|
||||||
new BufferList(Buffer.from('\u00e8', 'binary'))
|
|
||||||
.indexOf('\u00e8', 'binary'),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
t.equal(
|
|
||||||
new BufferList(Buffer.from('\u00e8', 'binary'))
|
|
||||||
.indexOf(Buffer.from('\u00e8', 'binary'), 'binary'),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('indexOf the entire nodejs10 buffer test suite', (t) => {
|
|
||||||
const b = new BufferList('abcdef')
|
|
||||||
const bufA = Buffer.from('a')
|
|
||||||
const bufBc = Buffer.from('bc')
|
|
||||||
const bufF = Buffer.from('f')
|
|
||||||
const bufZ = Buffer.from('z')
|
|
||||||
|
|
||||||
const stringComparison = 'abcdef'
|
|
||||||
|
|
||||||
t.equal(b.indexOf('a'), 0)
|
|
||||||
t.equal(b.indexOf('a', 1), -1)
|
|
||||||
t.equal(b.indexOf('a', -1), -1)
|
|
||||||
t.equal(b.indexOf('a', -4), -1)
|
|
||||||
t.equal(b.indexOf('a', -b.length), 0)
|
|
||||||
t.equal(b.indexOf('a', NaN), 0)
|
|
||||||
t.equal(b.indexOf('a', -Infinity), 0)
|
|
||||||
t.equal(b.indexOf('a', Infinity), -1)
|
|
||||||
t.equal(b.indexOf('bc'), 1)
|
|
||||||
t.equal(b.indexOf('bc', 2), -1)
|
|
||||||
t.equal(b.indexOf('bc', -1), -1)
|
|
||||||
t.equal(b.indexOf('bc', -3), -1)
|
|
||||||
t.equal(b.indexOf('bc', -5), 1)
|
|
||||||
t.equal(b.indexOf('bc', NaN), 1)
|
|
||||||
t.equal(b.indexOf('bc', -Infinity), 1)
|
|
||||||
t.equal(b.indexOf('bc', Infinity), -1)
|
|
||||||
t.equal(b.indexOf('f'), b.length - 1)
|
|
||||||
t.equal(b.indexOf('z'), -1)
|
|
||||||
|
|
||||||
// empty search tests
|
|
||||||
t.equal(b.indexOf(bufA), 0)
|
|
||||||
t.equal(b.indexOf(bufA, 1), -1)
|
|
||||||
t.equal(b.indexOf(bufA, -1), -1)
|
|
||||||
t.equal(b.indexOf(bufA, -4), -1)
|
|
||||||
t.equal(b.indexOf(bufA, -b.length), 0)
|
|
||||||
t.equal(b.indexOf(bufA, NaN), 0)
|
|
||||||
t.equal(b.indexOf(bufA, -Infinity), 0)
|
|
||||||
t.equal(b.indexOf(bufA, Infinity), -1)
|
|
||||||
t.equal(b.indexOf(bufBc), 1)
|
|
||||||
t.equal(b.indexOf(bufBc, 2), -1)
|
|
||||||
t.equal(b.indexOf(bufBc, -1), -1)
|
|
||||||
t.equal(b.indexOf(bufBc, -3), -1)
|
|
||||||
t.equal(b.indexOf(bufBc, -5), 1)
|
|
||||||
t.equal(b.indexOf(bufBc, NaN), 1)
|
|
||||||
t.equal(b.indexOf(bufBc, -Infinity), 1)
|
|
||||||
t.equal(b.indexOf(bufBc, Infinity), -1)
|
|
||||||
t.equal(b.indexOf(bufF), b.length - 1)
|
|
||||||
t.equal(b.indexOf(bufZ), -1)
|
|
||||||
t.equal(b.indexOf(0x61), 0)
|
|
||||||
t.equal(b.indexOf(0x61, 1), -1)
|
|
||||||
t.equal(b.indexOf(0x61, -1), -1)
|
|
||||||
t.equal(b.indexOf(0x61, -4), -1)
|
|
||||||
t.equal(b.indexOf(0x61, -b.length), 0)
|
|
||||||
t.equal(b.indexOf(0x61, NaN), 0)
|
|
||||||
t.equal(b.indexOf(0x61, -Infinity), 0)
|
|
||||||
t.equal(b.indexOf(0x61, Infinity), -1)
|
|
||||||
t.equal(b.indexOf(0x0), -1)
|
|
||||||
|
|
||||||
// test offsets
|
|
||||||
t.equal(b.indexOf('d', 2), 3)
|
|
||||||
t.equal(b.indexOf('f', 5), 5)
|
|
||||||
t.equal(b.indexOf('f', -1), 5)
|
|
||||||
t.equal(b.indexOf('f', 6), -1)
|
|
||||||
|
|
||||||
t.equal(b.indexOf(Buffer.from('d'), 2), 3)
|
|
||||||
t.equal(b.indexOf(Buffer.from('f'), 5), 5)
|
|
||||||
t.equal(b.indexOf(Buffer.from('f'), -1), 5)
|
|
||||||
t.equal(b.indexOf(Buffer.from('f'), 6), -1)
|
|
||||||
|
|
||||||
t.equal(Buffer.from('ff').indexOf(Buffer.from('f'), 1, 'ucs2'), -1)
|
|
||||||
|
|
||||||
// test invalid and uppercase encoding
|
|
||||||
t.equal(b.indexOf('b', 'utf8'), 1)
|
|
||||||
t.equal(b.indexOf('b', 'UTF8'), 1)
|
|
||||||
t.equal(b.indexOf('62', 'HEX'), 1)
|
|
||||||
t.throws(() => b.indexOf('bad', 'enc'), TypeError)
|
|
||||||
|
|
||||||
// test hex encoding
|
|
||||||
t.equal(
|
|
||||||
Buffer.from(b.toString('hex'), 'hex')
|
|
||||||
.indexOf('64', 0, 'hex'),
|
|
||||||
3
|
|
||||||
)
|
|
||||||
t.equal(
|
|
||||||
Buffer.from(b.toString('hex'), 'hex')
|
|
||||||
.indexOf(Buffer.from('64', 'hex'), 0, 'hex'),
|
|
||||||
3
|
|
||||||
)
|
|
||||||
|
|
||||||
// test base64 encoding
|
|
||||||
t.equal(
|
|
||||||
Buffer.from(b.toString('base64'), 'base64')
|
|
||||||
.indexOf('ZA==', 0, 'base64'),
|
|
||||||
3
|
|
||||||
)
|
|
||||||
t.equal(
|
|
||||||
Buffer.from(b.toString('base64'), 'base64')
|
|
||||||
.indexOf(Buffer.from('ZA==', 'base64'), 0, 'base64'),
|
|
||||||
3
|
|
||||||
)
|
|
||||||
|
|
||||||
// test ascii encoding
|
|
||||||
t.equal(
|
|
||||||
Buffer.from(b.toString('ascii'), 'ascii')
|
|
||||||
.indexOf('d', 0, 'ascii'),
|
|
||||||
3
|
|
||||||
)
|
|
||||||
t.equal(
|
|
||||||
Buffer.from(b.toString('ascii'), 'ascii')
|
|
||||||
.indexOf(Buffer.from('d', 'ascii'), 0, 'ascii'),
|
|
||||||
3
|
|
||||||
)
|
|
||||||
|
|
||||||
// test optional offset with passed encoding
|
|
||||||
t.equal(Buffer.from('aaaa0').indexOf('30', 'hex'), 4)
|
|
||||||
t.equal(Buffer.from('aaaa00a').indexOf('3030', 'hex'), 4)
|
|
||||||
|
|
||||||
{
|
|
||||||
// test usc2 encoding
|
|
||||||
const twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2')
|
|
||||||
|
|
||||||
t.equal(8, twoByteString.indexOf('\u0395', 4, 'ucs2'))
|
|
||||||
t.equal(6, twoByteString.indexOf('\u03a3', -4, 'ucs2'))
|
|
||||||
t.equal(4, twoByteString.indexOf('\u03a3', -6, 'ucs2'))
|
|
||||||
t.equal(4, twoByteString.indexOf(
|
|
||||||
Buffer.from('\u03a3', 'ucs2'), -6, 'ucs2'))
|
|
||||||
t.equal(-1, twoByteString.indexOf('\u03a3', -2, 'ucs2'))
|
|
||||||
}
|
|
||||||
|
|
||||||
const mixedByteStringUcs2 =
|
|
||||||
Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395', 'ucs2')
|
|
||||||
|
|
||||||
t.equal(6, mixedByteStringUcs2.indexOf('bc', 0, 'ucs2'))
|
|
||||||
t.equal(10, mixedByteStringUcs2.indexOf('\u03a3', 0, 'ucs2'))
|
|
||||||
t.equal(-1, mixedByteStringUcs2.indexOf('\u0396', 0, 'ucs2'))
|
|
||||||
|
|
||||||
t.equal(
|
|
||||||
6, mixedByteStringUcs2.indexOf(Buffer.from('bc', 'ucs2'), 0, 'ucs2'))
|
|
||||||
t.equal(
|
|
||||||
10, mixedByteStringUcs2.indexOf(Buffer.from('\u03a3', 'ucs2'), 0, 'ucs2'))
|
|
||||||
t.equal(
|
|
||||||
-1, mixedByteStringUcs2.indexOf(Buffer.from('\u0396', 'ucs2'), 0, 'ucs2'))
|
|
||||||
|
|
||||||
{
|
|
||||||
const twoByteString = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'ucs2')
|
|
||||||
|
|
||||||
// Test single char pattern
|
|
||||||
t.equal(0, twoByteString.indexOf('\u039a', 0, 'ucs2'))
|
|
||||||
let index = twoByteString.indexOf('\u0391', 0, 'ucs2')
|
|
||||||
t.equal(2, index, `Alpha - at index ${index}`)
|
|
||||||
index = twoByteString.indexOf('\u03a3', 0, 'ucs2')
|
|
||||||
t.equal(4, index, `First Sigma - at index ${index}`)
|
|
||||||
index = twoByteString.indexOf('\u03a3', 6, 'ucs2')
|
|
||||||
t.equal(6, index, `Second Sigma - at index ${index}`)
|
|
||||||
index = twoByteString.indexOf('\u0395', 0, 'ucs2')
|
|
||||||
t.equal(8, index, `Epsilon - at index ${index}`)
|
|
||||||
index = twoByteString.indexOf('\u0392', 0, 'ucs2')
|
|
||||||
t.equal(-1, index, `Not beta - at index ${index}`)
|
|
||||||
|
|
||||||
// Test multi-char pattern
|
|
||||||
index = twoByteString.indexOf('\u039a\u0391', 0, 'ucs2')
|
|
||||||
t.equal(0, index, `Lambda Alpha - at index ${index}`)
|
|
||||||
index = twoByteString.indexOf('\u0391\u03a3', 0, 'ucs2')
|
|
||||||
t.equal(2, index, `Alpha Sigma - at index ${index}`)
|
|
||||||
index = twoByteString.indexOf('\u03a3\u03a3', 0, 'ucs2')
|
|
||||||
t.equal(4, index, `Sigma Sigma - at index ${index}`)
|
|
||||||
index = twoByteString.indexOf('\u03a3\u0395', 0, 'ucs2')
|
|
||||||
t.equal(6, index, `Sigma Epsilon - at index ${index}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const mixedByteStringUtf8 = Buffer.from('\u039a\u0391abc\u03a3\u03a3\u0395')
|
|
||||||
|
|
||||||
t.equal(5, mixedByteStringUtf8.indexOf('bc'))
|
|
||||||
t.equal(5, mixedByteStringUtf8.indexOf('bc', 5))
|
|
||||||
t.equal(5, mixedByteStringUtf8.indexOf('bc', -8))
|
|
||||||
t.equal(7, mixedByteStringUtf8.indexOf('\u03a3'))
|
|
||||||
t.equal(-1, mixedByteStringUtf8.indexOf('\u0396'))
|
|
||||||
|
|
||||||
// Test complex string indexOf algorithms. Only trigger for long strings.
|
|
||||||
// Long string that isn't a simple repeat of a shorter string.
|
|
||||||
let longString = 'A'
|
|
||||||
for (let i = 66; i < 76; i++) { // from 'B' to 'K'
|
|
||||||
longString = longString + String.fromCharCode(i) + longString
|
|
||||||
}
|
|
||||||
|
|
||||||
const longBufferString = Buffer.from(longString)
|
|
||||||
|
|
||||||
// pattern of 15 chars, repeated every 16 chars in long
|
|
||||||
let pattern = 'ABACABADABACABA'
|
|
||||||
for (let i = 0; i < longBufferString.length - pattern.length; i += 7) {
|
|
||||||
const index = longBufferString.indexOf(pattern, i)
|
|
||||||
t.equal((i + 15) & ~0xf, index,
|
|
||||||
`Long ABACABA...-string at index ${i}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
let index = longBufferString.indexOf('AJABACA')
|
|
||||||
t.equal(510, index, `Long AJABACA, First J - at index ${index}`)
|
|
||||||
index = longBufferString.indexOf('AJABACA', 511)
|
|
||||||
t.equal(1534, index, `Long AJABACA, Second J - at index ${index}`)
|
|
||||||
|
|
||||||
pattern = 'JABACABADABACABA'
|
|
||||||
index = longBufferString.indexOf(pattern)
|
|
||||||
t.equal(511, index, `Long JABACABA..., First J - at index ${index}`)
|
|
||||||
index = longBufferString.indexOf(pattern, 512)
|
|
||||||
t.equal(
|
|
||||||
1535, index, `Long JABACABA..., Second J - at index ${index}`)
|
|
||||||
|
|
||||||
// Search for a non-ASCII string in a pure ASCII string.
|
|
||||||
const asciiString = Buffer.from(
|
|
||||||
'somethingnotatallsinisterwhichalsoworks')
|
|
||||||
t.equal(-1, asciiString.indexOf('\x2061'))
|
|
||||||
t.equal(3, asciiString.indexOf('eth', 0))
|
|
||||||
|
|
||||||
// Search in string containing many non-ASCII chars.
|
|
||||||
const allCodePoints = []
|
|
||||||
for (let i = 0; i < 65536; i++) {
|
|
||||||
allCodePoints[i] = i
|
|
||||||
}
|
|
||||||
|
|
||||||
const allCharsString = String.fromCharCode.apply(String, allCodePoints)
|
|
||||||
const allCharsBufferUtf8 = Buffer.from(allCharsString)
|
|
||||||
const allCharsBufferUcs2 = Buffer.from(allCharsString, 'ucs2')
|
|
||||||
|
|
||||||
// Search for string long enough to trigger complex search with ASCII pattern
|
|
||||||
// and UC16 subject.
|
|
||||||
t.equal(-1, allCharsBufferUtf8.indexOf('notfound'))
|
|
||||||
t.equal(-1, allCharsBufferUcs2.indexOf('notfound'))
|
|
||||||
|
|
||||||
// Needle is longer than haystack, but only because it's encoded as UTF-16
|
|
||||||
t.equal(Buffer.from('aaaa').indexOf('a'.repeat(4), 'ucs2'), -1)
|
|
||||||
|
|
||||||
t.equal(Buffer.from('aaaa').indexOf('a'.repeat(4), 'utf8'), 0)
|
|
||||||
t.equal(Buffer.from('aaaa').indexOf('你好', 'ucs2'), -1)
|
|
||||||
|
|
||||||
// Haystack has odd length, but the needle is UCS2.
|
|
||||||
t.equal(Buffer.from('aaaaa').indexOf('b', 'ucs2'), -1)
|
|
||||||
|
|
||||||
{
|
|
||||||
// Find substrings in Utf8.
|
|
||||||
const lengths = [1, 3, 15] // Single char, simple and complex.
|
|
||||||
const indices = [0x5, 0x60, 0x400, 0x680, 0x7ee, 0xFF02, 0x16610, 0x2f77b]
|
|
||||||
for (let lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) {
|
|
||||||
for (let i = 0; i < indices.length; i++) {
|
|
||||||
const index = indices[i]
|
|
||||||
let length = lengths[lengthIndex]
|
|
||||||
|
|
||||||
if (index + length > 0x7F) {
|
|
||||||
length = 2 * length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index + length > 0x7FF) {
|
|
||||||
length = 3 * length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index + length > 0xFFFF) {
|
|
||||||
length = 4 * length
|
|
||||||
}
|
|
||||||
|
|
||||||
const patternBufferUtf8 = allCharsBufferUtf8.slice(index, index + length)
|
|
||||||
t.equal(index, allCharsBufferUtf8.indexOf(patternBufferUtf8))
|
|
||||||
|
|
||||||
const patternStringUtf8 = patternBufferUtf8.toString()
|
|
||||||
t.equal(index, allCharsBufferUtf8.indexOf(patternStringUtf8))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Find substrings in Usc2.
|
|
||||||
const lengths = [2, 4, 16] // Single char, simple and complex.
|
|
||||||
const indices = [0x5, 0x65, 0x105, 0x205, 0x285, 0x2005, 0x2085, 0xfff0]
|
|
||||||
|
|
||||||
for (let lengthIndex = 0; lengthIndex < lengths.length; lengthIndex++) {
|
|
||||||
for (let i = 0; i < indices.length; i++) {
|
|
||||||
const index = indices[i] * 2
|
|
||||||
const length = lengths[lengthIndex]
|
|
||||||
|
|
||||||
const patternBufferUcs2 =
|
|
||||||
allCharsBufferUcs2.slice(index, index + length)
|
|
||||||
t.equal(
|
|
||||||
index, allCharsBufferUcs2.indexOf(patternBufferUcs2, 0, 'ucs2'))
|
|
||||||
|
|
||||||
const patternStringUcs2 = patternBufferUcs2.toString('ucs2')
|
|
||||||
t.equal(
|
|
||||||
index, allCharsBufferUcs2.indexOf(patternStringUcs2, 0, 'ucs2'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[
|
|
||||||
() => {},
|
|
||||||
{},
|
|
||||||
[]
|
|
||||||
].forEach((val) => {
|
|
||||||
t.throws(() => b.indexOf(val), TypeError, `"${JSON.stringify(val)}" should throw`)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test weird offset arguments.
|
|
||||||
// The following offsets coerce to NaN or 0, searching the whole Buffer
|
|
||||||
t.equal(b.indexOf('b', undefined), 1)
|
|
||||||
t.equal(b.indexOf('b', {}), 1)
|
|
||||||
t.equal(b.indexOf('b', 0), 1)
|
|
||||||
t.equal(b.indexOf('b', null), 1)
|
|
||||||
t.equal(b.indexOf('b', []), 1)
|
|
||||||
|
|
||||||
// The following offset coerces to 2, in other words +[2] === 2
|
|
||||||
t.equal(b.indexOf('b', [2]), -1)
|
|
||||||
|
|
||||||
// Behavior should match String.indexOf()
|
|
||||||
t.equal(
|
|
||||||
b.indexOf('b', undefined),
|
|
||||||
stringComparison.indexOf('b', undefined))
|
|
||||||
t.equal(
|
|
||||||
b.indexOf('b', {}),
|
|
||||||
stringComparison.indexOf('b', {}))
|
|
||||||
t.equal(
|
|
||||||
b.indexOf('b', 0),
|
|
||||||
stringComparison.indexOf('b', 0))
|
|
||||||
t.equal(
|
|
||||||
b.indexOf('b', null),
|
|
||||||
stringComparison.indexOf('b', null))
|
|
||||||
t.equal(
|
|
||||||
b.indexOf('b', []),
|
|
||||||
stringComparison.indexOf('b', []))
|
|
||||||
t.equal(
|
|
||||||
b.indexOf('b', [2]),
|
|
||||||
stringComparison.indexOf('b', [2]))
|
|
||||||
|
|
||||||
// test truncation of Number arguments to uint8
|
|
||||||
{
|
|
||||||
const buf = Buffer.from('this is a test')
|
|
||||||
|
|
||||||
t.equal(buf.indexOf(0x6973), 3)
|
|
||||||
t.equal(buf.indexOf(0x697320), 4)
|
|
||||||
t.equal(buf.indexOf(0x69732069), 2)
|
|
||||||
t.equal(buf.indexOf(0x697374657374), 0)
|
|
||||||
t.equal(buf.indexOf(0x69737374), 0)
|
|
||||||
t.equal(buf.indexOf(0x69737465), 11)
|
|
||||||
t.equal(buf.indexOf(0x69737465), 11)
|
|
||||||
t.equal(buf.indexOf(-140), 0)
|
|
||||||
t.equal(buf.indexOf(-152), 1)
|
|
||||||
t.equal(buf.indexOf(0xff), -1)
|
|
||||||
t.equal(buf.indexOf(0xffff), -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that Uint8Array arguments are okay.
|
|
||||||
{
|
|
||||||
const needle = new Uint8Array([0x66, 0x6f, 0x6f])
|
|
||||||
const haystack = new BufferList(Buffer.from('a foo b foo'))
|
|
||||||
t.equal(haystack.indexOf(needle), 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
const tape = require('tape')
|
|
||||||
const { BufferList, BufferListStream } = require('../')
|
|
||||||
const { Buffer } = require('buffer')
|
|
||||||
|
|
||||||
tape('isBufferList positives', (t) => {
|
|
||||||
t.ok(BufferList.isBufferList(new BufferList()))
|
|
||||||
t.ok(BufferList.isBufferList(new BufferListStream()))
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('isBufferList negatives', (t) => {
|
|
||||||
const types = [
|
|
||||||
null,
|
|
||||||
undefined,
|
|
||||||
NaN,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
{},
|
|
||||||
[],
|
|
||||||
Buffer.alloc(0),
|
|
||||||
[Buffer.alloc(0)]
|
|
||||||
]
|
|
||||||
|
|
||||||
for (const obj of types) {
|
|
||||||
t.notOk(BufferList.isBufferList(obj))
|
|
||||||
}
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
-869
@@ -1,869 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
const tape = require('tape')
|
|
||||||
const crypto = require('crypto')
|
|
||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
const BufferList = require('../')
|
|
||||||
const { Buffer } = require('buffer')
|
|
||||||
|
|
||||||
const encodings =
|
|
||||||
('hex utf8 utf-8 ascii binary base64' +
|
|
||||||
(process.browser ? '' : ' ucs2 ucs-2 utf16le utf-16le')).split(' ')
|
|
||||||
|
|
||||||
require('./indexOf')
|
|
||||||
require('./isBufferList')
|
|
||||||
require('./convert')
|
|
||||||
|
|
||||||
tape('single bytes from single buffer', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
bl.append(Buffer.from('abcd'))
|
|
||||||
|
|
||||||
t.equal(bl.length, 4)
|
|
||||||
t.equal(bl.get(-1), undefined)
|
|
||||||
t.equal(bl.get(0), 97)
|
|
||||||
t.equal(bl.get(1), 98)
|
|
||||||
t.equal(bl.get(2), 99)
|
|
||||||
t.equal(bl.get(3), 100)
|
|
||||||
t.equal(bl.get(4), undefined)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('single bytes from multiple buffers', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
bl.append(Buffer.from('abcd'))
|
|
||||||
bl.append(Buffer.from('efg'))
|
|
||||||
bl.append(Buffer.from('hi'))
|
|
||||||
bl.append(Buffer.from('j'))
|
|
||||||
|
|
||||||
t.equal(bl.length, 10)
|
|
||||||
|
|
||||||
t.equal(bl.get(0), 97)
|
|
||||||
t.equal(bl.get(1), 98)
|
|
||||||
t.equal(bl.get(2), 99)
|
|
||||||
t.equal(bl.get(3), 100)
|
|
||||||
t.equal(bl.get(4), 101)
|
|
||||||
t.equal(bl.get(5), 102)
|
|
||||||
t.equal(bl.get(6), 103)
|
|
||||||
t.equal(bl.get(7), 104)
|
|
||||||
t.equal(bl.get(8), 105)
|
|
||||||
t.equal(bl.get(9), 106)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('multi bytes from single buffer', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
bl.append(Buffer.from('abcd'))
|
|
||||||
|
|
||||||
t.equal(bl.length, 4)
|
|
||||||
|
|
||||||
t.equal(bl.slice(0, 4).toString('ascii'), 'abcd')
|
|
||||||
t.equal(bl.slice(0, 3).toString('ascii'), 'abc')
|
|
||||||
t.equal(bl.slice(1, 4).toString('ascii'), 'bcd')
|
|
||||||
t.equal(bl.slice(-4, -1).toString('ascii'), 'abc')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('multi bytes from single buffer (negative indexes)', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
bl.append(Buffer.from('buffer'))
|
|
||||||
|
|
||||||
t.equal(bl.length, 6)
|
|
||||||
|
|
||||||
t.equal(bl.slice(-6, -1).toString('ascii'), 'buffe')
|
|
||||||
t.equal(bl.slice(-6, -2).toString('ascii'), 'buff')
|
|
||||||
t.equal(bl.slice(-5, -2).toString('ascii'), 'uff')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('multiple bytes from multiple buffers', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
bl.append(Buffer.from('abcd'))
|
|
||||||
bl.append(Buffer.from('efg'))
|
|
||||||
bl.append(Buffer.from('hi'))
|
|
||||||
bl.append(Buffer.from('j'))
|
|
||||||
|
|
||||||
t.equal(bl.length, 10)
|
|
||||||
|
|
||||||
t.equal(bl.slice(0, 10).toString('ascii'), 'abcdefghij')
|
|
||||||
t.equal(bl.slice(3, 10).toString('ascii'), 'defghij')
|
|
||||||
t.equal(bl.slice(3, 6).toString('ascii'), 'def')
|
|
||||||
t.equal(bl.slice(3, 8).toString('ascii'), 'defgh')
|
|
||||||
t.equal(bl.slice(5, 10).toString('ascii'), 'fghij')
|
|
||||||
t.equal(bl.slice(-7, -4).toString('ascii'), 'def')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('multiple bytes from multiple buffer lists', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
bl.append(new BufferList([Buffer.from('abcd'), Buffer.from('efg')]))
|
|
||||||
bl.append(new BufferList([Buffer.from('hi'), Buffer.from('j')]))
|
|
||||||
|
|
||||||
t.equal(bl.length, 10)
|
|
||||||
|
|
||||||
t.equal(bl.slice(0, 10).toString('ascii'), 'abcdefghij')
|
|
||||||
|
|
||||||
t.equal(bl.slice(3, 10).toString('ascii'), 'defghij')
|
|
||||||
t.equal(bl.slice(3, 6).toString('ascii'), 'def')
|
|
||||||
t.equal(bl.slice(3, 8).toString('ascii'), 'defgh')
|
|
||||||
t.equal(bl.slice(5, 10).toString('ascii'), 'fghij')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
// same data as previous test, just using nested constructors
|
|
||||||
tape('multiple bytes from crazy nested buffer lists', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
bl.append(new BufferList([
|
|
||||||
new BufferList([
|
|
||||||
new BufferList(Buffer.from('abc')),
|
|
||||||
Buffer.from('d'),
|
|
||||||
new BufferList(Buffer.from('efg'))
|
|
||||||
]),
|
|
||||||
new BufferList([Buffer.from('hi')]),
|
|
||||||
new BufferList(Buffer.from('j'))
|
|
||||||
]))
|
|
||||||
|
|
||||||
t.equal(bl.length, 10)
|
|
||||||
|
|
||||||
t.equal(bl.slice(0, 10).toString('ascii'), 'abcdefghij')
|
|
||||||
|
|
||||||
t.equal(bl.slice(3, 10).toString('ascii'), 'defghij')
|
|
||||||
t.equal(bl.slice(3, 6).toString('ascii'), 'def')
|
|
||||||
t.equal(bl.slice(3, 8).toString('ascii'), 'defgh')
|
|
||||||
t.equal(bl.slice(5, 10).toString('ascii'), 'fghij')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('append accepts arrays of Buffers', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
bl.append(Buffer.from('abc'))
|
|
||||||
bl.append([Buffer.from('def')])
|
|
||||||
bl.append([Buffer.from('ghi'), Buffer.from('jkl')])
|
|
||||||
bl.append([Buffer.from('mnop'), Buffer.from('qrstu'), Buffer.from('vwxyz')])
|
|
||||||
t.equal(bl.length, 26)
|
|
||||||
t.equal(bl.slice().toString('ascii'), 'abcdefghijklmnopqrstuvwxyz')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('append accepts arrays of Uint8Arrays', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
bl.append(new Uint8Array([97, 98, 99]))
|
|
||||||
bl.append([Uint8Array.from([100, 101, 102])])
|
|
||||||
bl.append([new Uint8Array([103, 104, 105]), new Uint8Array([106, 107, 108])])
|
|
||||||
bl.append([new Uint8Array([109, 110, 111, 112]), new Uint8Array([113, 114, 115, 116, 117]), new Uint8Array([118, 119, 120, 121, 122])])
|
|
||||||
t.equal(bl.length, 26)
|
|
||||||
t.equal(bl.slice().toString('ascii'), 'abcdefghijklmnopqrstuvwxyz')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('append accepts arrays of BufferLists', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
bl.append(Buffer.from('abc'))
|
|
||||||
bl.append([new BufferList('def')])
|
|
||||||
bl.append(new BufferList([Buffer.from('ghi'), new BufferList('jkl')]))
|
|
||||||
bl.append([Buffer.from('mnop'), new BufferList([Buffer.from('qrstu'), Buffer.from('vwxyz')])])
|
|
||||||
t.equal(bl.length, 26)
|
|
||||||
t.equal(bl.slice().toString('ascii'), 'abcdefghijklmnopqrstuvwxyz')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('append chainable', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
t.ok(bl.append(Buffer.from('abcd')) === bl)
|
|
||||||
t.ok(bl.append([Buffer.from('abcd')]) === bl)
|
|
||||||
t.ok(bl.append(new BufferList(Buffer.from('abcd'))) === bl)
|
|
||||||
t.ok(bl.append([new BufferList(Buffer.from('abcd'))]) === bl)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('append chainable (test results)', function (t) {
|
|
||||||
const bl = new BufferList('abc')
|
|
||||||
.append([new BufferList('def')])
|
|
||||||
.append(new BufferList([Buffer.from('ghi'), new BufferList('jkl')]))
|
|
||||||
.append([Buffer.from('mnop'), new BufferList([Buffer.from('qrstu'), Buffer.from('vwxyz')])])
|
|
||||||
|
|
||||||
t.equal(bl.length, 26)
|
|
||||||
t.equal(bl.slice().toString('ascii'), 'abcdefghijklmnopqrstuvwxyz')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('consuming from multiple buffers', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
bl.append(Buffer.from('abcd'))
|
|
||||||
bl.append(Buffer.from('efg'))
|
|
||||||
bl.append(Buffer.from('hi'))
|
|
||||||
bl.append(Buffer.from('j'))
|
|
||||||
|
|
||||||
t.equal(bl.length, 10)
|
|
||||||
|
|
||||||
t.equal(bl.slice(0, 10).toString('ascii'), 'abcdefghij')
|
|
||||||
|
|
||||||
bl.consume(3)
|
|
||||||
t.equal(bl.length, 7)
|
|
||||||
t.equal(bl.slice(0, 7).toString('ascii'), 'defghij')
|
|
||||||
|
|
||||||
bl.consume(2)
|
|
||||||
t.equal(bl.length, 5)
|
|
||||||
t.equal(bl.slice(0, 5).toString('ascii'), 'fghij')
|
|
||||||
|
|
||||||
bl.consume(1)
|
|
||||||
t.equal(bl.length, 4)
|
|
||||||
t.equal(bl.slice(0, 4).toString('ascii'), 'ghij')
|
|
||||||
|
|
||||||
bl.consume(1)
|
|
||||||
t.equal(bl.length, 3)
|
|
||||||
t.equal(bl.slice(0, 3).toString('ascii'), 'hij')
|
|
||||||
|
|
||||||
bl.consume(2)
|
|
||||||
t.equal(bl.length, 1)
|
|
||||||
t.equal(bl.slice(0, 1).toString('ascii'), 'j')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('complete consumption', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
bl.append(Buffer.from('a'))
|
|
||||||
bl.append(Buffer.from('b'))
|
|
||||||
|
|
||||||
bl.consume(2)
|
|
||||||
|
|
||||||
t.equal(bl.length, 0)
|
|
||||||
t.equal(bl._bufs.length, 0)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('test readUInt8 / readInt8', function (t) {
|
|
||||||
const buf1 = Buffer.alloc(1)
|
|
||||||
const buf2 = Buffer.alloc(3)
|
|
||||||
const buf3 = Buffer.alloc(3)
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
buf1[0] = 0x1
|
|
||||||
buf2[1] = 0x3
|
|
||||||
buf2[2] = 0x4
|
|
||||||
buf3[0] = 0x23
|
|
||||||
buf3[1] = 0x42
|
|
||||||
|
|
||||||
bl.append(buf1)
|
|
||||||
bl.append(buf2)
|
|
||||||
bl.append(buf3)
|
|
||||||
|
|
||||||
t.equal(bl.readUInt8(), 0x1)
|
|
||||||
t.equal(bl.readUInt8(2), 0x3)
|
|
||||||
t.equal(bl.readInt8(2), 0x3)
|
|
||||||
t.equal(bl.readUInt8(3), 0x4)
|
|
||||||
t.equal(bl.readInt8(3), 0x4)
|
|
||||||
t.equal(bl.readUInt8(4), 0x23)
|
|
||||||
t.equal(bl.readInt8(4), 0x23)
|
|
||||||
t.equal(bl.readUInt8(5), 0x42)
|
|
||||||
t.equal(bl.readInt8(5), 0x42)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('test readUInt16LE / readUInt16BE / readInt16LE / readInt16BE', function (t) {
|
|
||||||
const buf1 = Buffer.alloc(1)
|
|
||||||
const buf2 = Buffer.alloc(3)
|
|
||||||
const buf3 = Buffer.alloc(3)
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
buf1[0] = 0x1
|
|
||||||
buf2[1] = 0x3
|
|
||||||
buf2[2] = 0x4
|
|
||||||
buf3[0] = 0x23
|
|
||||||
buf3[1] = 0x42
|
|
||||||
|
|
||||||
bl.append(buf1)
|
|
||||||
bl.append(buf2)
|
|
||||||
bl.append(buf3)
|
|
||||||
|
|
||||||
t.equal(bl.readUInt16BE(), 0x0100)
|
|
||||||
t.equal(bl.readUInt16LE(), 0x0001)
|
|
||||||
t.equal(bl.readUInt16BE(2), 0x0304)
|
|
||||||
t.equal(bl.readUInt16LE(2), 0x0403)
|
|
||||||
t.equal(bl.readInt16BE(2), 0x0304)
|
|
||||||
t.equal(bl.readInt16LE(2), 0x0403)
|
|
||||||
t.equal(bl.readUInt16BE(3), 0x0423)
|
|
||||||
t.equal(bl.readUInt16LE(3), 0x2304)
|
|
||||||
t.equal(bl.readInt16BE(3), 0x0423)
|
|
||||||
t.equal(bl.readInt16LE(3), 0x2304)
|
|
||||||
t.equal(bl.readUInt16BE(4), 0x2342)
|
|
||||||
t.equal(bl.readUInt16LE(4), 0x4223)
|
|
||||||
t.equal(bl.readInt16BE(4), 0x2342)
|
|
||||||
t.equal(bl.readInt16LE(4), 0x4223)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('test readUInt32LE / readUInt32BE / readInt32LE / readInt32BE', function (t) {
|
|
||||||
const buf1 = Buffer.alloc(1)
|
|
||||||
const buf2 = Buffer.alloc(3)
|
|
||||||
const buf3 = Buffer.alloc(3)
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
buf1[0] = 0x1
|
|
||||||
buf2[1] = 0x3
|
|
||||||
buf2[2] = 0x4
|
|
||||||
buf3[0] = 0x23
|
|
||||||
buf3[1] = 0x42
|
|
||||||
|
|
||||||
bl.append(buf1)
|
|
||||||
bl.append(buf2)
|
|
||||||
bl.append(buf3)
|
|
||||||
|
|
||||||
t.equal(bl.readUInt32BE(), 0x01000304)
|
|
||||||
t.equal(bl.readUInt32LE(), 0x04030001)
|
|
||||||
t.equal(bl.readUInt32BE(2), 0x03042342)
|
|
||||||
t.equal(bl.readUInt32LE(2), 0x42230403)
|
|
||||||
t.equal(bl.readInt32BE(2), 0x03042342)
|
|
||||||
t.equal(bl.readInt32LE(2), 0x42230403)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('test readUIntLE / readUIntBE / readIntLE / readIntBE', function (t) {
|
|
||||||
const buf1 = Buffer.alloc(1)
|
|
||||||
const buf2 = Buffer.alloc(3)
|
|
||||||
const buf3 = Buffer.alloc(3)
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
buf2[0] = 0x2
|
|
||||||
buf2[1] = 0x3
|
|
||||||
buf2[2] = 0x4
|
|
||||||
buf3[0] = 0x23
|
|
||||||
buf3[1] = 0x42
|
|
||||||
buf3[2] = 0x61
|
|
||||||
|
|
||||||
bl.append(buf1)
|
|
||||||
bl.append(buf2)
|
|
||||||
bl.append(buf3)
|
|
||||||
|
|
||||||
t.equal(bl.readUIntBE(1, 1), 0x02)
|
|
||||||
t.equal(bl.readUIntBE(1, 2), 0x0203)
|
|
||||||
t.equal(bl.readUIntBE(1, 3), 0x020304)
|
|
||||||
t.equal(bl.readUIntBE(1, 4), 0x02030423)
|
|
||||||
t.equal(bl.readUIntBE(1, 5), 0x0203042342)
|
|
||||||
t.equal(bl.readUIntBE(1, 6), 0x020304234261)
|
|
||||||
t.equal(bl.readUIntLE(1, 1), 0x02)
|
|
||||||
t.equal(bl.readUIntLE(1, 2), 0x0302)
|
|
||||||
t.equal(bl.readUIntLE(1, 3), 0x040302)
|
|
||||||
t.equal(bl.readUIntLE(1, 4), 0x23040302)
|
|
||||||
t.equal(bl.readUIntLE(1, 5), 0x4223040302)
|
|
||||||
t.equal(bl.readUIntLE(1, 6), 0x614223040302)
|
|
||||||
t.equal(bl.readIntBE(1, 1), 0x02)
|
|
||||||
t.equal(bl.readIntBE(1, 2), 0x0203)
|
|
||||||
t.equal(bl.readIntBE(1, 3), 0x020304)
|
|
||||||
t.equal(bl.readIntBE(1, 4), 0x02030423)
|
|
||||||
t.equal(bl.readIntBE(1, 5), 0x0203042342)
|
|
||||||
t.equal(bl.readIntBE(1, 6), 0x020304234261)
|
|
||||||
t.equal(bl.readIntLE(1, 1), 0x02)
|
|
||||||
t.equal(bl.readIntLE(1, 2), 0x0302)
|
|
||||||
t.equal(bl.readIntLE(1, 3), 0x040302)
|
|
||||||
t.equal(bl.readIntLE(1, 4), 0x23040302)
|
|
||||||
t.equal(bl.readIntLE(1, 5), 0x4223040302)
|
|
||||||
t.equal(bl.readIntLE(1, 6), 0x614223040302)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('test readFloatLE / readFloatBE', function (t) {
|
|
||||||
const buf1 = Buffer.alloc(1)
|
|
||||||
const buf2 = Buffer.alloc(3)
|
|
||||||
const buf3 = Buffer.alloc(3)
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
buf1[0] = 0x01
|
|
||||||
buf2[1] = 0x00
|
|
||||||
buf2[2] = 0x00
|
|
||||||
buf3[0] = 0x80
|
|
||||||
buf3[1] = 0x3f
|
|
||||||
|
|
||||||
bl.append(buf1)
|
|
||||||
bl.append(buf2)
|
|
||||||
bl.append(buf3)
|
|
||||||
|
|
||||||
const canonical = Buffer.concat([buf1, buf2, buf3])
|
|
||||||
t.equal(bl.readFloatLE(), canonical.readFloatLE())
|
|
||||||
t.equal(bl.readFloatBE(), canonical.readFloatBE())
|
|
||||||
t.equal(bl.readFloatLE(2), canonical.readFloatLE(2))
|
|
||||||
t.equal(bl.readFloatBE(2), canonical.readFloatBE(2))
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('test readDoubleLE / readDoubleBE', function (t) {
|
|
||||||
const buf1 = Buffer.alloc(1)
|
|
||||||
const buf2 = Buffer.alloc(3)
|
|
||||||
const buf3 = Buffer.alloc(10)
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
buf1[0] = 0x01
|
|
||||||
buf2[1] = 0x55
|
|
||||||
buf2[2] = 0x55
|
|
||||||
buf3[0] = 0x55
|
|
||||||
buf3[1] = 0x55
|
|
||||||
buf3[2] = 0x55
|
|
||||||
buf3[3] = 0x55
|
|
||||||
buf3[4] = 0xd5
|
|
||||||
buf3[5] = 0x3f
|
|
||||||
|
|
||||||
bl.append(buf1)
|
|
||||||
bl.append(buf2)
|
|
||||||
bl.append(buf3)
|
|
||||||
|
|
||||||
const canonical = Buffer.concat([buf1, buf2, buf3])
|
|
||||||
t.equal(bl.readDoubleBE(), canonical.readDoubleBE())
|
|
||||||
t.equal(bl.readDoubleLE(), canonical.readDoubleLE())
|
|
||||||
t.equal(bl.readDoubleBE(2), canonical.readDoubleBE(2))
|
|
||||||
t.equal(bl.readDoubleLE(2), canonical.readDoubleLE(2))
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('test toString', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
bl.append(Buffer.from('abcd'))
|
|
||||||
bl.append(Buffer.from('efg'))
|
|
||||||
bl.append(Buffer.from('hi'))
|
|
||||||
bl.append(Buffer.from('j'))
|
|
||||||
|
|
||||||
t.equal(bl.toString('ascii', 0, 10), 'abcdefghij')
|
|
||||||
t.equal(bl.toString('ascii', 3, 10), 'defghij')
|
|
||||||
t.equal(bl.toString('ascii', 3, 6), 'def')
|
|
||||||
t.equal(bl.toString('ascii', 3, 8), 'defgh')
|
|
||||||
t.equal(bl.toString('ascii', 5, 10), 'fghij')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('test toString encoding', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
const b = Buffer.from('abcdefghij\xff\x00')
|
|
||||||
|
|
||||||
bl.append(Buffer.from('abcd'))
|
|
||||||
bl.append(Buffer.from('efg'))
|
|
||||||
bl.append(Buffer.from('hi'))
|
|
||||||
bl.append(Buffer.from('j'))
|
|
||||||
bl.append(Buffer.from('\xff\x00'))
|
|
||||||
|
|
||||||
encodings.forEach(function (enc) {
|
|
||||||
t.equal(bl.toString(enc), b.toString(enc), enc)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('uninitialized memory', function (t) {
|
|
||||||
const secret = crypto.randomBytes(256)
|
|
||||||
for (let i = 0; i < 1e6; i++) {
|
|
||||||
const clone = Buffer.from(secret)
|
|
||||||
const bl = new BufferList()
|
|
||||||
bl.append(Buffer.from('a'))
|
|
||||||
bl.consume(-1024)
|
|
||||||
const buf = bl.slice(1)
|
|
||||||
if (buf.indexOf(clone) !== -1) {
|
|
||||||
t.fail(`Match (at ${i})`)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
!process.browser && tape('test stream', function (t) {
|
|
||||||
const random = crypto.randomBytes(65534)
|
|
||||||
|
|
||||||
const bl = new BufferList((err, buf) => {
|
|
||||||
t.ok(Buffer.isBuffer(buf))
|
|
||||||
t.ok(err === null)
|
|
||||||
t.ok(random.equals(bl.slice()))
|
|
||||||
t.ok(random.equals(buf.slice()))
|
|
||||||
|
|
||||||
bl.pipe(fs.createWriteStream('/tmp/bl_test_rnd_out.dat'))
|
|
||||||
.on('close', function () {
|
|
||||||
const rndhash = crypto.createHash('md5').update(random).digest('hex')
|
|
||||||
const md5sum = crypto.createHash('md5')
|
|
||||||
const s = fs.createReadStream('/tmp/bl_test_rnd_out.dat')
|
|
||||||
|
|
||||||
s.on('data', md5sum.update.bind(md5sum))
|
|
||||||
s.on('end', function () {
|
|
||||||
t.equal(rndhash, md5sum.digest('hex'), 'woohoo! correct hash!')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
fs.writeFileSync('/tmp/bl_test_rnd.dat', random)
|
|
||||||
fs.createReadStream('/tmp/bl_test_rnd.dat').pipe(bl)
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('instantiation with Buffer', function (t) {
|
|
||||||
const buf = crypto.randomBytes(1024)
|
|
||||||
const buf2 = crypto.randomBytes(1024)
|
|
||||||
let b = BufferList(buf)
|
|
||||||
|
|
||||||
t.equal(buf.toString('hex'), b.slice().toString('hex'), 'same buffer')
|
|
||||||
b = BufferList([buf, buf2])
|
|
||||||
t.equal(b.slice().toString('hex'), Buffer.concat([buf, buf2]).toString('hex'), 'same buffer')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('test String appendage', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
const b = Buffer.from('abcdefghij\xff\x00')
|
|
||||||
|
|
||||||
bl.append('abcd')
|
|
||||||
bl.append('efg')
|
|
||||||
bl.append('hi')
|
|
||||||
bl.append('j')
|
|
||||||
bl.append('\xff\x00')
|
|
||||||
|
|
||||||
encodings.forEach(function (enc) {
|
|
||||||
t.equal(bl.toString(enc), b.toString(enc))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('test Number appendage', function (t) {
|
|
||||||
const bl = new BufferList()
|
|
||||||
const b = Buffer.from('1234567890')
|
|
||||||
|
|
||||||
bl.append(1234)
|
|
||||||
bl.append(567)
|
|
||||||
bl.append(89)
|
|
||||||
bl.append(0)
|
|
||||||
|
|
||||||
encodings.forEach(function (enc) {
|
|
||||||
t.equal(bl.toString(enc), b.toString(enc))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('write nothing, should get empty buffer', function (t) {
|
|
||||||
t.plan(3)
|
|
||||||
BufferList(function (err, data) {
|
|
||||||
t.notOk(err, 'no error')
|
|
||||||
t.ok(Buffer.isBuffer(data), 'got a buffer')
|
|
||||||
t.equal(0, data.length, 'got a zero-length buffer')
|
|
||||||
t.end()
|
|
||||||
}).end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('unicode string', function (t) {
|
|
||||||
t.plan(2)
|
|
||||||
|
|
||||||
const inp1 = '\u2600'
|
|
||||||
const inp2 = '\u2603'
|
|
||||||
const exp = inp1 + ' and ' + inp2
|
|
||||||
const bl = BufferList()
|
|
||||||
|
|
||||||
bl.write(inp1)
|
|
||||||
bl.write(' and ')
|
|
||||||
bl.write(inp2)
|
|
||||||
t.equal(exp, bl.toString())
|
|
||||||
t.equal(Buffer.from(exp).toString('hex'), bl.toString('hex'))
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('should emit finish', function (t) {
|
|
||||||
const source = BufferList()
|
|
||||||
const dest = BufferList()
|
|
||||||
|
|
||||||
source.write('hello')
|
|
||||||
source.pipe(dest)
|
|
||||||
|
|
||||||
dest.on('finish', function () {
|
|
||||||
t.equal(dest.toString('utf8'), 'hello')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('basic copy', function (t) {
|
|
||||||
const buf = crypto.randomBytes(1024)
|
|
||||||
const buf2 = Buffer.alloc(1024)
|
|
||||||
const b = BufferList(buf)
|
|
||||||
|
|
||||||
b.copy(buf2)
|
|
||||||
t.equal(b.slice().toString('hex'), buf2.toString('hex'), 'same buffer')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('copy after many appends', function (t) {
|
|
||||||
const buf = crypto.randomBytes(512)
|
|
||||||
const buf2 = Buffer.alloc(1024)
|
|
||||||
const b = BufferList(buf)
|
|
||||||
|
|
||||||
b.append(buf)
|
|
||||||
b.copy(buf2)
|
|
||||||
t.equal(b.slice().toString('hex'), buf2.toString('hex'), 'same buffer')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('copy at a precise position', function (t) {
|
|
||||||
const buf = crypto.randomBytes(1004)
|
|
||||||
const buf2 = Buffer.alloc(1024)
|
|
||||||
const b = BufferList(buf)
|
|
||||||
|
|
||||||
b.copy(buf2, 20)
|
|
||||||
t.equal(b.slice().toString('hex'), buf2.slice(20).toString('hex'), 'same buffer')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('copy starting from a precise location', function (t) {
|
|
||||||
const buf = crypto.randomBytes(10)
|
|
||||||
const buf2 = Buffer.alloc(5)
|
|
||||||
const b = BufferList(buf)
|
|
||||||
|
|
||||||
b.copy(buf2, 0, 5)
|
|
||||||
t.equal(b.slice(5).toString('hex'), buf2.toString('hex'), 'same buffer')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('copy in an interval', function (t) {
|
|
||||||
const rnd = crypto.randomBytes(10)
|
|
||||||
const b = BufferList(rnd) // put the random bytes there
|
|
||||||
const actual = Buffer.alloc(3)
|
|
||||||
const expected = Buffer.alloc(3)
|
|
||||||
|
|
||||||
rnd.copy(expected, 0, 5, 8)
|
|
||||||
b.copy(actual, 0, 5, 8)
|
|
||||||
|
|
||||||
t.equal(actual.toString('hex'), expected.toString('hex'), 'same buffer')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('copy an interval between two buffers', function (t) {
|
|
||||||
const buf = crypto.randomBytes(10)
|
|
||||||
const buf2 = Buffer.alloc(10)
|
|
||||||
const b = BufferList(buf)
|
|
||||||
|
|
||||||
b.append(buf)
|
|
||||||
b.copy(buf2, 0, 5, 15)
|
|
||||||
|
|
||||||
t.equal(b.slice(5, 15).toString('hex'), buf2.toString('hex'), 'same buffer')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('shallow slice across buffer boundaries', function (t) {
|
|
||||||
const bl = new BufferList(['First', 'Second', 'Third'])
|
|
||||||
|
|
||||||
t.equal(bl.shallowSlice(3, 13).toString(), 'stSecondTh')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('shallow slice within single buffer', function (t) {
|
|
||||||
t.plan(2)
|
|
||||||
|
|
||||||
const bl = new BufferList(['First', 'Second', 'Third'])
|
|
||||||
|
|
||||||
t.equal(bl.shallowSlice(5, 10).toString(), 'Secon')
|
|
||||||
t.equal(bl.shallowSlice(7, 10).toString(), 'con')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('shallow slice single buffer', function (t) {
|
|
||||||
t.plan(3)
|
|
||||||
|
|
||||||
const bl = new BufferList(['First', 'Second', 'Third'])
|
|
||||||
|
|
||||||
t.equal(bl.shallowSlice(0, 5).toString(), 'First')
|
|
||||||
t.equal(bl.shallowSlice(5, 11).toString(), 'Second')
|
|
||||||
t.equal(bl.shallowSlice(11, 16).toString(), 'Third')
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('shallow slice with negative or omitted indices', function (t) {
|
|
||||||
t.plan(4)
|
|
||||||
|
|
||||||
const bl = new BufferList(['First', 'Second', 'Third'])
|
|
||||||
|
|
||||||
t.equal(bl.shallowSlice().toString(), 'FirstSecondThird')
|
|
||||||
t.equal(bl.shallowSlice(5).toString(), 'SecondThird')
|
|
||||||
t.equal(bl.shallowSlice(5, -3).toString(), 'SecondTh')
|
|
||||||
t.equal(bl.shallowSlice(-8).toString(), 'ondThird')
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('shallow slice does not make a copy', function (t) {
|
|
||||||
t.plan(1)
|
|
||||||
|
|
||||||
const buffers = [Buffer.from('First'), Buffer.from('Second'), Buffer.from('Third')]
|
|
||||||
const bl = (new BufferList(buffers)).shallowSlice(5, -3)
|
|
||||||
|
|
||||||
buffers[1].fill('h')
|
|
||||||
buffers[2].fill('h')
|
|
||||||
|
|
||||||
t.equal(bl.toString(), 'hhhhhhhh')
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('shallow slice with 0 length', function (t) {
|
|
||||||
t.plan(1)
|
|
||||||
|
|
||||||
const buffers = [Buffer.from('First'), Buffer.from('Second'), Buffer.from('Third')]
|
|
||||||
const bl = (new BufferList(buffers)).shallowSlice(0, 0)
|
|
||||||
|
|
||||||
t.equal(bl.length, 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('shallow slice with 0 length from middle', function (t) {
|
|
||||||
t.plan(1)
|
|
||||||
|
|
||||||
const buffers = [Buffer.from('First'), Buffer.from('Second'), Buffer.from('Third')]
|
|
||||||
const bl = (new BufferList(buffers)).shallowSlice(10, 10)
|
|
||||||
|
|
||||||
t.equal(bl.length, 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('duplicate', function (t) {
|
|
||||||
t.plan(2)
|
|
||||||
|
|
||||||
const bl = new BufferList('abcdefghij\xff\x00')
|
|
||||||
const dup = bl.duplicate()
|
|
||||||
|
|
||||||
t.equal(bl.prototype, dup.prototype)
|
|
||||||
t.equal(bl.toString('hex'), dup.toString('hex'))
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('destroy no pipe', function (t) {
|
|
||||||
t.plan(2)
|
|
||||||
|
|
||||||
const bl = new BufferList('alsdkfja;lsdkfja;lsdk')
|
|
||||||
|
|
||||||
bl.destroy()
|
|
||||||
|
|
||||||
t.equal(bl._bufs.length, 0)
|
|
||||||
t.equal(bl.length, 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
tape('destroy with error', function (t) {
|
|
||||||
t.plan(3)
|
|
||||||
|
|
||||||
const bl = new BufferList('alsdkfja;lsdkfja;lsdk')
|
|
||||||
const err = new Error('kaboom')
|
|
||||||
|
|
||||||
bl.destroy(err)
|
|
||||||
bl.on('error', function (_err) {
|
|
||||||
t.equal(_err, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.equal(bl._bufs.length, 0)
|
|
||||||
t.equal(bl.length, 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
!process.browser && tape('destroy with pipe before read end', function (t) {
|
|
||||||
t.plan(2)
|
|
||||||
|
|
||||||
const bl = new BufferList()
|
|
||||||
fs.createReadStream(path.join(__dirname, '/test.js'))
|
|
||||||
.pipe(bl)
|
|
||||||
|
|
||||||
bl.destroy()
|
|
||||||
|
|
||||||
t.equal(bl._bufs.length, 0)
|
|
||||||
t.equal(bl.length, 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
!process.browser && tape('destroy with pipe before read end with race', function (t) {
|
|
||||||
t.plan(2)
|
|
||||||
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
fs.createReadStream(path.join(__dirname, '/test.js'))
|
|
||||||
.pipe(bl)
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
bl.destroy()
|
|
||||||
setTimeout(function () {
|
|
||||||
t.equal(bl._bufs.length, 0)
|
|
||||||
t.equal(bl.length, 0)
|
|
||||||
}, 500)
|
|
||||||
}, 500)
|
|
||||||
})
|
|
||||||
|
|
||||||
!process.browser && tape('destroy with pipe after read end', function (t) {
|
|
||||||
t.plan(2)
|
|
||||||
|
|
||||||
const bl = new BufferList()
|
|
||||||
|
|
||||||
fs.createReadStream(path.join(__dirname, '/test.js'))
|
|
||||||
.on('end', onEnd)
|
|
||||||
.pipe(bl)
|
|
||||||
|
|
||||||
function onEnd () {
|
|
||||||
bl.destroy()
|
|
||||||
|
|
||||||
t.equal(bl._bufs.length, 0)
|
|
||||||
t.equal(bl.length, 0)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
!process.browser && tape('destroy with pipe while writing to a destination', function (t) {
|
|
||||||
t.plan(4)
|
|
||||||
|
|
||||||
const bl = new BufferList()
|
|
||||||
const ds = new BufferList()
|
|
||||||
|
|
||||||
fs.createReadStream(path.join(__dirname, '/test.js'))
|
|
||||||
.on('end', onEnd)
|
|
||||||
.pipe(bl)
|
|
||||||
|
|
||||||
function onEnd () {
|
|
||||||
bl.pipe(ds)
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
bl.destroy()
|
|
||||||
|
|
||||||
t.equals(bl._bufs.length, 0)
|
|
||||||
t.equals(bl.length, 0)
|
|
||||||
|
|
||||||
ds.destroy()
|
|
||||||
|
|
||||||
t.equals(bl._bufs.length, 0)
|
|
||||||
t.equals(bl.length, 0)
|
|
||||||
}, 100)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
!process.browser && tape('handle error', function (t) {
|
|
||||||
t.plan(2)
|
|
||||||
|
|
||||||
fs.createReadStream('/does/not/exist').pipe(BufferList(function (err, data) {
|
|
||||||
t.ok(err instanceof Error, 'has error')
|
|
||||||
t.notOk(data, 'no data')
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
-686
@@ -1,686 +0,0 @@
|
|||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-243
@@ -1,243 +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 !== 'number'
|
|
||||||
? bytes.parse(opts.limit || '100kb')
|
|
||||||
: opts.limit
|
|
||||||
var inflate = opts.inflate !== false
|
|
||||||
var reviver = opts.reviver
|
|
||||||
var strict = opts.strict !== false
|
|
||||||
var type = opts.type || 'application/json'
|
|
||||||
var verify = opts.verify || false
|
|
||||||
|
|
||||||
if (verify !== false && typeof verify !== 'function') {
|
|
||||||
throw new TypeError('option verify must be function')
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the appropriate type checking function
|
|
||||||
var shouldParse = typeof type !== 'function'
|
|
||||||
? typeChecker(type)
|
|
||||||
: type
|
|
||||||
|
|
||||||
function parse (body) {
|
|
||||||
if (body.length === 0) {
|
|
||||||
// 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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-101
@@ -1,101 +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 !== 'number'
|
|
||||||
? bytes.parse(opts.limit || '100kb')
|
|
||||||
: opts.limit
|
|
||||||
var type = opts.type || 'application/octet-stream'
|
|
||||||
var verify = opts.verify || false
|
|
||||||
|
|
||||||
if (verify !== false && typeof verify !== 'function') {
|
|
||||||
throw new TypeError('option verify must be function')
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the appropriate type checking function
|
|
||||||
var shouldParse = typeof type !== 'function'
|
|
||||||
? typeChecker(type)
|
|
||||||
: type
|
|
||||||
|
|
||||||
function parse (buf) {
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
return function rawParser (req, res, next) {
|
|
||||||
if (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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-121
@@ -1,121 +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 !== 'number'
|
|
||||||
? bytes.parse(opts.limit || '100kb')
|
|
||||||
: opts.limit
|
|
||||||
var type = opts.type || 'text/plain'
|
|
||||||
var verify = opts.verify || false
|
|
||||||
|
|
||||||
if (verify !== false && typeof verify !== 'function') {
|
|
||||||
throw new TypeError('option verify must be function')
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the appropriate type checking function
|
|
||||||
var shouldParse = typeof type !== 'function'
|
|
||||||
? typeChecker(type)
|
|
||||||
: type
|
|
||||||
|
|
||||||
function parse (buf) {
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
return function textParser (req, res, next) {
|
|
||||||
if (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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-299
@@ -1,299 +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 !== 'number'
|
|
||||||
? bytes.parse(opts.limit || '100kb')
|
|
||||||
: opts.limit
|
|
||||||
var type = opts.type || 'application/x-www-form-urlencoded'
|
|
||||||
var verify = opts.verify || false
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-46
@@ -1,46 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
end_of_line = lf
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
max_line_length = 180
|
|
||||||
quote_type = single
|
|
||||||
|
|
||||||
[test/*]
|
|
||||||
max_line_length = off
|
|
||||||
|
|
||||||
[LICENSE.md]
|
|
||||||
indent_size = off
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
max_line_length = off
|
|
||||||
|
|
||||||
[*.json]
|
|
||||||
max_line_length = off
|
|
||||||
|
|
||||||
[Makefile]
|
|
||||||
max_line_length = off
|
|
||||||
|
|
||||||
[CHANGELOG.md]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[LICENSE]
|
|
||||||
indent_size = 2
|
|
||||||
max_line_length = off
|
|
||||||
|
|
||||||
[coverage/**/*]
|
|
||||||
indent_size = off
|
|
||||||
indent_style = off
|
|
||||||
indent = off
|
|
||||||
max_line_length = off
|
|
||||||
|
|
||||||
[.nycrc]
|
|
||||||
indent_style = tab
|
|
||||||
|
|
||||||
[tea.yaml]
|
|
||||||
indent_size = 2
|
|
||||||
-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/qs
|
|
||||||
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 a single custom sponsorship URL
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
# Security
|
|
||||||
|
|
||||||
Please file a private vulnerability report via GitHub, email [@ljharb](https://github.com/ljharb), or see https://tidelift.com/security if you have a potential security vulnerability to report.
|
|
||||||
|
|
||||||
## Incident Response Plan
|
|
||||||
|
|
||||||
Please see our [Incident Response Plan](https://github.com/ljharb/.github/blob/main/INCIDENT_RESPONSE_PLAN.md).
|
|
||||||
|
|
||||||
## Threat Model
|
|
||||||
|
|
||||||
Please see [THREAT_MODEL.md](./THREAT_MODEL.md).
|
|
||||||
-78
@@ -1,78 +0,0 @@
|
|||||||
## Threat Model for qs (querystring parsing library)
|
|
||||||
|
|
||||||
### 1. Library Overview
|
|
||||||
|
|
||||||
- **Library Name:** qs
|
|
||||||
- **Brief Description:** A JavaScript library for parsing and stringifying URL query strings, supporting nested objects and arrays. It is widely used in Node.js and web applications for processing query parameters[2][6][8].
|
|
||||||
- **Key Public APIs/Functions:** `qs.parse()`, `qs.stringify()`
|
|
||||||
|
|
||||||
### 2. Define Scope
|
|
||||||
|
|
||||||
This threat model focuses on the core parsing and stringifying functionality, specifically the handling of nested objects and arrays, option validation, and cycle management in stringification.
|
|
||||||
|
|
||||||
### 3. Conceptual System Diagram
|
|
||||||
|
|
||||||
```
|
|
||||||
Caller Application → qs.parse(input, options) → Parsing Engine → Output Object
|
|
||||||
│
|
|
||||||
└→ Options Handling
|
|
||||||
|
|
||||||
Caller Application → qs.stringify(obj, options) → Stringifying Engine → Output String
|
|
||||||
│
|
|
||||||
└→ Options Handling
|
|
||||||
└→ Cycle Tracking
|
|
||||||
```
|
|
||||||
|
|
||||||
**Trust Boundaries:**
|
|
||||||
- **Input string (parse):** May come from untrusted sources (e.g., user input, network requests)
|
|
||||||
- **Input object (stringify):** May contain cycles, which can lead to infinite loops during stringification
|
|
||||||
- **Options:** Provided by the caller
|
|
||||||
- **Cycle Tracking:** Used only during stringification to detect and handle circular references
|
|
||||||
|
|
||||||
### 4. Identify Assets
|
|
||||||
|
|
||||||
- **Integrity of parsed output:** Prevent malicious manipulation of the output object structure, especially ensuring builtins/globals are not modified as a result of parse[3][4][8].
|
|
||||||
- **Confidentiality of processed data:** Avoid leaking sensitive information through errors or output.
|
|
||||||
- **Availability/performance for host application:** Prevent crashes or resource exhaustion in the consuming application.
|
|
||||||
- **Security of host application:** Prevent the library from being a vector for attacks (e.g., prototype pollution, DoS).
|
|
||||||
- **Reputation of library:** Maintain trust by avoiding supply chain attacks and vulnerabilities[1].
|
|
||||||
|
|
||||||
### 5. Identify Threats
|
|
||||||
|
|
||||||
| Component / API / Interaction | S | T | R | I | D | E |
|
|
||||||
|---------------------------------------|----|----|----|----|----|----|
|
|
||||||
| Public API Call (`parse`) | – | ✓ | – | ✓ | ✓ | ✓ |
|
|
||||||
| Public API Call (`stringify`) | – | ✓ | – | ✓ | ✓ | – |
|
|
||||||
| Options Handling | ✓ | ✓ | – | ✓ | – | ✓ |
|
|
||||||
| Dependency Interaction | – | – | – | – | ✓ | – |
|
|
||||||
|
|
||||||
**Key Threats:**
|
|
||||||
- **Tampering:** Malicious input can, if not prevented, alter parsed output (e.g., prototype pollution via `__proto__`, modification of builtins/globals)[3][4][8].
|
|
||||||
- **Information Disclosure:** Error messages may expose internal details or sensitive data.
|
|
||||||
- **Denial of Service:** Large or malformed input can exhaust memory or CPU.
|
|
||||||
- **Elevation of Privilege:** Prototype pollution can lead to unintended privilege escalation in the host application[3][4][8].
|
|
||||||
|
|
||||||
### 6. Mitigation/Countermeasures
|
|
||||||
|
|
||||||
| Threat Identified | Proposed Mitigation |
|
|
||||||
|---------------------------------------------------|---------------------|
|
|
||||||
| Tampering (malicious input, prototype pollution) | Strict input validation; keep `allowPrototypes: false` by default; use `plainObjects` for output; ensure builtins/globals are never modified by parse[4][8]. |
|
|
||||||
| Information Disclosure (error messages) | Generic error messages without stack traces or internal paths. |
|
|
||||||
| Denial of Service (memory/CPU exhaustion) | Enforce `arrayLimit` and `parameterLimit` with safe defaults; enable `throwOnLimitExceeded`; limit nesting depth[7]. |
|
|
||||||
| Elevation of Privilege (prototype pollution) | Keep `allowPrototypes: false`; validate options against allowlist; use `plainObjects` to avoid prototype pollution[4][8]. |
|
|
||||||
|
|
||||||
### 7. Risk Ranking
|
|
||||||
|
|
||||||
- **High:** Denial of Service via array parsing or malformed input (historical vulnerability)
|
|
||||||
- **Medium:** Prototype pollution via options or input (if `allowPrototypes` enabled)
|
|
||||||
- **Low:** Information disclosure in errors
|
|
||||||
|
|
||||||
### 8. Next Steps & Review
|
|
||||||
|
|
||||||
1. **Audit option validation logic.**
|
|
||||||
2. **Add depth limiting to nested parsing and stringification.**
|
|
||||||
3. **Implement fuzz testing for parser and stringifier edge cases.**
|
|
||||||
4. **Regularly review dependencies for vulnerabilities.**
|
|
||||||
5. **Keep documentation and threat model up to date.**
|
|
||||||
6. **Ensure builtins/globals are never modified as a result of parse.**
|
|
||||||
7. **Support round-trip consistency between parse and stringify as a non-security goal, with the right options[5][9].**
|
|
||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"all": true,
|
|
||||||
"check-coverage": false,
|
|
||||||
"reporter": ["text-summary", "text", "html", "json"],
|
|
||||||
"lines": 86,
|
|
||||||
"statements": 85.93,
|
|
||||||
"functions": 82.43,
|
|
||||||
"branches": 76.06,
|
|
||||||
"exclude": [
|
|
||||||
"coverage",
|
|
||||||
"dist"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
-812
@@ -1,812 +0,0 @@
|
|||||||
## **6.15.1**
|
|
||||||
- [Fix] `parse`: `parameterLimit: Infinity` with `throwOnLimitExceeded: true` silently drops all parameters
|
|
||||||
- [Deps] update `@ljharb/eslint-config`
|
|
||||||
- [Dev Deps] update `@ljharb/eslint-config`, `iconv-lite`
|
|
||||||
- [Tests] increase coverage
|
|
||||||
|
|
||||||
## **6.15.0**
|
|
||||||
- [New] `parse`: add `strictMerge` option to wrap object/primitive conflicts in an array (#425, #122)
|
|
||||||
- [Fix] `duplicates` option should not apply to bracket notation keys (#514)
|
|
||||||
|
|
||||||
## **6.14.2**
|
|
||||||
- [Fix] `parse`: mark overflow objects for indexed notation exceeding `arrayLimit` (#546)
|
|
||||||
- [Fix] `arrayLimit` means max count, not max index, in `combine`/`merge`/`parseArrayValue`
|
|
||||||
- [Fix] `parse`: throw on `arrayLimit` exceeded with indexed notation when `throwOnLimitExceeded` is true (#529)
|
|
||||||
- [Fix] `parse`: enforce `arrayLimit` on `comma`-parsed values
|
|
||||||
- [Fix] `parse`: fix error message to reflect arrayLimit as max index; remove extraneous comments (#545)
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] replace runkit CI badge with shields.io check-runs badge
|
|
||||||
- [meta] fix changelog typo (`arrayLength` → `arrayLimit`)
|
|
||||||
- [actions] fix rebase workflow permissions
|
|
||||||
|
|
||||||
## **6.14.1**
|
|
||||||
- [Fix] ensure `arrayLimit` applies to `[]` notation as well
|
|
||||||
- [Fix] `parse`: when a custom decoder returns `null` for a key, ignore that key
|
|
||||||
- [Refactor] `parse`: extract key segment splitting helper
|
|
||||||
- [meta] add threat model
|
|
||||||
- [actions] add workflow permissions
|
|
||||||
- [Tests] `stringify`: increase coverage
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `npmignore`, `es-value-fixtures`, `for-each`, `object-inspect`
|
|
||||||
|
|
||||||
## **6.14.0**
|
|
||||||
- [New] `parse`: add `throwOnParameterLimitExceeded` option (#517)
|
|
||||||
- [Refactor] `parse`: use `utils.combine` more
|
|
||||||
- [patch] `parse`: add explicit `throwOnLimitExceeded` default
|
|
||||||
- [actions] use shared action; re-add finishers
|
|
||||||
- [meta] Fix changelog formatting bug
|
|
||||||
- [Deps] update `side-channel`
|
|
||||||
- [Dev Deps] update `es-value-fixtures`, `has-bigints`, `has-proto`, `has-symbols`
|
|
||||||
- [Tests] increase coverage
|
|
||||||
|
|
||||||
## **6.13.3**
|
|
||||||
[Fix] fix regressions from robustness refactor
|
|
||||||
[actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.13.2**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
|
|
||||||
- [readme] replace runkit CI badge with shields.io check-runs badge
|
|
||||||
- [actions] fix rebase workflow permissions
|
|
||||||
|
|
||||||
## **6.13.1**
|
|
||||||
- [Fix] `stringify`: avoid a crash when a `filter` key is `null`
|
|
||||||
- [Fix] `utils.merge`: functions should not be stringified into keys
|
|
||||||
- [Fix] `parse`: avoid a crash with interpretNumericEntities: true, comma: true, and iso charset
|
|
||||||
- [Fix] `stringify`: ensure a non-string `filter` does not crash
|
|
||||||
- [Refactor] use `__proto__` syntax instead of `Object.create` for null objects
|
|
||||||
- [Refactor] misc cleanup
|
|
||||||
- [Tests] `utils.merge`: add some coverage
|
|
||||||
- [Tests] fix a test case
|
|
||||||
- [actions] split out node 10-20, and 20+
|
|
||||||
- [Dev Deps] update `es-value-fixtures`, `mock-property`, `object-inspect`, `tape`
|
|
||||||
|
|
||||||
## **6.13.0**
|
|
||||||
- [New] `parse`: add `strictDepth` option (#511)
|
|
||||||
- [Tests] use `npm audit` instead of `aud`
|
|
||||||
|
|
||||||
## **6.12.5**
|
|
||||||
- [Fix] fix regressions from robustness refactor
|
|
||||||
- [actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.12.4**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
|
|
||||||
- [readme] replace runkit CI badge with shields.io check-runs badge
|
|
||||||
- [actions] fix rebase workflow permissions
|
|
||||||
|
|
||||||
## **6.12.3**
|
|
||||||
- [Fix] `parse`: properly account for `strictNullHandling` when `allowEmptyArrays`
|
|
||||||
- [meta] fix changelog indentation
|
|
||||||
|
|
||||||
## **6.12.2**
|
|
||||||
- [Fix] `parse`: parse encoded square brackets (#506)
|
|
||||||
- [readme] add CII best practices badge
|
|
||||||
|
|
||||||
## **6.12.1**
|
|
||||||
- [Fix] `parse`: Disable `decodeDotInKeys` by default to restore previous behavior (#501)
|
|
||||||
- [Performance] `utils`: Optimize performance under large data volumes, reduce memory usage, and speed up processing (#502)
|
|
||||||
- [Refactor] `utils`: use `+=`
|
|
||||||
- [Tests] increase coverage
|
|
||||||
|
|
||||||
## **6.12.0**
|
|
||||||
|
|
||||||
- [New] `parse`/`stringify`: add `decodeDotInKeys`/`encodeDotKeys` options (#488)
|
|
||||||
- [New] `parse`: add `duplicates` option
|
|
||||||
- [New] `parse`/`stringify`: add `allowEmptyArrays` option to allow [] in object values (#487)
|
|
||||||
- [Refactor] `parse`/`stringify`: move allowDots config logic to its own variable
|
|
||||||
- [Refactor] `stringify`: move option-handling code into `normalizeStringifyOptions`
|
|
||||||
- [readme] update readme, add logos (#484)
|
|
||||||
- [readme] `stringify`: clarify default `arrayFormat` behavior
|
|
||||||
- [readme] fix line wrapping
|
|
||||||
- [readme] remove dead badges
|
|
||||||
- [Deps] update `side-channel`
|
|
||||||
- [meta] make the dist build 50% smaller
|
|
||||||
- [meta] add `sideEffects` flag
|
|
||||||
- [meta] run build in prepack, not prepublish
|
|
||||||
- [Tests] `parse`: remove useless tests; add coverage
|
|
||||||
- [Tests] `stringify`: increase coverage
|
|
||||||
- [Tests] use `mock-property`
|
|
||||||
- [Tests] `stringify`: improve coverage
|
|
||||||
- [Dev Deps] update `@ljharb/eslint-config `, `aud`, `has-override-mistake`, `has-property-descriptors`, `mock-property`, `npmignore`, `object-inspect`, `tape`
|
|
||||||
- [Dev Deps] pin `glob`, since v10.3.8+ requires a broken `jackspeak`
|
|
||||||
- [Dev Deps] pin `jackspeak` since 2.1.2+ depends on npm aliases, which kill the install process in npm < 6
|
|
||||||
|
|
||||||
## **6.11.4**
|
|
||||||
- [Fix] fix regressions from robustness refactor
|
|
||||||
- [actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.11.3**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
|
|
||||||
- [readme] replace runkit CI badge with shields.io check-runs badge
|
|
||||||
- [actions] fix rebase workflow permissions
|
|
||||||
|
|
||||||
## **6.11.2**
|
|
||||||
- [Fix] `parse`: Fix parsing when the global Object prototype is frozen (#473)
|
|
||||||
- [Tests] add passing test cases with empty keys (#473)
|
|
||||||
|
|
||||||
## **6.11.1**
|
|
||||||
- [Fix] `stringify`: encode comma values more consistently (#463)
|
|
||||||
- [readme] add usage of `filter` option for injecting custom serialization, i.e. of custom types (#447)
|
|
||||||
- [meta] remove extraneous code backticks (#457)
|
|
||||||
- [meta] fix changelog markdown
|
|
||||||
- [actions] update checkout action
|
|
||||||
- [actions] restrict action permissions
|
|
||||||
- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `object-inspect`, `tape`
|
|
||||||
|
|
||||||
## **6.11.0**
|
|
||||||
- [New] [Fix] `stringify`: revert 0e903c0; add `commaRoundTrip` option (#442)
|
|
||||||
- [readme] fix version badge
|
|
||||||
|
|
||||||
## **6.10.7**
|
|
||||||
- [Fix] fix regressions from robustness refactor
|
|
||||||
- [actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.10.6**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
|
|
||||||
- [readme] replace runkit CI badge with shields.io check-runs badge
|
|
||||||
- [actions] fix rebase workflow permissions
|
|
||||||
|
|
||||||
## **6.10.5**
|
|
||||||
- [Fix] `stringify`: with `arrayFormat: comma`, properly include an explicit `[]` on a single-item array (#434)
|
|
||||||
|
|
||||||
## **6.10.4**
|
|
||||||
- [Fix] `stringify`: with `arrayFormat: comma`, include an explicit `[]` on a single-item array (#441)
|
|
||||||
- [meta] use `npmignore` to autogenerate an npmignore file
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `has-symbol`, `object-inspect`, `tape`
|
|
||||||
|
|
||||||
## **6.10.3**
|
|
||||||
- [Fix] `parse`: ignore `__proto__` keys (#428)
|
|
||||||
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
|
|
||||||
- [actions] reuse common workflows
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `object-inspect`, `tape`
|
|
||||||
|
|
||||||
## **6.10.2**
|
|
||||||
- [Fix] `stringify`: actually fix cyclic references (#426)
|
|
||||||
- [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
|
|
||||||
- [readme] remove travis badge; add github actions/codecov badges; update URLs
|
|
||||||
- [Docs] add note and links for coercing primitive values (#408)
|
|
||||||
- [actions] update codecov uploader
|
|
||||||
- [actions] update workflows
|
|
||||||
- [Tests] clean up stringify tests slightly
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `object-inspect`, `safe-publish-latest`, `tape`
|
|
||||||
|
|
||||||
## **6.10.1**
|
|
||||||
- [Fix] `stringify`: avoid exception on repeated object values (#402)
|
|
||||||
|
|
||||||
## **6.10.0**
|
|
||||||
- [New] `stringify`: throw on cycles, instead of an infinite loop (#395, #394, #393)
|
|
||||||
- [New] `parse`: add `allowSparse` option for collapsing arrays with missing indices (#312)
|
|
||||||
- [meta] fix README.md (#399)
|
|
||||||
- [meta] only run `npm run dist` in publish, not install
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `has-symbols`, `tape`
|
|
||||||
- [Tests] fix tests on node v0.6
|
|
||||||
- [Tests] use `ljharb/actions/node/install` instead of `ljharb/actions/node/run`
|
|
||||||
- [Tests] Revert "[meta] ignore eclint transitive audit warning"
|
|
||||||
|
|
||||||
## **6.9.9**
|
|
||||||
- [Fix] fix regressions from robustness refactor
|
|
||||||
- [meta] add `npmignore` to autogenerate an npmignore file
|
|
||||||
- [actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.9.8**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
|
|
||||||
- [readme] replace runkit CI badge with shields.io check-runs badge
|
|
||||||
- [actions] fix rebase workflow permissions
|
|
||||||
|
|
||||||
## **6.9.7**
|
|
||||||
- [Fix] `parse`: ignore `__proto__` keys (#428)
|
|
||||||
- [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
|
|
||||||
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
|
|
||||||
- [readme] remove travis badge; add github actions/codecov badges; update URLs
|
|
||||||
- [Docs] add note and links for coercing primitive values (#408)
|
|
||||||
- [Tests] clean up stringify tests slightly
|
|
||||||
- [meta] fix README.md (#399)
|
|
||||||
- Revert "[meta] ignore eclint transitive audit warning"
|
|
||||||
- [actions] backport actions from main
|
|
||||||
- [Dev Deps] backport updates from main
|
|
||||||
|
|
||||||
## **6.9.6**
|
|
||||||
- [Fix] restore `dist` dir; mistakenly removed in d4f6c32
|
|
||||||
|
|
||||||
## **6.9.5**
|
|
||||||
- [Fix] `stringify`: do not encode parens for RFC1738
|
|
||||||
- [Fix] `stringify`: fix arrayFormat comma with empty array/objects (#350)
|
|
||||||
- [Refactor] `format`: remove `util.assign` call
|
|
||||||
- [meta] add "Allow Edits" workflow; update rebase workflow
|
|
||||||
- [actions] switch Automatic Rebase workflow to `pull_request_target` event
|
|
||||||
- [Tests] `stringify`: add tests for #378
|
|
||||||
- [Tests] migrate tests to Github Actions
|
|
||||||
- [Tests] run `nyc` on all tests; use `tape` runner
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `browserify`, `mkdirp`, `object-inspect`, `tape`; add `aud`
|
|
||||||
|
|
||||||
## **6.9.4**
|
|
||||||
- [Fix] `stringify`: when `arrayFormat` is `comma`, respect `serializeDate` (#364)
|
|
||||||
- [Refactor] `stringify`: reduce branching (part of #350)
|
|
||||||
- [Refactor] move `maybeMap` to `utils`
|
|
||||||
- [Dev Deps] update `browserify`, `tape`
|
|
||||||
|
|
||||||
## **6.9.3**
|
|
||||||
- [Fix] proper comma parsing of URL-encoded commas (#361)
|
|
||||||
- [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
|
|
||||||
|
|
||||||
## **6.9.2**
|
|
||||||
- [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
|
|
||||||
- [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
|
|
||||||
- [meta] ignore eclint transitive audit warning
|
|
||||||
- [meta] fix indentation in package.json
|
|
||||||
- [meta] add tidelift marketing copy
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `object-inspect`, `has-symbols`, `tape`, `mkdirp`, `iconv-lite`
|
|
||||||
- [actions] add automatic rebasing / merge commit blocking
|
|
||||||
|
|
||||||
## **6.9.1**
|
|
||||||
- [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
|
|
||||||
- [Fix] `parse`: with comma true, do not split non-string values (#334)
|
|
||||||
- [meta] add `funding` field
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`
|
|
||||||
- [Tests] use shared travis-ci config
|
|
||||||
|
|
||||||
## **6.9.0**
|
|
||||||
- [New] `parse`/`stringify`: Pass extra key/value argument to `decoder` (#333)
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `evalmd`
|
|
||||||
- [Tests] `parse`: add passing `arrayFormat` tests
|
|
||||||
- [Tests] add `posttest` using `npx aud` to run `npm audit` without a lockfile
|
|
||||||
- [Tests] up to `node` `v12.10`, `v11.15`, `v10.16`, `v8.16`
|
|
||||||
- [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
|
|
||||||
|
|
||||||
## **6.8.5**
|
|
||||||
- [Fix] fix regressions from robustness refactor
|
|
||||||
- [meta] add `npmignore` to autogenerate an npmignore file
|
|
||||||
- [actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.8.4**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
|
|
||||||
- [readme] replace runkit CI badge with shields.io check-runs badge
|
|
||||||
- [actions] fix rebase workflow permissions
|
|
||||||
|
|
||||||
## **6.8.3**
|
|
||||||
- [Fix] `parse`: ignore `__proto__` keys (#428)
|
|
||||||
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
|
|
||||||
- [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
|
|
||||||
- [readme] remove travis badge; add github actions/codecov badges; update URLs
|
|
||||||
- [Tests] clean up stringify tests slightly
|
|
||||||
- [Docs] add note and links for coercing primitive values (#408)
|
|
||||||
- [meta] fix README.md (#399)
|
|
||||||
- [actions] backport actions from main
|
|
||||||
- [Dev Deps] backport updates from main
|
|
||||||
- [Refactor] `stringify`: reduce branching
|
|
||||||
- [meta] do not publish workflow files
|
|
||||||
|
|
||||||
## **6.8.2**
|
|
||||||
- [Fix] proper comma parsing of URL-encoded commas (#361)
|
|
||||||
- [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
|
|
||||||
|
|
||||||
## **6.8.1**
|
|
||||||
- [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
|
|
||||||
- [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
|
|
||||||
- [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
|
|
||||||
- [fix] `parse`: with comma true, do not split non-string values (#334)
|
|
||||||
- [meta] add tidelift marketing copy
|
|
||||||
- [meta] add `funding` field
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`, `safe-publish-latest`, `evalmd`, `has-symbols`, `iconv-lite`, `mkdirp`, `object-inspect`
|
|
||||||
- [Tests] `parse`: add passing `arrayFormat` tests
|
|
||||||
- [Tests] use shared travis-ci configs
|
|
||||||
- [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
|
|
||||||
- [actions] add automatic rebasing / merge commit blocking
|
|
||||||
|
|
||||||
## **6.8.0**
|
|
||||||
- [New] add `depth=false` to preserve the original key; [Fix] `depth=0` should preserve the original key (#326)
|
|
||||||
- [New] [Fix] stringify symbols and bigints
|
|
||||||
- [Fix] ensure node 0.12 can stringify Symbols
|
|
||||||
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
|
|
||||||
- [Refactor] `formats`: tiny bit of cleanup.
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `browserify`, `safe-publish-latest`, `iconv-lite`, `tape`
|
|
||||||
- [Tests] add tests for `depth=0` and `depth=false` behavior, both current and intuitive/intended (#326)
|
|
||||||
- [Tests] use `eclint` instead of `editorconfig-tools`
|
|
||||||
- [docs] readme: add security note
|
|
||||||
- [meta] add github sponsorship
|
|
||||||
- [meta] add FUNDING.yml
|
|
||||||
- [meta] Clean up license text so it’s properly detected as BSD-3-Clause
|
|
||||||
|
|
||||||
## **6.7.5**
|
|
||||||
- [Fix] fix regressions from robustness refactor
|
|
||||||
- [meta] add `npmignore` to autogenerate an npmignore file
|
|
||||||
- [actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.7.4**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
|
|
||||||
- [readme] replace runkit CI badge with shields.io check-runs badge
|
|
||||||
- [actions] fix rebase workflow permissions
|
|
||||||
|
|
||||||
## **6.7.3**
|
|
||||||
- [Fix] `parse`: ignore `__proto__` keys (#428)
|
|
||||||
- [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
|
|
||||||
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
|
|
||||||
- [readme] remove travis badge; add github actions/codecov badges; update URLs
|
|
||||||
- [Docs] add note and links for coercing primitive values (#408)
|
|
||||||
- [meta] fix README.md (#399)
|
|
||||||
- [meta] do not publish workflow files
|
|
||||||
- [actions] backport actions from main
|
|
||||||
- [Dev Deps] backport updates from main
|
|
||||||
- [Tests] use `nyc` for coverage
|
|
||||||
- [Tests] clean up stringify tests slightly
|
|
||||||
|
|
||||||
## **6.7.2**
|
|
||||||
- [Fix] proper comma parsing of URL-encoded commas (#361)
|
|
||||||
- [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
|
|
||||||
|
|
||||||
## **6.7.1**
|
|
||||||
- [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
|
|
||||||
- [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
|
|
||||||
- [fix] `parse`: with comma true, do not split non-string values (#334)
|
|
||||||
- [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
|
|
||||||
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
|
|
||||||
- [Refactor] `formats`: tiny bit of cleanup.
|
|
||||||
- readme: add security note
|
|
||||||
- [meta] add tidelift marketing copy
|
|
||||||
- [meta] add `funding` field
|
|
||||||
- [meta] add FUNDING.yml
|
|
||||||
- [meta] Clean up license text so it’s properly detected as BSD-3-Clause
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`, `safe-publish-latest`, `evalmd`, `iconv-lite`, `mkdirp`, `object-inspect`, `browserify`
|
|
||||||
- [Tests] `parse`: add passing `arrayFormat` tests
|
|
||||||
- [Tests] use shared travis-ci configs
|
|
||||||
- [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
|
|
||||||
- [Tests] add tests for `depth=0` and `depth=false` behavior, both current and intuitive/intended
|
|
||||||
- [Tests] use `eclint` instead of `editorconfig-tools`
|
|
||||||
- [actions] add automatic rebasing / merge commit blocking
|
|
||||||
|
|
||||||
## **6.7.0**
|
|
||||||
- [New] `stringify`/`parse`: add `comma` as an `arrayFormat` option (#276, #219)
|
|
||||||
- [Fix] correctly parse nested arrays (#212)
|
|
||||||
- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source, also with an array source
|
|
||||||
- [Robustness] `stringify`: cache `Object.prototype.hasOwnProperty`
|
|
||||||
- [Refactor] `utils`: `isBuffer`: small tweak; add tests
|
|
||||||
- [Refactor] use cached `Array.isArray`
|
|
||||||
- [Refactor] `parse`/`stringify`: make a function to normalize the options
|
|
||||||
- [Refactor] `utils`: reduce observable [[Get]]s
|
|
||||||
- [Refactor] `stringify`/`utils`: cache `Array.isArray`
|
|
||||||
- [Tests] always use `String(x)` over `x.toString()`
|
|
||||||
- [Tests] fix Buffer tests to work in node < 4.5 and node < 5.10
|
|
||||||
- [Tests] temporarily allow coverage to fail
|
|
||||||
|
|
||||||
## **6.6.3**
|
|
||||||
- [Fix] fix regressions from robustness refactor
|
|
||||||
- [meta] add `npmignore` to autogenerate an npmignore file
|
|
||||||
- [actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.6.2**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
|
|
||||||
- [readme] replace runkit CI badge with shields.io check-runs badge
|
|
||||||
- [actions] fix rebase workflow permissions
|
|
||||||
|
|
||||||
## **6.6.1**
|
|
||||||
- [Fix] `parse`: ignore `__proto__` keys (#428)
|
|
||||||
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
|
|
||||||
- [Fix] `utils.merge`: avoid a crash with a null target and an array source
|
|
||||||
- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
|
|
||||||
- [Fix] correctly parse nested arrays
|
|
||||||
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
|
|
||||||
- [Robustness] `stringify`: cache `Object.prototype.hasOwnProperty`
|
|
||||||
- [Refactor] `formats`: tiny bit of cleanup.
|
|
||||||
- [Refactor] `utils`: `isBuffer`: small tweak; add tests
|
|
||||||
- [Refactor]: `stringify`/`utils`: cache `Array.isArray`
|
|
||||||
- [Refactor] `utils`: reduce observable [[Get]]s
|
|
||||||
- [Refactor] use cached `Array.isArray`
|
|
||||||
- [Refactor] `parse`/`stringify`: make a function to normalize the options
|
|
||||||
- [readme] remove travis badge; add github actions/codecov badges; update URLs
|
|
||||||
- [Docs] Clarify the need for "arrayLimit" option
|
|
||||||
- [meta] fix README.md (#399)
|
|
||||||
- [meta] do not publish workflow files
|
|
||||||
- [meta] Clean up license text so it’s properly detected as BSD-3-Clause
|
|
||||||
- [meta] add FUNDING.yml
|
|
||||||
- [meta] Fixes typo in CHANGELOG.md
|
|
||||||
- [actions] backport actions from main
|
|
||||||
- [Tests] fix Buffer tests to work in node < 4.5 and node < 5.10
|
|
||||||
- [Tests] always use `String(x)` over `x.toString()`
|
|
||||||
- [Dev Deps] backport from main
|
|
||||||
|
|
||||||
## **6.6.0**
|
|
||||||
- [New] Add support for iso-8859-1, utf8 "sentinel" and numeric entities (#268)
|
|
||||||
- [New] move two-value combine to a `utils` function (#189)
|
|
||||||
- [Fix] `stringify`: fix a crash with `strictNullHandling` and a custom `filter`/`serializeDate` (#279)
|
|
||||||
- [Fix] when `parseArrays` is false, properly handle keys ending in `[]` (#260)
|
|
||||||
- [Fix] `stringify`: do not crash in an obscure combo of `interpretNumericEntities`, a bad custom `decoder`, & `iso-8859-1`
|
|
||||||
- [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
|
|
||||||
- [refactor] `stringify`: Avoid arr = arr.concat(...), push to the existing instance (#269)
|
|
||||||
- [Refactor] `parse`: only need to reassign the var once
|
|
||||||
- [Refactor] `parse`/`stringify`: clean up `charset` options checking; fix defaults
|
|
||||||
- [Refactor] add missing defaults
|
|
||||||
- [Refactor] `parse`: one less `concat` call
|
|
||||||
- [Refactor] `utils`: `compactQueue`: make it explicitly side-effecting
|
|
||||||
- [Dev Deps] update `browserify`, `eslint`, `@ljharb/eslint-config`, `iconv-lite`, `safe-publish-latest`, `tape`
|
|
||||||
- [Tests] up to `node` `v10.10`, `v9.11`, `v8.12`, `v6.14`, `v4.9`; pin included builds to LTS
|
|
||||||
|
|
||||||
## **6.5.5**
|
|
||||||
- [Fix] fix regressions from robustness refactor
|
|
||||||
- [meta] add `npmignore` to autogenerate an npmignore file
|
|
||||||
- [actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.5.4**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
|
|
||||||
- [readme] replace runkit CI badge with shields.io check-runs badge
|
|
||||||
- [actions] fix rebase workflow permissions
|
|
||||||
|
|
||||||
## **6.5.3**
|
|
||||||
- [Fix] `parse`: ignore `__proto__` keys (#428)
|
|
||||||
- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
|
|
||||||
- [Fix] correctly parse nested arrays
|
|
||||||
- [Fix] `stringify`: fix a crash with `strictNullHandling` and a custom `filter`/`serializeDate` (#279)
|
|
||||||
- [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
|
|
||||||
- [Fix] when `parseArrays` is false, properly handle keys ending in `[]`
|
|
||||||
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
|
|
||||||
- [Fix] `utils.merge`: avoid a crash with a null target and an array source
|
|
||||||
- [Refactor] `utils`: reduce observable [[Get]]s
|
|
||||||
- [Refactor] use cached `Array.isArray`
|
|
||||||
- [Refactor] `stringify`: Avoid arr = arr.concat(...), push to the existing instance (#269)
|
|
||||||
- [Refactor] `parse`: only need to reassign the var once
|
|
||||||
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
|
|
||||||
- [readme] remove travis badge; add github actions/codecov badges; update URLs
|
|
||||||
- [Docs] Clean up license text so it’s properly detected as BSD-3-Clause
|
|
||||||
- [Docs] Clarify the need for "arrayLimit" option
|
|
||||||
- [meta] fix README.md (#399)
|
|
||||||
- [meta] add FUNDING.yml
|
|
||||||
- [actions] backport actions from main
|
|
||||||
- [Tests] always use `String(x)` over `x.toString()`
|
|
||||||
- [Tests] remove nonexistent tape option
|
|
||||||
- [Dev Deps] backport from main
|
|
||||||
|
|
||||||
## **6.5.2**
|
|
||||||
- [Fix] use `safer-buffer` instead of `Buffer` constructor
|
|
||||||
- [Refactor] utils: `module.exports` one thing, instead of mutating `exports` (#230)
|
|
||||||
- [Dev Deps] update `browserify`, `eslint`, `iconv-lite`, `safer-buffer`, `tape`, `browserify`
|
|
||||||
|
|
||||||
## **6.5.1**
|
|
||||||
- [Fix] Fix parsing & compacting very deep objects (#224)
|
|
||||||
- [Refactor] name utils functions
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`
|
|
||||||
- [Tests] up to `node` `v8.4`; use `nvm install-latest-npm` so newer npm doesn’t break older node
|
|
||||||
- [Tests] Use precise dist for Node.js 0.6 runtime (#225)
|
|
||||||
- [Tests] make 0.6 required, now that it’s passing
|
|
||||||
- [Tests] on `node` `v8.2`; fix npm on node 0.6
|
|
||||||
|
|
||||||
## **6.5.0**
|
|
||||||
- [New] add `utils.assign`
|
|
||||||
- [New] pass default encoder/decoder to custom encoder/decoder functions (#206)
|
|
||||||
- [New] `parse`/`stringify`: add `ignoreQueryPrefix`/`addQueryPrefix` options, respectively (#213)
|
|
||||||
- [Fix] Handle stringifying empty objects with addQueryPrefix (#217)
|
|
||||||
- [Fix] do not mutate `options` argument (#207)
|
|
||||||
- [Refactor] `parse`: cache index to reuse in else statement (#182)
|
|
||||||
- [Docs] add various badges to readme (#208)
|
|
||||||
- [Dev Deps] update `eslint`, `browserify`, `iconv-lite`, `tape`
|
|
||||||
- [Tests] up to `node` `v8.1`, `v7.10`, `v6.11`; npm v4.6 breaks on node < v1; npm v5+ breaks on node < v4
|
|
||||||
- [Tests] add `editorconfig-tools`
|
|
||||||
|
|
||||||
## **6.4.3**
|
|
||||||
- [Fix] fix regressions from robustness refactor
|
|
||||||
- [meta] add `npmignore` to autogenerate an npmignore file
|
|
||||||
- [actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.4.2**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] replace runkit CI badge with shields.io check-runs badge
|
|
||||||
- [readme] replace travis CI badge with shields.io check-runs badge
|
|
||||||
- [actions] fix rebase workflow permissions
|
|
||||||
|
|
||||||
## **6.4.1**
|
|
||||||
- [Fix] `parse`: ignore `__proto__` keys (#428)
|
|
||||||
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
|
|
||||||
- [Fix] use `safer-buffer` instead of `Buffer` constructor
|
|
||||||
- [Fix] `utils.merge`: avoid a crash with a null target and an array source
|
|
||||||
- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
|
|
||||||
- [Fix] `stringify`: fix a crash with `strictNullHandling` and a custom `filter`/`serializeDate` (#279)
|
|
||||||
- [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
|
|
||||||
- [Fix] when `parseArrays` is false, properly handle keys ending in `[]`
|
|
||||||
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
|
|
||||||
- [Refactor] use cached `Array.isArray`
|
|
||||||
- [Refactor] `stringify`: Avoid arr = arr.concat(...), push to the existing instance (#269)
|
|
||||||
- [readme] remove travis badge; add github actions/codecov badges; update URLs
|
|
||||||
- [Docs] Clarify the need for "arrayLimit" option
|
|
||||||
- [meta] fix README.md (#399)
|
|
||||||
- [meta] Clean up license text so it’s properly detected as BSD-3-Clause
|
|
||||||
- [meta] add FUNDING.yml
|
|
||||||
- [actions] backport actions from main
|
|
||||||
- [Tests] remove nonexistent tape option
|
|
||||||
- [Dev Deps] backport from main
|
|
||||||
|
|
||||||
## **6.4.0**
|
|
||||||
- [New] `qs.stringify`: add `encodeValuesOnly` option
|
|
||||||
- [Fix] follow `allowPrototypes` option during merge (#201, #201)
|
|
||||||
- [Fix] support keys starting with brackets (#202, #200)
|
|
||||||
- [Fix] chmod a-x
|
|
||||||
- [Dev Deps] update `eslint`
|
|
||||||
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
|
|
||||||
- [eslint] reduce warnings
|
|
||||||
|
|
||||||
## **6.3.5**
|
|
||||||
- [Fix] fix regressions from robustness refactor
|
|
||||||
- [meta] add `npmignore` to autogenerate an npmignore file
|
|
||||||
- [actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.3.4**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] replace travis CI badge with shields.io check-runs badge
|
|
||||||
- [actions] fix rebase workflow permissions
|
|
||||||
|
|
||||||
## **6.3.3**
|
|
||||||
- [Fix] `parse`: ignore `__proto__` keys (#428)
|
|
||||||
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
|
|
||||||
- [Fix] `utils.merge`: avoid a crash with a null target and an array source
|
|
||||||
- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
|
|
||||||
- [Fix] `stringify`: fix a crash with `strictNullHandling` and a custom `filter`/`serializeDate` (#279)
|
|
||||||
- [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
|
|
||||||
- [Fix] when `parseArrays` is false, properly handle keys ending in `[]`
|
|
||||||
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
|
|
||||||
- [Refactor] use cached `Array.isArray`
|
|
||||||
- [Refactor] `stringify`: Avoid arr = arr.concat(...), push to the existing instance (#269)
|
|
||||||
- [Docs] Clarify the need for "arrayLimit" option
|
|
||||||
- [meta] fix README.md (#399)
|
|
||||||
- [meta] Clean up license text so it’s properly detected as BSD-3-Clause
|
|
||||||
- [meta] add FUNDING.yml
|
|
||||||
- [actions] backport actions from main
|
|
||||||
- [Tests] use `safer-buffer` instead of `Buffer` constructor
|
|
||||||
- [Tests] remove nonexistent tape option
|
|
||||||
- [Dev Deps] backport from main
|
|
||||||
|
|
||||||
## **6.3.2**
|
|
||||||
- [Fix] follow `allowPrototypes` option during merge (#201, #200)
|
|
||||||
- [Dev Deps] update `eslint`
|
|
||||||
- [Fix] chmod a-x
|
|
||||||
- [Fix] support keys starting with brackets (#202, #200)
|
|
||||||
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
|
|
||||||
|
|
||||||
## **6.3.1**
|
|
||||||
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties (thanks, @snyk!)
|
|
||||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `browserify`, `iconv-lite`, `qs-iconv`, `tape`
|
|
||||||
- [Tests] on all node minors; improve test matrix
|
|
||||||
- [Docs] document stringify option `allowDots` (#195)
|
|
||||||
- [Docs] add empty object and array values example (#195)
|
|
||||||
- [Docs] Fix minor inconsistency/typo (#192)
|
|
||||||
- [Docs] document stringify option `sort` (#191)
|
|
||||||
- [Refactor] `stringify`: throw faster with an invalid encoder
|
|
||||||
- [Refactor] remove unnecessary escapes (#184)
|
|
||||||
- Remove contributing.md, since `qs` is no longer part of `hapi` (#183)
|
|
||||||
|
|
||||||
## **6.3.0**
|
|
||||||
- [New] Add support for RFC 1738 (#174, #173)
|
|
||||||
- [New] `stringify`: Add `serializeDate` option to customize Date serialization (#159)
|
|
||||||
- [Fix] ensure `utils.merge` handles merging two arrays
|
|
||||||
- [Refactor] only constructors should be capitalized
|
|
||||||
- [Refactor] capitalized var names are for constructors only
|
|
||||||
- [Refactor] avoid using a sparse array
|
|
||||||
- [Robustness] `formats`: cache `String#replace`
|
|
||||||
- [Dev Deps] update `browserify`, `eslint`, `@ljharb/eslint-config`; add `safe-publish-latest`
|
|
||||||
- [Tests] up to `node` `v6.8`, `v4.6`; improve test matrix
|
|
||||||
- [Tests] flesh out arrayLimit/arrayFormat tests (#107)
|
|
||||||
- [Tests] skip Object.create tests when null objects are not available
|
|
||||||
- [Tests] Turn on eslint for test files (#175)
|
|
||||||
|
|
||||||
## **6.2.6**
|
|
||||||
- [Fix] fix regression from robustness refactor
|
|
||||||
- [meta] add `npmignore` to autogenerate an npmignore file
|
|
||||||
- [actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.2.5**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] replace travis CI badge with shields.io check-runs badge
|
|
||||||
- [actions] fix rebase workflow permissions
|
|
||||||
|
|
||||||
## **6.2.4**
|
|
||||||
- [Fix] `parse`: ignore `__proto__` keys (#428)
|
|
||||||
- [Fix] `utils.merge`: avoid a crash with a null target and an array source
|
|
||||||
- [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
|
|
||||||
- [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
|
|
||||||
- [Fix] when `parseArrays` is false, properly handle keys ending in `[]`
|
|
||||||
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
|
|
||||||
- [Refactor] use cached `Array.isArray`
|
|
||||||
- [Docs] Clarify the need for "arrayLimit" option
|
|
||||||
- [meta] fix README.md (#399)
|
|
||||||
- [meta] Clean up license text so it’s properly detected as BSD-3-Clause
|
|
||||||
- [meta] add FUNDING.yml
|
|
||||||
- [actions] backport actions from main
|
|
||||||
- [Tests] use `safer-buffer` instead of `Buffer` constructor
|
|
||||||
- [Tests] remove nonexistent tape option
|
|
||||||
- [Dev Deps] backport from main
|
|
||||||
|
|
||||||
## **6.2.3**
|
|
||||||
- [Fix] follow `allowPrototypes` option during merge (#201, #200)
|
|
||||||
- [Fix] chmod a-x
|
|
||||||
- [Fix] support keys starting with brackets (#202, #200)
|
|
||||||
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
|
|
||||||
|
|
||||||
## **6.2.2**
|
|
||||||
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties
|
|
||||||
|
|
||||||
## **6.2.1**
|
|
||||||
- [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values
|
|
||||||
- [Refactor] Be explicit and use `Object.prototype.hasOwnProperty.call`
|
|
||||||
- [Tests] remove `parallelshell` since it does not reliably report failures
|
|
||||||
- [Tests] up to `node` `v6.3`, `v5.12`
|
|
||||||
- [Dev Deps] update `tape`, `eslint`, `@ljharb/eslint-config`, `qs-iconv`
|
|
||||||
|
|
||||||
## [**6.2.0**](https://github.com/ljharb/qs/issues?milestone=36&state=closed)
|
|
||||||
- [New] pass Buffers to the encoder/decoder directly (#161)
|
|
||||||
- [New] add "encoder" and "decoder" options, for custom param encoding/decoding (#160)
|
|
||||||
- [Fix] fix compacting of nested sparse arrays (#150)
|
|
||||||
|
|
||||||
## **6.1.4**
|
|
||||||
- [Fix] fix regression from robustness refactor
|
|
||||||
- [meta] add `npmignore` to autogenerate an npmignore file
|
|
||||||
- [actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.1.3**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] replace travis CI badge with shields.io check-runs badge
|
|
||||||
|
|
||||||
## **6.1.2**
|
|
||||||
- [Fix] follow `allowPrototypes` option during merge (#201, #200)
|
|
||||||
- [Fix] chmod a-x
|
|
||||||
- [Fix] support keys starting with brackets (#202, #200)
|
|
||||||
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
|
|
||||||
|
|
||||||
## **6.1.1**
|
|
||||||
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties
|
|
||||||
|
|
||||||
## [**6.1.0**](https://github.com/ljharb/qs/issues?milestone=35&state=closed)
|
|
||||||
- [New] allowDots option for `stringify` (#151)
|
|
||||||
- [Fix] "sort" option should work at a depth of 3 or more (#151)
|
|
||||||
- [Fix] Restore `dist` directory; will be removed in v7 (#148)
|
|
||||||
|
|
||||||
## **6.0.6**
|
|
||||||
- [Fix] fix regression from robustness refactor
|
|
||||||
- [meta] add `npmignore` to autogenerate an npmignore file
|
|
||||||
- [actions] update reusable workflows
|
|
||||||
|
|
||||||
## **6.0.5**
|
|
||||||
- [Robustness] avoid `.push`, use `void`
|
|
||||||
- [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
|
|
||||||
- [readme] replace travis CI badge with shields.io check-runs badge
|
|
||||||
|
|
||||||
## **6.0.4**
|
|
||||||
- [Fix] follow `allowPrototypes` option during merge (#201, #200)
|
|
||||||
- [Fix] chmod a-x
|
|
||||||
- [Fix] support keys starting with brackets (#202, #200)
|
|
||||||
- [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
|
|
||||||
|
|
||||||
## **6.0.3**
|
|
||||||
- [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties
|
|
||||||
- [Fix] Restore `dist` directory; will be removed in v7 (#148)
|
|
||||||
|
|
||||||
## [**6.0.2**](https://github.com/ljharb/qs/issues?milestone=33&state=closed)
|
|
||||||
- Revert ES6 requirement and restore support for node down to v0.8.
|
|
||||||
|
|
||||||
## [**6.0.1**](https://github.com/ljharb/qs/issues?milestone=32&state=closed)
|
|
||||||
- [**#127**](https://github.com/ljharb/qs/pull/127) Fix engines definition in package.json
|
|
||||||
|
|
||||||
## [**6.0.0**](https://github.com/ljharb/qs/issues?milestone=31&state=closed)
|
|
||||||
- [**#124**](https://github.com/ljharb/qs/issues/124) Use ES6 and drop support for node < v4
|
|
||||||
|
|
||||||
## **5.2.1**
|
|
||||||
- [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values
|
|
||||||
|
|
||||||
## [**5.2.0**](https://github.com/ljharb/qs/issues?milestone=30&state=closed)
|
|
||||||
- [**#64**](https://github.com/ljharb/qs/issues/64) Add option to sort object keys in the query string
|
|
||||||
|
|
||||||
## [**5.1.0**](https://github.com/ljharb/qs/issues?milestone=29&state=closed)
|
|
||||||
- [**#117**](https://github.com/ljharb/qs/issues/117) make URI encoding stringified results optional
|
|
||||||
- [**#106**](https://github.com/ljharb/qs/issues/106) Add flag `skipNulls` to optionally skip null values in stringify
|
|
||||||
|
|
||||||
## [**5.0.0**](https://github.com/ljharb/qs/issues?milestone=28&state=closed)
|
|
||||||
- [**#114**](https://github.com/ljharb/qs/issues/114) default allowDots to false
|
|
||||||
- [**#100**](https://github.com/ljharb/qs/issues/100) include dist to npm
|
|
||||||
|
|
||||||
## [**4.0.0**](https://github.com/ljharb/qs/issues?milestone=26&state=closed)
|
|
||||||
- [**#98**](https://github.com/ljharb/qs/issues/98) make returning plain objects and allowing prototype overwriting properties optional
|
|
||||||
|
|
||||||
## [**3.1.0**](https://github.com/ljharb/qs/issues?milestone=24&state=closed)
|
|
||||||
- [**#89**](https://github.com/ljharb/qs/issues/89) Add option to disable "Transform dot notation to bracket notation"
|
|
||||||
|
|
||||||
## [**3.0.0**](https://github.com/ljharb/qs/issues?milestone=23&state=closed)
|
|
||||||
- [**#80**](https://github.com/ljharb/qs/issues/80) qs.parse silently drops properties
|
|
||||||
- [**#77**](https://github.com/ljharb/qs/issues/77) Perf boost
|
|
||||||
- [**#60**](https://github.com/ljharb/qs/issues/60) Add explicit option to disable array parsing
|
|
||||||
- [**#74**](https://github.com/ljharb/qs/issues/74) Bad parse when turning array into object
|
|
||||||
- [**#81**](https://github.com/ljharb/qs/issues/81) Add a `filter` option
|
|
||||||
- [**#68**](https://github.com/ljharb/qs/issues/68) Fixed issue with recursion and passing strings into objects.
|
|
||||||
- [**#66**](https://github.com/ljharb/qs/issues/66) Add mixed array and object dot notation support Closes: #47
|
|
||||||
- [**#76**](https://github.com/ljharb/qs/issues/76) RFC 3986
|
|
||||||
- [**#85**](https://github.com/ljharb/qs/issues/85) No equal sign
|
|
||||||
- [**#84**](https://github.com/ljharb/qs/issues/84) update license attribute
|
|
||||||
|
|
||||||
## [**2.4.1**](https://github.com/ljharb/qs/issues?milestone=20&state=closed)
|
|
||||||
- [**#73**](https://github.com/ljharb/qs/issues/73) Property 'hasOwnProperty' of object #<Object> is not a function
|
|
||||||
|
|
||||||
## [**2.4.0**](https://github.com/ljharb/qs/issues?milestone=19&state=closed)
|
|
||||||
- [**#70**](https://github.com/ljharb/qs/issues/70) Add arrayFormat option
|
|
||||||
|
|
||||||
## [**2.3.3**](https://github.com/ljharb/qs/issues?milestone=18&state=closed)
|
|
||||||
- [**#59**](https://github.com/ljharb/qs/issues/59) make sure array indexes are >= 0, closes #57
|
|
||||||
- [**#58**](https://github.com/ljharb/qs/issues/58) make qs usable for browser loader
|
|
||||||
|
|
||||||
## [**2.3.2**](https://github.com/ljharb/qs/issues?milestone=17&state=closed)
|
|
||||||
- [**#55**](https://github.com/ljharb/qs/issues/55) allow merging a string into an object
|
|
||||||
|
|
||||||
## [**2.3.1**](https://github.com/ljharb/qs/issues?milestone=16&state=closed)
|
|
||||||
- [**#52**](https://github.com/ljharb/qs/issues/52) Return "undefined" and "false" instead of throwing "TypeError".
|
|
||||||
|
|
||||||
## [**2.3.0**](https://github.com/ljharb/qs/issues?milestone=15&state=closed)
|
|
||||||
- [**#50**](https://github.com/ljharb/qs/issues/50) add option to omit array indices, closes #46
|
|
||||||
|
|
||||||
## [**2.2.5**](https://github.com/ljharb/qs/issues?milestone=14&state=closed)
|
|
||||||
- [**#39**](https://github.com/ljharb/qs/issues/39) Is there an alternative to Buffer.isBuffer?
|
|
||||||
- [**#49**](https://github.com/ljharb/qs/issues/49) refactor utils.merge, fixes #45
|
|
||||||
- [**#41**](https://github.com/ljharb/qs/issues/41) avoid browserifying Buffer, for #39
|
|
||||||
|
|
||||||
## [**2.2.4**](https://github.com/ljharb/qs/issues?milestone=13&state=closed)
|
|
||||||
- [**#38**](https://github.com/ljharb/qs/issues/38) how to handle object keys beginning with a number
|
|
||||||
|
|
||||||
## [**2.2.3**](https://github.com/ljharb/qs/issues?milestone=12&state=closed)
|
|
||||||
- [**#37**](https://github.com/ljharb/qs/issues/37) parser discards first empty value in array
|
|
||||||
- [**#36**](https://github.com/ljharb/qs/issues/36) Update to lab 4.x
|
|
||||||
|
|
||||||
## [**2.2.2**](https://github.com/ljharb/qs/issues?milestone=11&state=closed)
|
|
||||||
- [**#33**](https://github.com/ljharb/qs/issues/33) Error when plain object in a value
|
|
||||||
- [**#34**](https://github.com/ljharb/qs/issues/34) use Object.prototype.hasOwnProperty.call instead of obj.hasOwnProperty
|
|
||||||
- [**#24**](https://github.com/ljharb/qs/issues/24) Changelog? Semver?
|
|
||||||
|
|
||||||
## [**2.2.1**](https://github.com/ljharb/qs/issues?milestone=10&state=closed)
|
|
||||||
- [**#32**](https://github.com/ljharb/qs/issues/32) account for circular references properly, closes #31
|
|
||||||
- [**#31**](https://github.com/ljharb/qs/issues/31) qs.parse stackoverflow on circular objects
|
|
||||||
|
|
||||||
## [**2.2.0**](https://github.com/ljharb/qs/issues?milestone=9&state=closed)
|
|
||||||
- [**#26**](https://github.com/ljharb/qs/issues/26) Don't use Buffer global if it's not present
|
|
||||||
- [**#30**](https://github.com/ljharb/qs/issues/30) Bug when merging non-object values into arrays
|
|
||||||
- [**#29**](https://github.com/ljharb/qs/issues/29) Don't call Utils.clone at the top of Utils.merge
|
|
||||||
- [**#23**](https://github.com/ljharb/qs/issues/23) Ability to not limit parameters?
|
|
||||||
|
|
||||||
## [**2.1.0**](https://github.com/ljharb/qs/issues?milestone=8&state=closed)
|
|
||||||
- [**#22**](https://github.com/ljharb/qs/issues/22) Enable using a RegExp as delimiter
|
|
||||||
|
|
||||||
## [**2.0.0**](https://github.com/ljharb/qs/issues?milestone=7&state=closed)
|
|
||||||
- [**#18**](https://github.com/ljharb/qs/issues/18) Why is there arrayLimit?
|
|
||||||
- [**#20**](https://github.com/ljharb/qs/issues/20) Configurable parametersLimit
|
|
||||||
- [**#21**](https://github.com/ljharb/qs/issues/21) make all limits optional, for #18, for #20
|
|
||||||
|
|
||||||
## [**1.2.2**](https://github.com/ljharb/qs/issues?milestone=6&state=closed)
|
|
||||||
- [**#19**](https://github.com/ljharb/qs/issues/19) Don't overwrite null values
|
|
||||||
|
|
||||||
## [**1.2.1**](https://github.com/ljharb/qs/issues?milestone=5&state=closed)
|
|
||||||
- [**#16**](https://github.com/ljharb/qs/issues/16) ignore non-string delimiters
|
|
||||||
- [**#15**](https://github.com/ljharb/qs/issues/15) Close code block
|
|
||||||
|
|
||||||
## [**1.2.0**](https://github.com/ljharb/qs/issues?milestone=4&state=closed)
|
|
||||||
- [**#12**](https://github.com/ljharb/qs/issues/12) Add optional delim argument
|
|
||||||
- [**#13**](https://github.com/ljharb/qs/issues/13) fix #11: flattened keys in array are now correctly parsed
|
|
||||||
|
|
||||||
## [**1.1.0**](https://github.com/ljharb/qs/issues?milestone=3&state=closed)
|
|
||||||
- [**#7**](https://github.com/ljharb/qs/issues/7) Empty values of a POST array disappear after being submitted
|
|
||||||
- [**#9**](https://github.com/ljharb/qs/issues/9) Should not omit equals signs (=) when value is null
|
|
||||||
- [**#6**](https://github.com/ljharb/qs/issues/6) Minor grammar fix in README
|
|
||||||
|
|
||||||
## [**1.0.2**](https://github.com/ljharb/qs/issues?milestone=2&state=closed)
|
|
||||||
- [**#5**](https://github.com/ljharb/qs/issues/5) array holes incorrectly copied into object on large index
|
|
||||||
-29
@@ -1,29 +0,0 @@
|
|||||||
BSD 3-Clause License
|
|
||||||
|
|
||||||
Copyright (c) 2014, Nathan LaFreniere and other [contributors](https://github.com/ljharb/qs/graphs/contributors)
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. Neither the name of the copyright holder nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
-758
@@ -1,758 +0,0 @@
|
|||||||
<p align="center">
|
|
||||||
<img alt="qs" src="./logos/banner_default.png" width="800" />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# qs <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
|
|
||||||
|
|
||||||
[![github actions][actions-image]][actions-url]
|
|
||||||
[![coverage][codecov-image]][codecov-url]
|
|
||||||
[![License][license-image]][license-url]
|
|
||||||
[![Downloads][downloads-image]][downloads-url]
|
|
||||||
[](https://bestpractices.coreinfrastructure.org/projects/9058)
|
|
||||||
|
|
||||||
[![npm badge][npm-badge-png]][package-url]
|
|
||||||
|
|
||||||
A querystring parsing and stringifying library with some added security.
|
|
||||||
|
|
||||||
Lead Maintainer: [Jordan Harband](https://github.com/ljharb)
|
|
||||||
|
|
||||||
The **qs** module was originally created and maintained by [TJ Holowaychuk](https://github.com/visionmedia/node-querystring).
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var qs = require('qs');
|
|
||||||
var assert = require('assert');
|
|
||||||
|
|
||||||
var obj = qs.parse('a=c');
|
|
||||||
assert.deepEqual(obj, { a: 'c' });
|
|
||||||
|
|
||||||
var str = qs.stringify(obj);
|
|
||||||
assert.equal(str, 'a=c');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Parsing Objects
|
|
||||||
|
|
||||||
[](#preventEval)
|
|
||||||
```javascript
|
|
||||||
qs.parse(string, [options]);
|
|
||||||
```
|
|
||||||
|
|
||||||
**qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`.
|
|
||||||
For example, the string `'foo[bar]=baz'` converts to:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
assert.deepEqual(qs.parse('foo[bar]=baz'), {
|
|
||||||
foo: {
|
|
||||||
bar: 'baz'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
When using the `plainObjects` option the parsed value is returned as a null object, created via `{ __proto__: null }` and as such you should be aware that prototype methods will not exist on it and a user may set those names to whatever value they like:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var nullObject = qs.parse('a[hasOwnProperty]=b', { plainObjects: true });
|
|
||||||
assert.deepEqual(nullObject, { a: { hasOwnProperty: 'b' } });
|
|
||||||
```
|
|
||||||
|
|
||||||
By default parameters that would overwrite properties on the object prototype are ignored, if you wish to keep the data from those fields either use `plainObjects` as mentioned above, or set `allowPrototypes` to `true` which will allow user input to overwrite those properties.
|
|
||||||
*WARNING* It is generally a bad idea to enable this option as it can cause problems when attempting to use the properties that have been overwritten.
|
|
||||||
Always be careful with this option.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var protoObject = qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true });
|
|
||||||
assert.deepEqual(protoObject, { a: { hasOwnProperty: 'b' } });
|
|
||||||
```
|
|
||||||
|
|
||||||
URI encoded strings work too:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
assert.deepEqual(qs.parse('a%5Bb%5D=c'), {
|
|
||||||
a: { b: 'c' }
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also nest your objects, like `'foo[bar][baz]=foobarbaz'`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
assert.deepEqual(qs.parse('foo[bar][baz]=foobarbaz'), {
|
|
||||||
foo: {
|
|
||||||
bar: {
|
|
||||||
baz: 'foobarbaz'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, when nesting objects **qs** will only parse up to 5 children deep.
|
|
||||||
This means if you attempt to parse a string like `'a[b][c][d][e][f][g][h][i]=j'` your resulting object will be:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var expected = {
|
|
||||||
a: {
|
|
||||||
b: {
|
|
||||||
c: {
|
|
||||||
d: {
|
|
||||||
e: {
|
|
||||||
f: {
|
|
||||||
'[g][h][i]': 'j'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var string = 'a[b][c][d][e][f][g][h][i]=j';
|
|
||||||
assert.deepEqual(qs.parse(string), expected);
|
|
||||||
```
|
|
||||||
|
|
||||||
This depth can be overridden by passing a `depth` option to `qs.parse(string, [options])`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var deep = qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 });
|
|
||||||
assert.deepEqual(deep, { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } });
|
|
||||||
```
|
|
||||||
|
|
||||||
You can configure **qs** to throw an error when parsing nested input beyond this depth using the `strictDepth` option (defaulted to false):
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
try {
|
|
||||||
qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1, strictDepth: true });
|
|
||||||
} catch (err) {
|
|
||||||
assert(err instanceof RangeError);
|
|
||||||
assert.strictEqual(err.message, 'Input depth exceeded depth option of 1 and strictDepth is true');
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The depth limit helps mitigate abuse when **qs** is used to parse user input, and it is recommended to keep it a reasonably small number. The strictDepth option adds a layer of protection by throwing an error when the limit is exceeded, allowing you to catch and handle such cases.
|
|
||||||
|
|
||||||
For similar reasons, by default **qs** will only parse up to 1000 parameters. This can be overridden by passing a `parameterLimit` option:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var limited = qs.parse('a=b&c=d', { parameterLimit: 1 });
|
|
||||||
assert.deepEqual(limited, { a: 'b' });
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want an error to be thrown whenever the a limit is exceeded (eg, `parameterLimit`, `arrayLimit`), set the `throwOnLimitExceeded` option to `true`. This option will generate a descriptive error if the query string exceeds a configured limit.
|
|
||||||
```javascript
|
|
||||||
try {
|
|
||||||
qs.parse('a=1&b=2&c=3&d=4', { parameterLimit: 3, throwOnLimitExceeded: true });
|
|
||||||
} catch (err) {
|
|
||||||
assert(err instanceof Error);
|
|
||||||
assert.strictEqual(err.message, 'Parameter limit exceeded. Only 3 parameters allowed.');
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
When `throwOnLimitExceeded` is set to `false` (default), **qs** will parse up to the specified `parameterLimit` and ignore the rest without throwing an error.
|
|
||||||
|
|
||||||
To bypass the leading question mark, use `ignoreQueryPrefix`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var prefixed = qs.parse('?a=b&c=d', { ignoreQueryPrefix: true });
|
|
||||||
assert.deepEqual(prefixed, { a: 'b', c: 'd' });
|
|
||||||
```
|
|
||||||
|
|
||||||
An optional delimiter can also be passed:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var delimited = qs.parse('a=b;c=d', { delimiter: ';' });
|
|
||||||
assert.deepEqual(delimited, { a: 'b', c: 'd' });
|
|
||||||
```
|
|
||||||
|
|
||||||
Delimiters can be a regular expression too:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var regexed = qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ });
|
|
||||||
assert.deepEqual(regexed, { a: 'b', c: 'd', e: 'f' });
|
|
||||||
```
|
|
||||||
|
|
||||||
Option `allowDots` can be used to enable dot notation:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var withDots = qs.parse('a.b=c', { allowDots: true });
|
|
||||||
assert.deepEqual(withDots, { a: { b: 'c' } });
|
|
||||||
```
|
|
||||||
|
|
||||||
Option `decodeDotInKeys` can be used to decode dots in keys
|
|
||||||
Note: it implies `allowDots`, so `parse` will error if you set `decodeDotInKeys` to `true`, and `allowDots` to `false`.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var withDots = qs.parse('name%252Eobj.first=John&name%252Eobj.last=Doe', { decodeDotInKeys: true });
|
|
||||||
assert.deepEqual(withDots, { 'name.obj': { first: 'John', last: 'Doe' }});
|
|
||||||
```
|
|
||||||
|
|
||||||
Option `allowEmptyArrays` can be used to allowing empty array values in object
|
|
||||||
```javascript
|
|
||||||
var withEmptyArrays = qs.parse('foo[]&bar=baz', { allowEmptyArrays: true });
|
|
||||||
assert.deepEqual(withEmptyArrays, { foo: [], bar: 'baz' });
|
|
||||||
```
|
|
||||||
|
|
||||||
Option `duplicates` can be used to change the behavior when duplicate keys are encountered
|
|
||||||
```javascript
|
|
||||||
assert.deepEqual(qs.parse('foo=bar&foo=baz'), { foo: ['bar', 'baz'] });
|
|
||||||
assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'combine' }), { foo: ['bar', 'baz'] });
|
|
||||||
assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'first' }), { foo: 'bar' });
|
|
||||||
assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'last' }), { foo: 'baz' });
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that keys with bracket notation (`[]`) always combine into arrays, regardless of the `duplicates` setting:
|
|
||||||
```javascript
|
|
||||||
assert.deepEqual(qs.parse('a=1&a=2&b[]=1&b[]=2', { duplicates: 'last' }), { a: '2', b: ['1', '2'] });
|
|
||||||
```
|
|
||||||
|
|
||||||
If you have to deal with legacy browsers or services, there's also support for decoding percent-encoded octets as iso-8859-1:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var oldCharset = qs.parse('a=%A7', { charset: 'iso-8859-1' });
|
|
||||||
assert.deepEqual(oldCharset, { a: '§' });
|
|
||||||
```
|
|
||||||
|
|
||||||
Some services add an initial `utf8=✓` value to forms so that old Internet Explorer versions are more likely to submit the form as utf-8.
|
|
||||||
Additionally, the server can check the value against wrong encodings of the checkmark character and detect that a query string or `application/x-www-form-urlencoded` body was *not* sent as utf-8, eg. if the form had an `accept-charset` parameter or the containing page had a different character set.
|
|
||||||
|
|
||||||
**qs** supports this mechanism via the `charsetSentinel` option.
|
|
||||||
If specified, the `utf8` parameter will be omitted from the returned object.
|
|
||||||
It will be used to switch to `iso-8859-1`/`utf-8` mode depending on how the checkmark is encoded.
|
|
||||||
|
|
||||||
**Important**: When you specify both the `charset` option and the `charsetSentinel` option, the `charset` will be overridden when the request contains a `utf8` parameter from which the actual charset can be deduced.
|
|
||||||
In that sense the `charset` will behave as the default charset rather than the authoritative charset.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var detectedAsUtf8 = qs.parse('utf8=%E2%9C%93&a=%C3%B8', {
|
|
||||||
charset: 'iso-8859-1',
|
|
||||||
charsetSentinel: true
|
|
||||||
});
|
|
||||||
assert.deepEqual(detectedAsUtf8, { a: 'ø' });
|
|
||||||
|
|
||||||
// Browsers encode the checkmark as ✓ when submitting as iso-8859-1:
|
|
||||||
var detectedAsIso8859_1 = qs.parse('utf8=%26%2310003%3B&a=%F8', {
|
|
||||||
charset: 'utf-8',
|
|
||||||
charsetSentinel: true
|
|
||||||
});
|
|
||||||
assert.deepEqual(detectedAsIso8859_1, { a: 'ø' });
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to decode the `&#...;` syntax to the actual character, you can specify the `interpretNumericEntities` option as well:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var detectedAsIso8859_1 = qs.parse('a=%26%239786%3B', {
|
|
||||||
charset: 'iso-8859-1',
|
|
||||||
interpretNumericEntities: true
|
|
||||||
});
|
|
||||||
assert.deepEqual(detectedAsIso8859_1, { a: '☺' });
|
|
||||||
```
|
|
||||||
|
|
||||||
It also works when the charset has been detected in `charsetSentinel` mode.
|
|
||||||
|
|
||||||
### Parsing Arrays
|
|
||||||
|
|
||||||
**qs** can also parse arrays using a similar `[]` notation:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var withArray = qs.parse('a[]=b&a[]=c');
|
|
||||||
assert.deepEqual(withArray, { a: ['b', 'c'] });
|
|
||||||
```
|
|
||||||
|
|
||||||
You may specify an index as well:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var withIndexes = qs.parse('a[1]=c&a[0]=b');
|
|
||||||
assert.deepEqual(withIndexes, { a: ['b', 'c'] });
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number to create an array.
|
|
||||||
When creating arrays with specific indices, **qs** will compact a sparse array to only the existing values preserving their order:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var noSparse = qs.parse('a[1]=b&a[15]=c');
|
|
||||||
assert.deepEqual(noSparse, { a: ['b', 'c'] });
|
|
||||||
```
|
|
||||||
|
|
||||||
You may also use `allowSparse` option to parse sparse arrays:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var sparseArray = qs.parse('a[1]=2&a[3]=5', { allowSparse: true });
|
|
||||||
assert.deepEqual(sparseArray, { a: [, '2', , '5'] });
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that an empty string is also a value, and will be preserved:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var withEmptyString = qs.parse('a[]=&a[]=b');
|
|
||||||
assert.deepEqual(withEmptyString, { a: ['', 'b'] });
|
|
||||||
|
|
||||||
var withIndexedEmptyString = qs.parse('a[0]=b&a[1]=&a[2]=c');
|
|
||||||
assert.deepEqual(withIndexedEmptyString, { a: ['b', '', 'c'] });
|
|
||||||
```
|
|
||||||
|
|
||||||
**qs** will also limit arrays to a maximum of `20` elements.
|
|
||||||
Any array members with an index of `20` or greater will instead be converted to an object with the index as the key.
|
|
||||||
This is needed to handle cases when someone sent, for example, `a[999999999]` and it will take significant time to iterate over this huge array.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var withMaxIndex = qs.parse('a[100]=b');
|
|
||||||
assert.deepEqual(withMaxIndex, { a: { '100': 'b' } });
|
|
||||||
```
|
|
||||||
|
|
||||||
This limit can be overridden by passing an `arrayLimit` option:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var withArrayLimit = qs.parse('a[1]=b', { arrayLimit: 0 });
|
|
||||||
assert.deepEqual(withArrayLimit, { a: { '1': 'b' } });
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to throw an error whenever the array limit is exceeded, set the `throwOnLimitExceeded` option to `true`. This option will generate a descriptive error if the query string exceeds a configured limit.
|
|
||||||
```javascript
|
|
||||||
try {
|
|
||||||
qs.parse('a[1]=b', { arrayLimit: 0, throwOnLimitExceeded: true });
|
|
||||||
} catch (err) {
|
|
||||||
assert(err instanceof Error);
|
|
||||||
assert.strictEqual(err.message, 'Array limit exceeded. Only 0 elements allowed in an array.');
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
When `throwOnLimitExceeded` is set to `false` (default), **qs** will parse up to the specified `arrayLimit` and if the limit is exceeded, the array will instead be converted to an object with the index as the key
|
|
||||||
|
|
||||||
To prevent array syntax (`a[]`, `a[0]`) from being parsed as arrays, set `parseArrays` to `false`.
|
|
||||||
Note that duplicate keys (e.g. `a=b&a=c`) may still produce arrays when `duplicates` is `'combine'` (the default).
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var noParsingArrays = qs.parse('a[]=b', { parseArrays: false });
|
|
||||||
assert.deepEqual(noParsingArrays, { a: { '0': 'b' } });
|
|
||||||
```
|
|
||||||
|
|
||||||
If you mix notations, **qs** will merge the two items into an object:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var mixedNotation = qs.parse('a[0]=b&a[b]=c');
|
|
||||||
assert.deepEqual(mixedNotation, { a: { '0': 'b', b: 'c' } });
|
|
||||||
```
|
|
||||||
|
|
||||||
When a key appears as both a plain value and an object, **qs** will by default wrap the conflicting values in an array (`strictMerge` defaults to `true`):
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
assert.deepEqual(qs.parse('a[b]=c&a=d'), { a: [{ b: 'c' }, 'd'] });
|
|
||||||
assert.deepEqual(qs.parse('a=d&a[b]=c'), { a: ['d', { b: 'c' }] });
|
|
||||||
```
|
|
||||||
|
|
||||||
To restore the legacy behavior (where the primitive is used as a key with value `true`), set `strictMerge` to `false`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
assert.deepEqual(qs.parse('a[b]=c&a=d', { strictMerge: false }), { a: { b: 'c', d: true } });
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also create arrays of objects:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var arraysOfObjects = qs.parse('a[][b]=c');
|
|
||||||
assert.deepEqual(arraysOfObjects, { a: [{ b: 'c' }] });
|
|
||||||
```
|
|
||||||
|
|
||||||
Some people use comma to join array, **qs** can parse it:
|
|
||||||
```javascript
|
|
||||||
var arraysOfObjects = qs.parse('a=b,c', { comma: true })
|
|
||||||
assert.deepEqual(arraysOfObjects, { a: ['b', 'c'] })
|
|
||||||
```
|
|
||||||
(_this cannot convert nested objects, such as `a={b:1},{c:d}`_)
|
|
||||||
|
|
||||||
### Parsing primitive/scalar values (numbers, booleans, null, etc)
|
|
||||||
|
|
||||||
By default, all values are parsed as strings.
|
|
||||||
This behavior will not change and is explained in [issue #91](https://github.com/ljharb/qs/issues/91).
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var primitiveValues = qs.parse('a=15&b=true&c=null');
|
|
||||||
assert.deepEqual(primitiveValues, { a: '15', b: 'true', c: 'null' });
|
|
||||||
```
|
|
||||||
|
|
||||||
If you wish to auto-convert values which look like numbers, booleans, and other values into their primitive counterparts, you can use the [query-types Express JS middleware](https://github.com/xpepermint/query-types) which will auto-convert all request query parameters.
|
|
||||||
|
|
||||||
### Stringifying
|
|
||||||
|
|
||||||
[](#preventEval)
|
|
||||||
```javascript
|
|
||||||
qs.stringify(object, [options]);
|
|
||||||
```
|
|
||||||
|
|
||||||
When stringifying, **qs** by default URI encodes output. Objects are stringified as you would expect:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
assert.equal(qs.stringify({ a: 'b' }), 'a=b');
|
|
||||||
assert.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');
|
|
||||||
```
|
|
||||||
|
|
||||||
This encoding can be disabled by setting the `encode` option to `false`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var unencoded = qs.stringify({ a: { b: 'c' } }, { encode: false });
|
|
||||||
assert.equal(unencoded, 'a[b]=c');
|
|
||||||
```
|
|
||||||
|
|
||||||
Encoding can be disabled for keys by setting the `encodeValuesOnly` option to `true`:
|
|
||||||
```javascript
|
|
||||||
var encodedValues = qs.stringify(
|
|
||||||
{ a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] },
|
|
||||||
{ encodeValuesOnly: true }
|
|
||||||
);
|
|
||||||
assert.equal(encodedValues,'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h');
|
|
||||||
```
|
|
||||||
|
|
||||||
This encoding can also be replaced by a custom encoding method set as `encoder` option:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str) {
|
|
||||||
// Passed in values `a`, `b`, `c`
|
|
||||||
return // Return encoded string
|
|
||||||
}})
|
|
||||||
```
|
|
||||||
|
|
||||||
_(Note: the `encoder` option does not apply if `encode` is `false`)_
|
|
||||||
|
|
||||||
Analogue to the `encoder` there is a `decoder` option for `parse` to override decoding of properties and values:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var decoded = qs.parse('x=z', { decoder: function (str) {
|
|
||||||
// Passed in values `x`, `z`
|
|
||||||
return // Return decoded string
|
|
||||||
}})
|
|
||||||
```
|
|
||||||
|
|
||||||
You can encode keys and values using different logic by using the type argument provided to the encoder:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str, defaultEncoder, charset, type) {
|
|
||||||
if (type === 'key') {
|
|
||||||
return // Encoded key
|
|
||||||
} else if (type === 'value') {
|
|
||||||
return // Encoded value
|
|
||||||
}
|
|
||||||
}})
|
|
||||||
```
|
|
||||||
|
|
||||||
The type argument is also provided to the decoder:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var decoded = qs.parse('x=z', { decoder: function (str, defaultDecoder, charset, type) {
|
|
||||||
if (type === 'key') {
|
|
||||||
return // Decoded key
|
|
||||||
} else if (type === 'value') {
|
|
||||||
return // Decoded value
|
|
||||||
}
|
|
||||||
}})
|
|
||||||
```
|
|
||||||
|
|
||||||
Examples beyond this point will be shown as though the output is not URI encoded for clarity.
|
|
||||||
Please note that the return values in these cases *will* be URI encoded during real usage.
|
|
||||||
|
|
||||||
When arrays are stringified, they follow the `arrayFormat` option, which defaults to `indices`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
qs.stringify({ a: ['b', 'c', 'd'] });
|
|
||||||
// 'a[0]=b&a[1]=c&a[2]=d'
|
|
||||||
```
|
|
||||||
|
|
||||||
You may override this by setting the `indices` option to `false`, or to be more explicit, the `arrayFormat` option to `repeat`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false });
|
|
||||||
// 'a=b&a=c&a=d'
|
|
||||||
```
|
|
||||||
|
|
||||||
You may use the `arrayFormat` option to specify the format of the output array:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })
|
|
||||||
// 'a[0]=b&a[1]=c'
|
|
||||||
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })
|
|
||||||
// 'a[]=b&a[]=c'
|
|
||||||
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })
|
|
||||||
// 'a=b&a=c'
|
|
||||||
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' })
|
|
||||||
// 'a=b,c'
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: when using `arrayFormat` set to `'comma'`, you can also pass the `commaRoundTrip` option set to `true` or `false`, to append `[]` on single-item arrays, so that they can round trip through a parse.
|
|
||||||
|
|
||||||
When objects are stringified, by default they use bracket notation:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
qs.stringify({ a: { b: { c: 'd', e: 'f' } } });
|
|
||||||
// 'a[b][c]=d&a[b][e]=f'
|
|
||||||
```
|
|
||||||
|
|
||||||
You may override this to use dot notation by setting the `allowDots` option to `true`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
qs.stringify({ a: { b: { c: 'd', e: 'f' } } }, { allowDots: true });
|
|
||||||
// 'a.b.c=d&a.b.e=f'
|
|
||||||
```
|
|
||||||
|
|
||||||
You may encode the dot notation in the keys of object with option `encodeDotInKeys` by setting it to `true`:
|
|
||||||
Note: it implies `allowDots`, so `stringify` will error if you set `decodeDotInKeys` to `true`, and `allowDots` to `false`.
|
|
||||||
Caveat: when `encodeValuesOnly` is `true` as well as `encodeDotInKeys`, only dots in keys and nothing else will be encoded.
|
|
||||||
```javascript
|
|
||||||
qs.stringify({ "name.obj": { "first": "John", "last": "Doe" } }, { allowDots: true, encodeDotInKeys: true })
|
|
||||||
// 'name%252Eobj.first=John&name%252Eobj.last=Doe'
|
|
||||||
```
|
|
||||||
|
|
||||||
You may allow empty array values by setting the `allowEmptyArrays` option to `true`:
|
|
||||||
```javascript
|
|
||||||
qs.stringify({ foo: [], bar: 'baz' }, { allowEmptyArrays: true });
|
|
||||||
// 'foo[]&bar=baz'
|
|
||||||
```
|
|
||||||
|
|
||||||
Empty strings and null values will omit the value, but the equals sign (=) remains in place:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
assert.equal(qs.stringify({ a: '' }), 'a=');
|
|
||||||
```
|
|
||||||
|
|
||||||
Key with no values (such as an empty object or array) will return nothing:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
assert.equal(qs.stringify({ a: [] }), '');
|
|
||||||
assert.equal(qs.stringify({ a: {} }), '');
|
|
||||||
assert.equal(qs.stringify({ a: [{}] }), '');
|
|
||||||
assert.equal(qs.stringify({ a: { b: []} }), '');
|
|
||||||
assert.equal(qs.stringify({ a: { b: {}} }), '');
|
|
||||||
```
|
|
||||||
|
|
||||||
Properties that are set to `undefined` will be omitted entirely:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
assert.equal(qs.stringify({ a: null, b: undefined }), 'a=');
|
|
||||||
```
|
|
||||||
|
|
||||||
The query string may optionally be prepended with a question mark:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
assert.equal(qs.stringify({ a: 'b', c: 'd' }, { addQueryPrefix: true }), '?a=b&c=d');
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that when the output is an empty string, the prefix will not be added:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
assert.equal(qs.stringify({}, { addQueryPrefix: true }), '');
|
|
||||||
```
|
|
||||||
|
|
||||||
The delimiter may be overridden with stringify as well:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
assert.equal(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d');
|
|
||||||
```
|
|
||||||
|
|
||||||
If you only want to override the serialization of `Date` objects, you can provide a `serializeDate` option:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var date = new Date(7);
|
|
||||||
assert.equal(qs.stringify({ a: date }), 'a=1970-01-01T00:00:00.007Z'.replace(/:/g, '%3A'));
|
|
||||||
assert.equal(
|
|
||||||
qs.stringify({ a: date }, { serializeDate: function (d) { return d.getTime(); } }),
|
|
||||||
'a=7'
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
You may use the `sort` option to affect the order of parameter keys:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function alphabeticalSort(a, b) {
|
|
||||||
return a.localeCompare(b);
|
|
||||||
}
|
|
||||||
assert.equal(qs.stringify({ a: 'c', z: 'y', b : 'f' }, { sort: alphabeticalSort }), 'a=c&b=f&z=y');
|
|
||||||
```
|
|
||||||
|
|
||||||
Finally, you can use the `filter` option to restrict which keys will be included in the stringified output.
|
|
||||||
If you pass a function, it will be called for each key to obtain the replacement value.
|
|
||||||
Otherwise, if you pass an array, it will be used to select properties and array indices for stringification:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function filterFunc(prefix, value) {
|
|
||||||
if (prefix == 'b') {
|
|
||||||
// Return an `undefined` value to omit a property.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (prefix == 'e[f]') {
|
|
||||||
return value.getTime();
|
|
||||||
}
|
|
||||||
if (prefix == 'e[g][0]') {
|
|
||||||
return value * 2;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
qs.stringify({ a: 'b', c: 'd', e: { f: new Date(123), g: [2] } }, { filter: filterFunc });
|
|
||||||
// 'a=b&c=d&e[f]=123&e[g][0]=4'
|
|
||||||
qs.stringify({ a: 'b', c: 'd', e: 'f' }, { filter: ['a', 'e'] });
|
|
||||||
// 'a=b&e=f'
|
|
||||||
qs.stringify({ a: ['b', 'c', 'd'], e: 'f' }, { filter: ['a', 0, 2] });
|
|
||||||
// 'a[0]=b&a[2]=d'
|
|
||||||
```
|
|
||||||
|
|
||||||
You could also use `filter` to inject custom serialization for user defined types.
|
|
||||||
Consider you're working with some api that expects query strings of the format for ranges:
|
|
||||||
|
|
||||||
```
|
|
||||||
https://domain.com/endpoint?range=30...70
|
|
||||||
```
|
|
||||||
|
|
||||||
For which you model as:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
class Range {
|
|
||||||
constructor(from, to) {
|
|
||||||
this.from = from;
|
|
||||||
this.to = to;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You could _inject_ a custom serializer to handle values of this type:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
qs.stringify(
|
|
||||||
{
|
|
||||||
range: new Range(30, 70),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
filter: (prefix, value) => {
|
|
||||||
if (value instanceof Range) {
|
|
||||||
return `${value.from}...${value.to}`;
|
|
||||||
}
|
|
||||||
// serialize the usual way
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
// range=30...70
|
|
||||||
```
|
|
||||||
|
|
||||||
### Handling of `null` values
|
|
||||||
|
|
||||||
By default, `null` values are treated like empty strings:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var withNull = qs.stringify({ a: null, b: '' });
|
|
||||||
assert.equal(withNull, 'a=&b=');
|
|
||||||
```
|
|
||||||
|
|
||||||
Parsing does not distinguish between parameters with and without equal signs.
|
|
||||||
Both are converted to empty strings.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var equalsInsensitive = qs.parse('a&b=');
|
|
||||||
assert.deepEqual(equalsInsensitive, { a: '', b: '' });
|
|
||||||
```
|
|
||||||
|
|
||||||
To distinguish between `null` values and empty strings use the `strictNullHandling` flag. In the result string the `null`
|
|
||||||
values have no `=` sign:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var strictNull = qs.stringify({ a: null, b: '' }, { strictNullHandling: true });
|
|
||||||
assert.equal(strictNull, 'a&b=');
|
|
||||||
```
|
|
||||||
|
|
||||||
To parse values without `=` back to `null` use the `strictNullHandling` flag:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var parsedStrictNull = qs.parse('a&b=', { strictNullHandling: true });
|
|
||||||
assert.deepEqual(parsedStrictNull, { a: null, b: '' });
|
|
||||||
```
|
|
||||||
|
|
||||||
To completely skip rendering keys with `null` values, use the `skipNulls` flag:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var nullsSkipped = qs.stringify({ a: 'b', c: null}, { skipNulls: true });
|
|
||||||
assert.equal(nullsSkipped, 'a=b');
|
|
||||||
```
|
|
||||||
|
|
||||||
If you're communicating with legacy systems, you can switch to `iso-8859-1` using the `charset` option:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var iso = qs.stringify({ æ: 'æ' }, { charset: 'iso-8859-1' });
|
|
||||||
assert.equal(iso, '%E6=%E6');
|
|
||||||
```
|
|
||||||
|
|
||||||
Characters that don't exist in `iso-8859-1` will be converted to numeric entities, similar to what browsers do:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var numeric = qs.stringify({ a: '☺' }, { charset: 'iso-8859-1' });
|
|
||||||
assert.equal(numeric, 'a=%26%239786%3B');
|
|
||||||
```
|
|
||||||
|
|
||||||
You can use the `charsetSentinel` option to announce the character by including an `utf8=✓` parameter with the proper encoding if the checkmark, similar to what Ruby on Rails and others do when submitting forms.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var sentinel = qs.stringify({ a: '☺' }, { charsetSentinel: true });
|
|
||||||
assert.equal(sentinel, 'utf8=%E2%9C%93&a=%E2%98%BA');
|
|
||||||
|
|
||||||
var isoSentinel = qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' });
|
|
||||||
assert.equal(isoSentinel, 'utf8=%26%2310003%3B&a=%E6');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dealing with special character sets
|
|
||||||
|
|
||||||
By default the encoding and decoding of characters is done in `utf-8`, and `iso-8859-1` support is also built in via the `charset` parameter.
|
|
||||||
|
|
||||||
If you wish to encode querystrings to a different character set (i.e.
|
|
||||||
[Shift JIS](https://en.wikipedia.org/wiki/Shift_JIS)) you can use the
|
|
||||||
[`qs-iconv`](https://github.com/martinheidegger/qs-iconv) library:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var encoder = require('qs-iconv/encoder')('shift_jis');
|
|
||||||
var shiftJISEncoded = qs.stringify({ a: 'こんにちは!' }, { encoder: encoder });
|
|
||||||
assert.equal(shiftJISEncoded, 'a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I');
|
|
||||||
```
|
|
||||||
|
|
||||||
This also works for decoding of query strings:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var decoder = require('qs-iconv/decoder')('shift_jis');
|
|
||||||
var obj = qs.parse('a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I', { decoder: decoder });
|
|
||||||
assert.deepEqual(obj, { a: 'こんにちは!' });
|
|
||||||
```
|
|
||||||
|
|
||||||
### RFC 3986 and RFC 1738 space encoding
|
|
||||||
|
|
||||||
RFC3986 used as default option and encodes ' ' to *%20* which is backward compatible.
|
|
||||||
In the same time, output can be stringified as per RFC1738 with ' ' equal to '+'.
|
|
||||||
|
|
||||||
```
|
|
||||||
assert.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');
|
|
||||||
assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC3986' }), 'a=b%20c');
|
|
||||||
assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC1738' }), 'a=b+c');
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
Please email [@ljharb](https://github.com/ljharb) or see https://tidelift.com/security if you have a potential security vulnerability to report.
|
|
||||||
|
|
||||||
## qs for enterprise
|
|
||||||
|
|
||||||
Available as part of the Tidelift Subscription
|
|
||||||
|
|
||||||
The maintainers of qs and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications.
|
|
||||||
Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use.
|
|
||||||
[Learn more.](https://tidelift.com/subscription/pkg/npm-qs?utm_source=npm-qs&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
|
|
||||||
|
|
||||||
[package-url]: https://npmjs.org/package/qs
|
|
||||||
[npm-version-svg]: https://versionbadg.es/ljharb/qs.svg
|
|
||||||
[deps-svg]: https://david-dm.org/ljharb/qs.svg
|
|
||||||
[deps-url]: https://david-dm.org/ljharb/qs
|
|
||||||
[dev-deps-svg]: https://david-dm.org/ljharb/qs/dev-status.svg
|
|
||||||
[dev-deps-url]: https://david-dm.org/ljharb/qs#info=devDependencies
|
|
||||||
[npm-badge-png]: https://nodei.co/npm/qs.png?downloads=true&stars=true
|
|
||||||
[license-image]: https://img.shields.io/npm/l/qs.svg
|
|
||||||
[license-url]: LICENSE
|
|
||||||
[downloads-image]: https://img.shields.io/npm/dm/qs.svg
|
|
||||||
[downloads-url]: https://npm-stat.com/charts.html?package=qs
|
|
||||||
[codecov-image]: https://codecov.io/gh/ljharb/qs/branch/main/graphs/badge.svg
|
|
||||||
[codecov-url]: https://app.codecov.io/gh/ljharb/qs/
|
|
||||||
[actions-image]: https://img.shields.io/github/check-runs/ljharb/qs/main
|
|
||||||
[actions-url]: https://github.com/ljharb/qs/actions
|
|
||||||
|
|
||||||
## Acknowledgements
|
|
||||||
|
|
||||||
qs logo by [NUMI](https://github.com/numi-hq/open-design):
|
|
||||||
|
|
||||||
[<img src="https://raw.githubusercontent.com/numi-hq/open-design/main/assets/numi-lockup.png" alt="NUMI Logo" style="width: 200px;"/>](https://numi.tech/?ref=qs)
|
|
||||||
-141
File diff suppressed because one or more lines are too long
-56
@@ -1,56 +0,0 @@
|
|||||||
import ljharbConfig from '@ljharb/eslint-config/flat';
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
ignores: ['dist/'],
|
|
||||||
},
|
|
||||||
|
|
||||||
...ljharbConfig,
|
|
||||||
|
|
||||||
{
|
|
||||||
rules: {
|
|
||||||
complexity: 'off',
|
|
||||||
'consistent-return': 'warn',
|
|
||||||
'func-name-matching': 'off',
|
|
||||||
'id-length': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
max: 25,
|
|
||||||
min: 1,
|
|
||||||
properties: 'never',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
indent: ['error', 4],
|
|
||||||
'max-lines': 'off',
|
|
||||||
'max-lines-per-function': [
|
|
||||||
'error',
|
|
||||||
{ max: 150 },
|
|
||||||
],
|
|
||||||
'max-params': ['error', 18],
|
|
||||||
'max-statements': ['error', 100],
|
|
||||||
'multiline-comment-style': 'off',
|
|
||||||
'no-continue': 'warn',
|
|
||||||
'no-magic-numbers': 'off',
|
|
||||||
'no-restricted-syntax': [
|
|
||||||
'error',
|
|
||||||
'BreakStatement',
|
|
||||||
'DebuggerStatement',
|
|
||||||
'ForInStatement',
|
|
||||||
'LabeledStatement',
|
|
||||||
'WithStatement',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
files: ['test/**'],
|
|
||||||
rules: {
|
|
||||||
'function-paren-newline': 'off',
|
|
||||||
'max-lines-per-function': 'off',
|
|
||||||
'max-statements': 'off',
|
|
||||||
'no-buffer-constructor': 'off',
|
|
||||||
'no-extend-native': 'off',
|
|
||||||
'no-throw-literal': 'off',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var replace = String.prototype.replace;
|
|
||||||
var percentTwenties = /%20/g;
|
|
||||||
|
|
||||||
var Format = {
|
|
||||||
RFC1738: 'RFC1738',
|
|
||||||
RFC3986: 'RFC3986'
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
'default': Format.RFC3986,
|
|
||||||
formatters: {
|
|
||||||
RFC1738: function (value) {
|
|
||||||
return replace.call(value, percentTwenties, '+');
|
|
||||||
},
|
|
||||||
RFC3986: function (value) {
|
|
||||||
return String(value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
RFC1738: Format.RFC1738,
|
|
||||||
RFC3986: Format.RFC3986
|
|
||||||
};
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var stringify = require('./stringify');
|
|
||||||
var parse = require('./parse');
|
|
||||||
var formats = require('./formats');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
formats: formats,
|
|
||||||
parse: parse,
|
|
||||||
stringify: stringify
|
|
||||||
};
|
|
||||||
-373
@@ -1,373 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var utils = require('./utils');
|
|
||||||
|
|
||||||
var has = Object.prototype.hasOwnProperty;
|
|
||||||
var isArray = Array.isArray;
|
|
||||||
|
|
||||||
var defaults = {
|
|
||||||
allowDots: false,
|
|
||||||
allowEmptyArrays: false,
|
|
||||||
allowPrototypes: false,
|
|
||||||
allowSparse: false,
|
|
||||||
arrayLimit: 20,
|
|
||||||
charset: 'utf-8',
|
|
||||||
charsetSentinel: false,
|
|
||||||
comma: false,
|
|
||||||
decodeDotInKeys: false,
|
|
||||||
decoder: utils.decode,
|
|
||||||
delimiter: '&',
|
|
||||||
depth: 5,
|
|
||||||
duplicates: 'combine',
|
|
||||||
ignoreQueryPrefix: false,
|
|
||||||
interpretNumericEntities: false,
|
|
||||||
parameterLimit: 1000,
|
|
||||||
parseArrays: true,
|
|
||||||
plainObjects: false,
|
|
||||||
strictDepth: false,
|
|
||||||
strictMerge: true,
|
|
||||||
strictNullHandling: false,
|
|
||||||
throwOnLimitExceeded: false
|
|
||||||
};
|
|
||||||
|
|
||||||
var interpretNumericEntities = function (str) {
|
|
||||||
return str.replace(/&#(\d+);/g, function ($0, numberStr) {
|
|
||||||
return String.fromCharCode(parseInt(numberStr, 10));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var parseArrayValue = function (val, options, currentArrayLength) {
|
|
||||||
if (val && typeof val === 'string' && options.comma && val.indexOf(',') > -1) {
|
|
||||||
return val.split(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.throwOnLimitExceeded && currentArrayLength >= options.arrayLimit) {
|
|
||||||
throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return val;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is what browsers will submit when the ✓ character occurs in an
|
|
||||||
// application/x-www-form-urlencoded body and the encoding of the page containing
|
|
||||||
// the form is iso-8859-1, or when the submitted form has an accept-charset
|
|
||||||
// attribute of iso-8859-1. Presumably also with other charsets that do not contain
|
|
||||||
// the ✓ character, such as us-ascii.
|
|
||||||
var isoSentinel = 'utf8=%26%2310003%3B'; // encodeURIComponent('✓')
|
|
||||||
|
|
||||||
// These are the percent-encoded utf-8 octets representing a checkmark, indicating that the request actually is utf-8 encoded.
|
|
||||||
var charsetSentinel = 'utf8=%E2%9C%93'; // encodeURIComponent('✓')
|
|
||||||
|
|
||||||
var parseValues = function parseQueryStringValues(str, options) {
|
|
||||||
var obj = { __proto__: null };
|
|
||||||
|
|
||||||
var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, '') : str;
|
|
||||||
cleanStr = cleanStr.replace(/%5B/gi, '[').replace(/%5D/gi, ']');
|
|
||||||
|
|
||||||
var limit = options.parameterLimit === Infinity ? void undefined : options.parameterLimit;
|
|
||||||
var parts = cleanStr.split(
|
|
||||||
options.delimiter,
|
|
||||||
options.throwOnLimitExceeded && typeof limit !== 'undefined' ? limit + 1 : limit
|
|
||||||
);
|
|
||||||
|
|
||||||
if (options.throwOnLimitExceeded && typeof limit !== 'undefined' && parts.length > limit) {
|
|
||||||
throw new RangeError('Parameter limit exceeded. Only ' + limit + ' parameter' + (limit === 1 ? '' : 's') + ' allowed.');
|
|
||||||
}
|
|
||||||
|
|
||||||
var skipIndex = -1; // Keep track of where the utf8 sentinel was found
|
|
||||||
var i;
|
|
||||||
|
|
||||||
var charset = options.charset;
|
|
||||||
if (options.charsetSentinel) {
|
|
||||||
for (i = 0; i < parts.length; ++i) {
|
|
||||||
if (parts[i].indexOf('utf8=') === 0) {
|
|
||||||
if (parts[i] === charsetSentinel) {
|
|
||||||
charset = 'utf-8';
|
|
||||||
} else if (parts[i] === isoSentinel) {
|
|
||||||
charset = 'iso-8859-1';
|
|
||||||
}
|
|
||||||
skipIndex = i;
|
|
||||||
i = parts.length; // The eslint settings do not allow break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < parts.length; ++i) {
|
|
||||||
if (i === skipIndex) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var part = parts[i];
|
|
||||||
|
|
||||||
var bracketEqualsPos = part.indexOf(']=');
|
|
||||||
var pos = bracketEqualsPos === -1 ? part.indexOf('=') : bracketEqualsPos + 1;
|
|
||||||
|
|
||||||
var key;
|
|
||||||
var val;
|
|
||||||
if (pos === -1) {
|
|
||||||
key = options.decoder(part, defaults.decoder, charset, 'key');
|
|
||||||
val = options.strictNullHandling ? null : '';
|
|
||||||
} else {
|
|
||||||
key = options.decoder(part.slice(0, pos), defaults.decoder, charset, 'key');
|
|
||||||
|
|
||||||
if (key !== null) {
|
|
||||||
val = utils.maybeMap(
|
|
||||||
parseArrayValue(
|
|
||||||
part.slice(pos + 1),
|
|
||||||
options,
|
|
||||||
isArray(obj[key]) ? obj[key].length : 0
|
|
||||||
),
|
|
||||||
function (encodedVal) {
|
|
||||||
return options.decoder(encodedVal, defaults.decoder, charset, 'value');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val && options.interpretNumericEntities && charset === 'iso-8859-1') {
|
|
||||||
val = interpretNumericEntities(String(val));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (part.indexOf('[]=') > -1) {
|
|
||||||
val = isArray(val) ? [val] : val;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.comma && isArray(val) && val.length > options.arrayLimit) {
|
|
||||||
if (options.throwOnLimitExceeded) {
|
|
||||||
throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
|
|
||||||
}
|
|
||||||
val = utils.combine([], val, options.arrayLimit, options.plainObjects);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key !== null) {
|
|
||||||
var existing = has.call(obj, key);
|
|
||||||
if (existing && (options.duplicates === 'combine' || part.indexOf('[]=') > -1)) {
|
|
||||||
obj[key] = utils.combine(
|
|
||||||
obj[key],
|
|
||||||
val,
|
|
||||||
options.arrayLimit,
|
|
||||||
options.plainObjects
|
|
||||||
);
|
|
||||||
} else if (!existing || options.duplicates === 'last') {
|
|
||||||
obj[key] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
};
|
|
||||||
|
|
||||||
var parseObject = function (chain, val, options, valuesParsed) {
|
|
||||||
var currentArrayLength = 0;
|
|
||||||
if (chain.length > 0 && chain[chain.length - 1] === '[]') {
|
|
||||||
var parentKey = chain.slice(0, -1).join('');
|
|
||||||
currentArrayLength = Array.isArray(val) && val[parentKey] ? val[parentKey].length : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var leaf = valuesParsed ? val : parseArrayValue(val, options, currentArrayLength);
|
|
||||||
|
|
||||||
for (var i = chain.length - 1; i >= 0; --i) {
|
|
||||||
var obj;
|
|
||||||
var root = chain[i];
|
|
||||||
|
|
||||||
if (root === '[]' && options.parseArrays) {
|
|
||||||
if (utils.isOverflow(leaf)) {
|
|
||||||
// leaf is already an overflow object, preserve it
|
|
||||||
obj = leaf;
|
|
||||||
} else {
|
|
||||||
obj = options.allowEmptyArrays && (leaf === '' || (options.strictNullHandling && leaf === null))
|
|
||||||
? []
|
|
||||||
: utils.combine(
|
|
||||||
[],
|
|
||||||
leaf,
|
|
||||||
options.arrayLimit,
|
|
||||||
options.plainObjects
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
obj = options.plainObjects ? { __proto__: null } : {};
|
|
||||||
var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root;
|
|
||||||
var decodedRoot = options.decodeDotInKeys ? cleanRoot.replace(/%2E/g, '.') : cleanRoot;
|
|
||||||
var index = parseInt(decodedRoot, 10);
|
|
||||||
var isValidArrayIndex = !isNaN(index)
|
|
||||||
&& root !== decodedRoot
|
|
||||||
&& String(index) === decodedRoot
|
|
||||||
&& index >= 0
|
|
||||||
&& options.parseArrays;
|
|
||||||
if (!options.parseArrays && decodedRoot === '') {
|
|
||||||
obj = { 0: leaf };
|
|
||||||
} else if (isValidArrayIndex && index < options.arrayLimit) {
|
|
||||||
obj = [];
|
|
||||||
obj[index] = leaf;
|
|
||||||
} else if (isValidArrayIndex && options.throwOnLimitExceeded) {
|
|
||||||
throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
|
|
||||||
} else if (isValidArrayIndex) {
|
|
||||||
obj[index] = leaf;
|
|
||||||
utils.markOverflow(obj, index);
|
|
||||||
} else if (decodedRoot !== '__proto__') {
|
|
||||||
obj[decodedRoot] = leaf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
leaf = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
return leaf;
|
|
||||||
};
|
|
||||||
|
|
||||||
var splitKeyIntoSegments = function splitKeyIntoSegments(givenKey, options) {
|
|
||||||
var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey;
|
|
||||||
|
|
||||||
if (options.depth <= 0) {
|
|
||||||
if (!options.plainObjects && has.call(Object.prototype, key)) {
|
|
||||||
if (!options.allowPrototypes) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [key];
|
|
||||||
}
|
|
||||||
|
|
||||||
var brackets = /(\[[^[\]]*])/;
|
|
||||||
var child = /(\[[^[\]]*])/g;
|
|
||||||
|
|
||||||
var segment = brackets.exec(key);
|
|
||||||
var parent = segment ? key.slice(0, segment.index) : key;
|
|
||||||
|
|
||||||
var keys = [];
|
|
||||||
|
|
||||||
if (parent) {
|
|
||||||
if (!options.plainObjects && has.call(Object.prototype, parent)) {
|
|
||||||
if (!options.allowPrototypes) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
keys[keys.length] = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
var i = 0;
|
|
||||||
while ((segment = child.exec(key)) !== null && i < options.depth) {
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
var segmentContent = segment[1].slice(1, -1);
|
|
||||||
if (!options.plainObjects && has.call(Object.prototype, segmentContent)) {
|
|
||||||
if (!options.allowPrototypes) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
keys[keys.length] = segment[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (segment) {
|
|
||||||
if (options.strictDepth === true) {
|
|
||||||
throw new RangeError('Input depth exceeded depth option of ' + options.depth + ' and strictDepth is true');
|
|
||||||
}
|
|
||||||
|
|
||||||
keys[keys.length] = '[' + key.slice(segment.index) + ']';
|
|
||||||
}
|
|
||||||
|
|
||||||
return keys;
|
|
||||||
};
|
|
||||||
|
|
||||||
var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesParsed) {
|
|
||||||
if (!givenKey) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var keys = splitKeyIntoSegments(givenKey, options);
|
|
||||||
|
|
||||||
if (!keys) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseObject(keys, val, options, valuesParsed);
|
|
||||||
};
|
|
||||||
|
|
||||||
var normalizeParseOptions = function normalizeParseOptions(opts) {
|
|
||||||
if (!opts) {
|
|
||||||
return defaults;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof opts.allowEmptyArrays !== 'undefined' && typeof opts.allowEmptyArrays !== 'boolean') {
|
|
||||||
throw new TypeError('`allowEmptyArrays` option can only be `true` or `false`, when provided');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof opts.decodeDotInKeys !== 'undefined' && typeof opts.decodeDotInKeys !== 'boolean') {
|
|
||||||
throw new TypeError('`decodeDotInKeys` option can only be `true` or `false`, when provided');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.decoder !== null && typeof opts.decoder !== 'undefined' && typeof opts.decoder !== 'function') {
|
|
||||||
throw new TypeError('Decoder has to be a function.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {
|
|
||||||
throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof opts.throwOnLimitExceeded !== 'undefined' && typeof opts.throwOnLimitExceeded !== 'boolean') {
|
|
||||||
throw new TypeError('`throwOnLimitExceeded` option must be a boolean');
|
|
||||||
}
|
|
||||||
|
|
||||||
var charset = typeof opts.charset === 'undefined' ? defaults.charset : opts.charset;
|
|
||||||
|
|
||||||
var duplicates = typeof opts.duplicates === 'undefined' ? defaults.duplicates : opts.duplicates;
|
|
||||||
|
|
||||||
if (duplicates !== 'combine' && duplicates !== 'first' && duplicates !== 'last') {
|
|
||||||
throw new TypeError('The duplicates option must be either combine, first, or last');
|
|
||||||
}
|
|
||||||
|
|
||||||
var allowDots = typeof opts.allowDots === 'undefined' ? opts.decodeDotInKeys === true ? true : defaults.allowDots : !!opts.allowDots;
|
|
||||||
|
|
||||||
return {
|
|
||||||
allowDots: allowDots,
|
|
||||||
allowEmptyArrays: typeof opts.allowEmptyArrays === 'boolean' ? !!opts.allowEmptyArrays : defaults.allowEmptyArrays,
|
|
||||||
allowPrototypes: typeof opts.allowPrototypes === 'boolean' ? opts.allowPrototypes : defaults.allowPrototypes,
|
|
||||||
allowSparse: typeof opts.allowSparse === 'boolean' ? opts.allowSparse : defaults.allowSparse,
|
|
||||||
arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit,
|
|
||||||
charset: charset,
|
|
||||||
charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
|
|
||||||
comma: typeof opts.comma === 'boolean' ? opts.comma : defaults.comma,
|
|
||||||
decodeDotInKeys: typeof opts.decodeDotInKeys === 'boolean' ? opts.decodeDotInKeys : defaults.decodeDotInKeys,
|
|
||||||
decoder: typeof opts.decoder === 'function' ? opts.decoder : defaults.decoder,
|
|
||||||
delimiter: typeof opts.delimiter === 'string' || utils.isRegExp(opts.delimiter) ? opts.delimiter : defaults.delimiter,
|
|
||||||
// eslint-disable-next-line no-implicit-coercion, no-extra-parens
|
|
||||||
depth: (typeof opts.depth === 'number' || opts.depth === false) ? +opts.depth : defaults.depth,
|
|
||||||
duplicates: duplicates,
|
|
||||||
ignoreQueryPrefix: opts.ignoreQueryPrefix === true,
|
|
||||||
interpretNumericEntities: typeof opts.interpretNumericEntities === 'boolean' ? opts.interpretNumericEntities : defaults.interpretNumericEntities,
|
|
||||||
parameterLimit: typeof opts.parameterLimit === 'number' ? opts.parameterLimit : defaults.parameterLimit,
|
|
||||||
parseArrays: opts.parseArrays !== false,
|
|
||||||
plainObjects: typeof opts.plainObjects === 'boolean' ? opts.plainObjects : defaults.plainObjects,
|
|
||||||
strictDepth: typeof opts.strictDepth === 'boolean' ? !!opts.strictDepth : defaults.strictDepth,
|
|
||||||
strictMerge: typeof opts.strictMerge === 'boolean' ? !!opts.strictMerge : defaults.strictMerge,
|
|
||||||
strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling,
|
|
||||||
throwOnLimitExceeded: typeof opts.throwOnLimitExceeded === 'boolean' ? opts.throwOnLimitExceeded : false
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = function (str, opts) {
|
|
||||||
var options = normalizeParseOptions(opts);
|
|
||||||
|
|
||||||
if (str === '' || str === null || typeof str === 'undefined') {
|
|
||||||
return options.plainObjects ? { __proto__: null } : {};
|
|
||||||
}
|
|
||||||
|
|
||||||
var tempObj = typeof str === 'string' ? parseValues(str, options) : str;
|
|
||||||
var obj = options.plainObjects ? { __proto__: null } : {};
|
|
||||||
|
|
||||||
// Iterate over the keys and setup the new object
|
|
||||||
|
|
||||||
var keys = Object.keys(tempObj);
|
|
||||||
for (var i = 0; i < keys.length; ++i) {
|
|
||||||
var key = keys[i];
|
|
||||||
var newObj = parseKeys(key, tempObj[key], options, typeof str === 'string');
|
|
||||||
obj = utils.merge(obj, newObj, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.allowSparse === true) {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
return utils.compact(obj);
|
|
||||||
};
|
|
||||||
-356
@@ -1,356 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var getSideChannel = require('side-channel');
|
|
||||||
var utils = require('./utils');
|
|
||||||
var formats = require('./formats');
|
|
||||||
var has = Object.prototype.hasOwnProperty;
|
|
||||||
|
|
||||||
var arrayPrefixGenerators = {
|
|
||||||
brackets: function brackets(prefix) {
|
|
||||||
return prefix + '[]';
|
|
||||||
},
|
|
||||||
comma: 'comma',
|
|
||||||
indices: function indices(prefix, key) {
|
|
||||||
return prefix + '[' + key + ']';
|
|
||||||
},
|
|
||||||
repeat: function repeat(prefix) {
|
|
||||||
return prefix;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var isArray = Array.isArray;
|
|
||||||
var push = Array.prototype.push;
|
|
||||||
var pushToArray = function (arr, valueOrArray) {
|
|
||||||
push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
|
|
||||||
};
|
|
||||||
|
|
||||||
var toISO = Date.prototype.toISOString;
|
|
||||||
|
|
||||||
var defaultFormat = formats['default'];
|
|
||||||
var defaults = {
|
|
||||||
addQueryPrefix: false,
|
|
||||||
allowDots: false,
|
|
||||||
allowEmptyArrays: false,
|
|
||||||
arrayFormat: 'indices',
|
|
||||||
charset: 'utf-8',
|
|
||||||
charsetSentinel: false,
|
|
||||||
commaRoundTrip: false,
|
|
||||||
delimiter: '&',
|
|
||||||
encode: true,
|
|
||||||
encodeDotInKeys: false,
|
|
||||||
encoder: utils.encode,
|
|
||||||
encodeValuesOnly: false,
|
|
||||||
filter: void undefined,
|
|
||||||
format: defaultFormat,
|
|
||||||
formatter: formats.formatters[defaultFormat],
|
|
||||||
// deprecated
|
|
||||||
indices: false,
|
|
||||||
serializeDate: function serializeDate(date) {
|
|
||||||
return toISO.call(date);
|
|
||||||
},
|
|
||||||
skipNulls: false,
|
|
||||||
strictNullHandling: false
|
|
||||||
};
|
|
||||||
|
|
||||||
var isNonNullishPrimitive = function isNonNullishPrimitive(v) {
|
|
||||||
return typeof v === 'string'
|
|
||||||
|| typeof v === 'number'
|
|
||||||
|| typeof v === 'boolean'
|
|
||||||
|| typeof v === 'symbol'
|
|
||||||
|| typeof v === 'bigint';
|
|
||||||
};
|
|
||||||
|
|
||||||
var sentinel = {};
|
|
||||||
|
|
||||||
var stringify = function stringify(
|
|
||||||
object,
|
|
||||||
prefix,
|
|
||||||
generateArrayPrefix,
|
|
||||||
commaRoundTrip,
|
|
||||||
allowEmptyArrays,
|
|
||||||
strictNullHandling,
|
|
||||||
skipNulls,
|
|
||||||
encodeDotInKeys,
|
|
||||||
encoder,
|
|
||||||
filter,
|
|
||||||
sort,
|
|
||||||
allowDots,
|
|
||||||
serializeDate,
|
|
||||||
format,
|
|
||||||
formatter,
|
|
||||||
encodeValuesOnly,
|
|
||||||
charset,
|
|
||||||
sideChannel
|
|
||||||
) {
|
|
||||||
var obj = object;
|
|
||||||
|
|
||||||
var tmpSc = sideChannel;
|
|
||||||
var step = 0;
|
|
||||||
var findFlag = false;
|
|
||||||
while ((tmpSc = tmpSc.get(sentinel)) !== void undefined && !findFlag) {
|
|
||||||
// Where object last appeared in the ref tree
|
|
||||||
var pos = tmpSc.get(object);
|
|
||||||
step += 1;
|
|
||||||
if (typeof pos !== 'undefined') {
|
|
||||||
if (pos === step) {
|
|
||||||
throw new RangeError('Cyclic object value');
|
|
||||||
} else {
|
|
||||||
findFlag = true; // Break while
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (typeof tmpSc.get(sentinel) === 'undefined') {
|
|
||||||
step = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof filter === 'function') {
|
|
||||||
obj = filter(prefix, obj);
|
|
||||||
} else if (obj instanceof Date) {
|
|
||||||
obj = serializeDate(obj);
|
|
||||||
} else if (generateArrayPrefix === 'comma' && isArray(obj)) {
|
|
||||||
obj = utils.maybeMap(obj, function (value) {
|
|
||||||
if (value instanceof Date) {
|
|
||||||
return serializeDate(value);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj === null) {
|
|
||||||
if (strictNullHandling) {
|
|
||||||
return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key', format) : prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) {
|
|
||||||
if (encoder) {
|
|
||||||
var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key', format);
|
|
||||||
return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value', format))];
|
|
||||||
}
|
|
||||||
return [formatter(prefix) + '=' + formatter(String(obj))];
|
|
||||||
}
|
|
||||||
|
|
||||||
var values = [];
|
|
||||||
|
|
||||||
if (typeof obj === 'undefined') {
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
var objKeys;
|
|
||||||
if (generateArrayPrefix === 'comma' && isArray(obj)) {
|
|
||||||
// we need to join elements in
|
|
||||||
if (encodeValuesOnly && encoder) {
|
|
||||||
obj = utils.maybeMap(obj, encoder);
|
|
||||||
}
|
|
||||||
objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }];
|
|
||||||
} else if (isArray(filter)) {
|
|
||||||
objKeys = filter;
|
|
||||||
} else {
|
|
||||||
var keys = Object.keys(obj);
|
|
||||||
objKeys = sort ? keys.sort(sort) : keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
var encodedPrefix = encodeDotInKeys ? String(prefix).replace(/\./g, '%2E') : String(prefix);
|
|
||||||
|
|
||||||
var adjustedPrefix = commaRoundTrip && isArray(obj) && obj.length === 1 ? encodedPrefix + '[]' : encodedPrefix;
|
|
||||||
|
|
||||||
if (allowEmptyArrays && isArray(obj) && obj.length === 0) {
|
|
||||||
return adjustedPrefix + '[]';
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var j = 0; j < objKeys.length; ++j) {
|
|
||||||
var key = objKeys[j];
|
|
||||||
var value = typeof key === 'object' && key && typeof key.value !== 'undefined'
|
|
||||||
? key.value
|
|
||||||
: obj[key];
|
|
||||||
|
|
||||||
if (skipNulls && value === null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var encodedKey = allowDots && encodeDotInKeys ? String(key).replace(/\./g, '%2E') : String(key);
|
|
||||||
var keyPrefix = isArray(obj)
|
|
||||||
? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(adjustedPrefix, encodedKey) : adjustedPrefix
|
|
||||||
: adjustedPrefix + (allowDots ? '.' + encodedKey : '[' + encodedKey + ']');
|
|
||||||
|
|
||||||
sideChannel.set(object, step);
|
|
||||||
var valueSideChannel = getSideChannel();
|
|
||||||
valueSideChannel.set(sentinel, sideChannel);
|
|
||||||
pushToArray(values, stringify(
|
|
||||||
value,
|
|
||||||
keyPrefix,
|
|
||||||
generateArrayPrefix,
|
|
||||||
commaRoundTrip,
|
|
||||||
allowEmptyArrays,
|
|
||||||
strictNullHandling,
|
|
||||||
skipNulls,
|
|
||||||
encodeDotInKeys,
|
|
||||||
generateArrayPrefix === 'comma' && encodeValuesOnly && isArray(obj) ? null : encoder,
|
|
||||||
filter,
|
|
||||||
sort,
|
|
||||||
allowDots,
|
|
||||||
serializeDate,
|
|
||||||
format,
|
|
||||||
formatter,
|
|
||||||
encodeValuesOnly,
|
|
||||||
charset,
|
|
||||||
valueSideChannel
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
};
|
|
||||||
|
|
||||||
var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
|
|
||||||
if (!opts) {
|
|
||||||
return defaults;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof opts.allowEmptyArrays !== 'undefined' && typeof opts.allowEmptyArrays !== 'boolean') {
|
|
||||||
throw new TypeError('`allowEmptyArrays` option can only be `true` or `false`, when provided');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof opts.encodeDotInKeys !== 'undefined' && typeof opts.encodeDotInKeys !== 'boolean') {
|
|
||||||
throw new TypeError('`encodeDotInKeys` option can only be `true` or `false`, when provided');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.encoder !== null && typeof opts.encoder !== 'undefined' && typeof opts.encoder !== 'function') {
|
|
||||||
throw new TypeError('Encoder has to be a function.');
|
|
||||||
}
|
|
||||||
|
|
||||||
var charset = opts.charset || defaults.charset;
|
|
||||||
if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {
|
|
||||||
throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined');
|
|
||||||
}
|
|
||||||
|
|
||||||
var format = formats['default'];
|
|
||||||
if (typeof opts.format !== 'undefined') {
|
|
||||||
if (!has.call(formats.formatters, opts.format)) {
|
|
||||||
throw new TypeError('Unknown format option provided.');
|
|
||||||
}
|
|
||||||
format = opts.format;
|
|
||||||
}
|
|
||||||
var formatter = formats.formatters[format];
|
|
||||||
|
|
||||||
var filter = defaults.filter;
|
|
||||||
if (typeof opts.filter === 'function' || isArray(opts.filter)) {
|
|
||||||
filter = opts.filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
var arrayFormat;
|
|
||||||
if (opts.arrayFormat in arrayPrefixGenerators) {
|
|
||||||
arrayFormat = opts.arrayFormat;
|
|
||||||
} else if ('indices' in opts) {
|
|
||||||
arrayFormat = opts.indices ? 'indices' : 'repeat';
|
|
||||||
} else {
|
|
||||||
arrayFormat = defaults.arrayFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('commaRoundTrip' in opts && typeof opts.commaRoundTrip !== 'boolean') {
|
|
||||||
throw new TypeError('`commaRoundTrip` must be a boolean, or absent');
|
|
||||||
}
|
|
||||||
|
|
||||||
var allowDots = typeof opts.allowDots === 'undefined' ? opts.encodeDotInKeys === true ? true : defaults.allowDots : !!opts.allowDots;
|
|
||||||
|
|
||||||
return {
|
|
||||||
addQueryPrefix: typeof opts.addQueryPrefix === 'boolean' ? opts.addQueryPrefix : defaults.addQueryPrefix,
|
|
||||||
allowDots: allowDots,
|
|
||||||
allowEmptyArrays: typeof opts.allowEmptyArrays === 'boolean' ? !!opts.allowEmptyArrays : defaults.allowEmptyArrays,
|
|
||||||
arrayFormat: arrayFormat,
|
|
||||||
charset: charset,
|
|
||||||
charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
|
|
||||||
commaRoundTrip: !!opts.commaRoundTrip,
|
|
||||||
delimiter: typeof opts.delimiter === 'undefined' ? defaults.delimiter : opts.delimiter,
|
|
||||||
encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode,
|
|
||||||
encodeDotInKeys: typeof opts.encodeDotInKeys === 'boolean' ? opts.encodeDotInKeys : defaults.encodeDotInKeys,
|
|
||||||
encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder,
|
|
||||||
encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly,
|
|
||||||
filter: filter,
|
|
||||||
format: format,
|
|
||||||
formatter: formatter,
|
|
||||||
serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate,
|
|
||||||
skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls,
|
|
||||||
sort: typeof opts.sort === 'function' ? opts.sort : null,
|
|
||||||
strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = function (object, opts) {
|
|
||||||
var obj = object;
|
|
||||||
var options = normalizeStringifyOptions(opts);
|
|
||||||
|
|
||||||
var objKeys;
|
|
||||||
var filter;
|
|
||||||
|
|
||||||
if (typeof options.filter === 'function') {
|
|
||||||
filter = options.filter;
|
|
||||||
obj = filter('', obj);
|
|
||||||
} else if (isArray(options.filter)) {
|
|
||||||
filter = options.filter;
|
|
||||||
objKeys = filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
var keys = [];
|
|
||||||
|
|
||||||
if (typeof obj !== 'object' || obj === null) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
var generateArrayPrefix = arrayPrefixGenerators[options.arrayFormat];
|
|
||||||
var commaRoundTrip = generateArrayPrefix === 'comma' && options.commaRoundTrip;
|
|
||||||
|
|
||||||
if (!objKeys) {
|
|
||||||
objKeys = Object.keys(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.sort) {
|
|
||||||
objKeys.sort(options.sort);
|
|
||||||
}
|
|
||||||
|
|
||||||
var sideChannel = getSideChannel();
|
|
||||||
for (var i = 0; i < objKeys.length; ++i) {
|
|
||||||
var key = objKeys[i];
|
|
||||||
var value = obj[key];
|
|
||||||
|
|
||||||
if (options.skipNulls && value === null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
pushToArray(keys, stringify(
|
|
||||||
value,
|
|
||||||
key,
|
|
||||||
generateArrayPrefix,
|
|
||||||
commaRoundTrip,
|
|
||||||
options.allowEmptyArrays,
|
|
||||||
options.strictNullHandling,
|
|
||||||
options.skipNulls,
|
|
||||||
options.encodeDotInKeys,
|
|
||||||
options.encode ? options.encoder : null,
|
|
||||||
options.filter,
|
|
||||||
options.sort,
|
|
||||||
options.allowDots,
|
|
||||||
options.serializeDate,
|
|
||||||
options.format,
|
|
||||||
options.formatter,
|
|
||||||
options.encodeValuesOnly,
|
|
||||||
options.charset,
|
|
||||||
sideChannel
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
var joined = keys.join(options.delimiter);
|
|
||||||
var prefix = options.addQueryPrefix === true ? '?' : '';
|
|
||||||
|
|
||||||
if (options.charsetSentinel) {
|
|
||||||
if (options.charset === 'iso-8859-1') {
|
|
||||||
// encodeURIComponent('✓'), the "numeric entity" representation of a checkmark
|
|
||||||
prefix += 'utf8=%26%2310003%3B&';
|
|
||||||
} else {
|
|
||||||
// encodeURIComponent('✓')
|
|
||||||
prefix += 'utf8=%E2%9C%93&';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return joined.length > 0 ? prefix + joined : '';
|
|
||||||
};
|
|
||||||
-342
@@ -1,342 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var formats = require('./formats');
|
|
||||||
var getSideChannel = require('side-channel');
|
|
||||||
|
|
||||||
var has = Object.prototype.hasOwnProperty;
|
|
||||||
var isArray = Array.isArray;
|
|
||||||
|
|
||||||
// Track objects created from arrayLimit overflow using side-channel
|
|
||||||
// Stores the current max numeric index for O(1) lookup
|
|
||||||
var overflowChannel = getSideChannel();
|
|
||||||
|
|
||||||
var markOverflow = function markOverflow(obj, maxIndex) {
|
|
||||||
overflowChannel.set(obj, maxIndex);
|
|
||||||
return obj;
|
|
||||||
};
|
|
||||||
|
|
||||||
var isOverflow = function isOverflow(obj) {
|
|
||||||
return overflowChannel.has(obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
var getMaxIndex = function getMaxIndex(obj) {
|
|
||||||
return overflowChannel.get(obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
var setMaxIndex = function setMaxIndex(obj, maxIndex) {
|
|
||||||
overflowChannel.set(obj, maxIndex);
|
|
||||||
};
|
|
||||||
|
|
||||||
var hexTable = (function () {
|
|
||||||
var array = [];
|
|
||||||
for (var i = 0; i < 256; ++i) {
|
|
||||||
array[array.length] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
return array;
|
|
||||||
}());
|
|
||||||
|
|
||||||
var compactQueue = function compactQueue(queue) {
|
|
||||||
while (queue.length > 1) {
|
|
||||||
var item = queue.pop();
|
|
||||||
var obj = item.obj[item.prop];
|
|
||||||
|
|
||||||
if (isArray(obj)) {
|
|
||||||
var compacted = [];
|
|
||||||
|
|
||||||
for (var j = 0; j < obj.length; ++j) {
|
|
||||||
if (typeof obj[j] !== 'undefined') {
|
|
||||||
compacted[compacted.length] = obj[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
item.obj[item.prop] = compacted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var arrayToObject = function arrayToObject(source, options) {
|
|
||||||
var obj = options && options.plainObjects ? { __proto__: null } : {};
|
|
||||||
for (var i = 0; i < source.length; ++i) {
|
|
||||||
if (typeof source[i] !== 'undefined') {
|
|
||||||
obj[i] = source[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
};
|
|
||||||
|
|
||||||
var merge = function merge(target, source, options) {
|
|
||||||
/* eslint no-param-reassign: 0 */
|
|
||||||
if (!source) {
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof source !== 'object' && typeof source !== 'function') {
|
|
||||||
if (isArray(target)) {
|
|
||||||
var nextIndex = target.length;
|
|
||||||
if (options && typeof options.arrayLimit === 'number' && nextIndex > options.arrayLimit) {
|
|
||||||
return markOverflow(arrayToObject(target.concat(source), options), nextIndex);
|
|
||||||
}
|
|
||||||
target[nextIndex] = source;
|
|
||||||
} else if (target && typeof target === 'object') {
|
|
||||||
if (isOverflow(target)) {
|
|
||||||
// Add at next numeric index for overflow objects
|
|
||||||
var newIndex = getMaxIndex(target) + 1;
|
|
||||||
target[newIndex] = source;
|
|
||||||
setMaxIndex(target, newIndex);
|
|
||||||
} else if (options && options.strictMerge) {
|
|
||||||
return [target, source];
|
|
||||||
} else if (
|
|
||||||
(options && (options.plainObjects || options.allowPrototypes))
|
|
||||||
|| !has.call(Object.prototype, source)
|
|
||||||
) {
|
|
||||||
target[source] = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return [target, source];
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!target || typeof target !== 'object') {
|
|
||||||
if (isOverflow(source)) {
|
|
||||||
// Create new object with target at 0, source values shifted by 1
|
|
||||||
var sourceKeys = Object.keys(source);
|
|
||||||
var result = options && options.plainObjects
|
|
||||||
? { __proto__: null, 0: target }
|
|
||||||
: { 0: target };
|
|
||||||
for (var m = 0; m < sourceKeys.length; m++) {
|
|
||||||
var oldKey = parseInt(sourceKeys[m], 10);
|
|
||||||
result[oldKey + 1] = source[sourceKeys[m]];
|
|
||||||
}
|
|
||||||
return markOverflow(result, getMaxIndex(source) + 1);
|
|
||||||
}
|
|
||||||
var combined = [target].concat(source);
|
|
||||||
if (options && typeof options.arrayLimit === 'number' && combined.length > options.arrayLimit) {
|
|
||||||
return markOverflow(arrayToObject(combined, options), combined.length - 1);
|
|
||||||
}
|
|
||||||
return combined;
|
|
||||||
}
|
|
||||||
|
|
||||||
var mergeTarget = target;
|
|
||||||
if (isArray(target) && !isArray(source)) {
|
|
||||||
mergeTarget = arrayToObject(target, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isArray(target) && isArray(source)) {
|
|
||||||
source.forEach(function (item, i) {
|
|
||||||
if (has.call(target, i)) {
|
|
||||||
var targetItem = target[i];
|
|
||||||
if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') {
|
|
||||||
target[i] = merge(targetItem, item, options);
|
|
||||||
} else {
|
|
||||||
target[target.length] = item;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
target[i] = item;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.keys(source).reduce(function (acc, key) {
|
|
||||||
var value = source[key];
|
|
||||||
|
|
||||||
if (has.call(acc, key)) {
|
|
||||||
acc[key] = merge(acc[key], value, options);
|
|
||||||
} else {
|
|
||||||
acc[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isOverflow(source) && !isOverflow(acc)) {
|
|
||||||
markOverflow(acc, getMaxIndex(source));
|
|
||||||
}
|
|
||||||
if (isOverflow(acc)) {
|
|
||||||
var keyNum = parseInt(key, 10);
|
|
||||||
if (String(keyNum) === key && keyNum >= 0 && keyNum > getMaxIndex(acc)) {
|
|
||||||
setMaxIndex(acc, keyNum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, mergeTarget);
|
|
||||||
};
|
|
||||||
|
|
||||||
var assign = function assignSingleSource(target, source) {
|
|
||||||
return Object.keys(source).reduce(function (acc, key) {
|
|
||||||
acc[key] = source[key];
|
|
||||||
return acc;
|
|
||||||
}, target);
|
|
||||||
};
|
|
||||||
|
|
||||||
var decode = function (str, defaultDecoder, charset) {
|
|
||||||
var strWithoutPlus = str.replace(/\+/g, ' ');
|
|
||||||
if (charset === 'iso-8859-1') {
|
|
||||||
// unescape never throws, no try...catch needed:
|
|
||||||
return strWithoutPlus.replace(/%[0-9a-f]{2}/gi, unescape);
|
|
||||||
}
|
|
||||||
// utf-8
|
|
||||||
try {
|
|
||||||
return decodeURIComponent(strWithoutPlus);
|
|
||||||
} catch (e) {
|
|
||||||
return strWithoutPlus;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var limit = 1024;
|
|
||||||
|
|
||||||
/* eslint operator-linebreak: [2, "before"] */
|
|
||||||
|
|
||||||
var encode = function encode(str, defaultEncoder, charset, kind, format) {
|
|
||||||
// This code was originally written by Brian White (mscdex) for the io.js core querystring library.
|
|
||||||
// It has been adapted here for stricter adherence to RFC 3986
|
|
||||||
if (str.length === 0) {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
var string = str;
|
|
||||||
if (typeof str === 'symbol') {
|
|
||||||
string = Symbol.prototype.toString.call(str);
|
|
||||||
} else if (typeof str !== 'string') {
|
|
||||||
string = String(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (charset === 'iso-8859-1') {
|
|
||||||
return escape(string).replace(/%u[0-9a-f]{4}/gi, function ($0) {
|
|
||||||
return '%26%23' + parseInt($0.slice(2), 16) + '%3B';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var out = '';
|
|
||||||
for (var j = 0; j < string.length; j += limit) {
|
|
||||||
var segment = string.length >= limit ? string.slice(j, j + limit) : string;
|
|
||||||
var arr = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < segment.length; ++i) {
|
|
||||||
var c = segment.charCodeAt(i);
|
|
||||||
if (
|
|
||||||
c === 0x2D // -
|
|
||||||
|| c === 0x2E // .
|
|
||||||
|| c === 0x5F // _
|
|
||||||
|| c === 0x7E // ~
|
|
||||||
|| (c >= 0x30 && c <= 0x39) // 0-9
|
|
||||||
|| (c >= 0x41 && c <= 0x5A) // a-z
|
|
||||||
|| (c >= 0x61 && c <= 0x7A) // A-Z
|
|
||||||
|| (format === formats.RFC1738 && (c === 0x28 || c === 0x29)) // ( )
|
|
||||||
) {
|
|
||||||
arr[arr.length] = segment.charAt(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c < 0x80) {
|
|
||||||
arr[arr.length] = hexTable[c];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c < 0x800) {
|
|
||||||
arr[arr.length] = hexTable[0xC0 | (c >> 6)]
|
|
||||||
+ hexTable[0x80 | (c & 0x3F)];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c < 0xD800 || c >= 0xE000) {
|
|
||||||
arr[arr.length] = hexTable[0xE0 | (c >> 12)]
|
|
||||||
+ hexTable[0x80 | ((c >> 6) & 0x3F)]
|
|
||||||
+ hexTable[0x80 | (c & 0x3F)];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
i += 1;
|
|
||||||
c = 0x10000 + (((c & 0x3FF) << 10) | (segment.charCodeAt(i) & 0x3FF));
|
|
||||||
|
|
||||||
arr[arr.length] = hexTable[0xF0 | (c >> 18)]
|
|
||||||
+ hexTable[0x80 | ((c >> 12) & 0x3F)]
|
|
||||||
+ hexTable[0x80 | ((c >> 6) & 0x3F)]
|
|
||||||
+ hexTable[0x80 | (c & 0x3F)];
|
|
||||||
}
|
|
||||||
|
|
||||||
out += arr.join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
};
|
|
||||||
|
|
||||||
var compact = function compact(value) {
|
|
||||||
var queue = [{ obj: { o: value }, prop: 'o' }];
|
|
||||||
var refs = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < queue.length; ++i) {
|
|
||||||
var item = queue[i];
|
|
||||||
var obj = item.obj[item.prop];
|
|
||||||
|
|
||||||
var keys = Object.keys(obj);
|
|
||||||
for (var j = 0; j < keys.length; ++j) {
|
|
||||||
var key = keys[j];
|
|
||||||
var val = obj[key];
|
|
||||||
if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) {
|
|
||||||
queue[queue.length] = { obj: obj, prop: key };
|
|
||||||
refs[refs.length] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compactQueue(queue);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
var isRegExp = function isRegExp(obj) {
|
|
||||||
return Object.prototype.toString.call(obj) === '[object RegExp]';
|
|
||||||
};
|
|
||||||
|
|
||||||
var isBuffer = function isBuffer(obj) {
|
|
||||||
if (!obj || typeof obj !== 'object') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj));
|
|
||||||
};
|
|
||||||
|
|
||||||
var combine = function combine(a, b, arrayLimit, plainObjects) {
|
|
||||||
// If 'a' is already an overflow object, add to it
|
|
||||||
if (isOverflow(a)) {
|
|
||||||
var newIndex = getMaxIndex(a) + 1;
|
|
||||||
a[newIndex] = b;
|
|
||||||
setMaxIndex(a, newIndex);
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = [].concat(a, b);
|
|
||||||
if (result.length > arrayLimit) {
|
|
||||||
return markOverflow(arrayToObject(result, { plainObjects: plainObjects }), result.length - 1);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
var maybeMap = function maybeMap(val, fn) {
|
|
||||||
if (isArray(val)) {
|
|
||||||
var mapped = [];
|
|
||||||
for (var i = 0; i < val.length; i += 1) {
|
|
||||||
mapped[mapped.length] = fn(val[i]);
|
|
||||||
}
|
|
||||||
return mapped;
|
|
||||||
}
|
|
||||||
return fn(val);
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
arrayToObject: arrayToObject,
|
|
||||||
assign: assign,
|
|
||||||
combine: combine,
|
|
||||||
compact: compact,
|
|
||||||
decode: decode,
|
|
||||||
encode: encode,
|
|
||||||
isBuffer: isBuffer,
|
|
||||||
isOverflow: isOverflow,
|
|
||||||
isRegExp: isRegExp,
|
|
||||||
markOverflow: markOverflow,
|
|
||||||
maybeMap: maybeMap,
|
|
||||||
merge: merge
|
|
||||||
};
|
|
||||||
-94
@@ -1,94 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "qs",
|
|
||||||
"description": "A querystring parser that supports nesting and arrays, with a depth limit",
|
|
||||||
"homepage": "https://github.com/ljharb/qs",
|
|
||||||
"version": "6.15.1",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/ljharb/qs.git"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
},
|
|
||||||
"main": "lib/index.js",
|
|
||||||
"sideEffects": false,
|
|
||||||
"contributors": [
|
|
||||||
{
|
|
||||||
"name": "Jordan Harband",
|
|
||||||
"email": "ljharb@gmail.com",
|
|
||||||
"url": "http://ljharb.codes"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"keywords": [
|
|
||||||
"querystring",
|
|
||||||
"qs",
|
|
||||||
"query",
|
|
||||||
"url",
|
|
||||||
"parse",
|
|
||||||
"stringify"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"side-channel": "^1.1.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@browserify/envify": "^6.0.0",
|
|
||||||
"@browserify/uglifyify": "^6.0.0",
|
|
||||||
"@ljharb/eslint-config": "^22.2.2",
|
|
||||||
"browserify": "^16.5.2",
|
|
||||||
"bundle-collapser": "^1.4.0",
|
|
||||||
"common-shakeify": "~1.0.0",
|
|
||||||
"eclint": "^2.8.1",
|
|
||||||
"es-value-fixtures": "^1.7.1",
|
|
||||||
"eslint": "^9.39.2",
|
|
||||||
"evalmd": "^0.0.19",
|
|
||||||
"for-each": "^0.3.5",
|
|
||||||
"glob": "=10.3.7",
|
|
||||||
"has-bigints": "^1.1.0",
|
|
||||||
"has-override-mistake": "^1.0.1",
|
|
||||||
"has-property-descriptors": "^1.0.2",
|
|
||||||
"has-proto": "^1.2.0",
|
|
||||||
"has-symbols": "^1.1.0",
|
|
||||||
"iconv-lite": "^0.5.2",
|
|
||||||
"in-publish": "^2.0.1",
|
|
||||||
"jackspeak": "=2.1.1",
|
|
||||||
"jiti": "^0.0.0",
|
|
||||||
"mkdirp": "^0.5.5",
|
|
||||||
"mock-property": "^1.1.0",
|
|
||||||
"module-deps": "^6.2.3",
|
|
||||||
"npmignore": "^0.3.5",
|
|
||||||
"nyc": "^10.3.2",
|
|
||||||
"object-inspect": "^1.13.4",
|
|
||||||
"qs-iconv": "^1.0.4",
|
|
||||||
"safe-publish-latest": "^2.0.0",
|
|
||||||
"safer-buffer": "^2.1.2",
|
|
||||||
"tape": "^5.9.0",
|
|
||||||
"unassertify": "^3.0.1"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"prepack": "npmignore --auto --commentLines=autogenerated && npm run dist",
|
|
||||||
"prepublishOnly": "safe-publish-latest",
|
|
||||||
"prepublish": "not-in-publish || npm run prepublishOnly",
|
|
||||||
"pretest": "npm run --silent readme && npm run --silent lint",
|
|
||||||
"test": "npm run tests-only",
|
|
||||||
"tests-only": "nyc tape 'test/**/*.js'",
|
|
||||||
"posttest": "npx npm@'>=10.2' audit --production",
|
|
||||||
"readme": "evalmd README.md",
|
|
||||||
"postlint": "eclint check $(git ls-files | xargs find 2> /dev/null | grep -vE 'node_modules|\\.git' | grep -v dist/)",
|
|
||||||
"lint": "eslint .",
|
|
||||||
"dist": "mkdirp dist && browserify --standalone Qs -g unassertify -g @browserify/envify -g [@browserify/uglifyify --mangle.keep_fnames --compress.keep_fnames --format.indent_level=1 --compress.arrows=false --compress.passes=4 --compress.typeofs=false] -p common-shakeify -p bundle-collapser/plugin lib/index.js > dist/qs.js"
|
|
||||||
},
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"publishConfig": {
|
|
||||||
"ignore": [
|
|
||||||
"!dist/*",
|
|
||||||
"bower.json",
|
|
||||||
"component.json",
|
|
||||||
".github/workflows",
|
|
||||||
"logos",
|
|
||||||
"tea.yaml"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-267
@@ -1,267 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
emptyTestCases: [
|
|
||||||
{
|
|
||||||
input: '&',
|
|
||||||
withEmptyKeys: {},
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '',
|
|
||||||
indices: '',
|
|
||||||
repeat: ''
|
|
||||||
},
|
|
||||||
noEmptyKeys: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '&&',
|
|
||||||
withEmptyKeys: {},
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '',
|
|
||||||
indices: '',
|
|
||||||
repeat: ''
|
|
||||||
},
|
|
||||||
noEmptyKeys: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '&=',
|
|
||||||
withEmptyKeys: { '': '' },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=',
|
|
||||||
indices: '=',
|
|
||||||
repeat: '='
|
|
||||||
},
|
|
||||||
noEmptyKeys: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '&=&',
|
|
||||||
withEmptyKeys: { '': '' },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=',
|
|
||||||
indices: '=',
|
|
||||||
repeat: '='
|
|
||||||
},
|
|
||||||
noEmptyKeys: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '&=&=',
|
|
||||||
withEmptyKeys: { '': ['', ''] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '[]=&[]=',
|
|
||||||
indices: '[0]=&[1]=',
|
|
||||||
repeat: '=&='
|
|
||||||
},
|
|
||||||
noEmptyKeys: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '&=&=&',
|
|
||||||
withEmptyKeys: { '': ['', ''] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '[]=&[]=',
|
|
||||||
indices: '[0]=&[1]=',
|
|
||||||
repeat: '=&='
|
|
||||||
},
|
|
||||||
noEmptyKeys: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '=',
|
|
||||||
withEmptyKeys: { '': '' },
|
|
||||||
noEmptyKeys: {},
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=',
|
|
||||||
indices: '=',
|
|
||||||
repeat: '='
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '=&',
|
|
||||||
withEmptyKeys: { '': '' },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=',
|
|
||||||
indices: '=',
|
|
||||||
repeat: '='
|
|
||||||
},
|
|
||||||
noEmptyKeys: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '=&&&',
|
|
||||||
withEmptyKeys: { '': '' },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=',
|
|
||||||
indices: '=',
|
|
||||||
repeat: '='
|
|
||||||
},
|
|
||||||
noEmptyKeys: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '=&=&=&',
|
|
||||||
withEmptyKeys: { '': ['', '', ''] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '[]=&[]=&[]=',
|
|
||||||
indices: '[0]=&[1]=&[2]=',
|
|
||||||
repeat: '=&=&='
|
|
||||||
},
|
|
||||||
noEmptyKeys: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '=&a[]=b&a[1]=c',
|
|
||||||
withEmptyKeys: { '': '', a: ['b', 'c'] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=&a[]=b&a[]=c',
|
|
||||||
indices: '=&a[0]=b&a[1]=c',
|
|
||||||
repeat: '=&a=b&a=c'
|
|
||||||
},
|
|
||||||
noEmptyKeys: { a: ['b', 'c'] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '=a',
|
|
||||||
withEmptyKeys: { '': 'a' },
|
|
||||||
noEmptyKeys: {},
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=a',
|
|
||||||
indices: '=a',
|
|
||||||
repeat: '=a'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'a==a',
|
|
||||||
withEmptyKeys: { a: '=a' },
|
|
||||||
noEmptyKeys: { a: '=a' },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: 'a==a',
|
|
||||||
indices: 'a==a',
|
|
||||||
repeat: 'a==a'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '=&a[]=b',
|
|
||||||
withEmptyKeys: { '': '', a: ['b'] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=&a[]=b',
|
|
||||||
indices: '=&a[0]=b',
|
|
||||||
repeat: '=&a=b'
|
|
||||||
},
|
|
||||||
noEmptyKeys: { a: ['b'] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '=&a[]=b&a[]=c&a[2]=d',
|
|
||||||
withEmptyKeys: { '': '', a: ['b', 'c', 'd'] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=&a[]=b&a[]=c&a[]=d',
|
|
||||||
indices: '=&a[0]=b&a[1]=c&a[2]=d',
|
|
||||||
repeat: '=&a=b&a=c&a=d'
|
|
||||||
},
|
|
||||||
noEmptyKeys: { a: ['b', 'c', 'd'] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '=a&=b',
|
|
||||||
withEmptyKeys: { '': ['a', 'b'] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '[]=a&[]=b',
|
|
||||||
indices: '[0]=a&[1]=b',
|
|
||||||
repeat: '=a&=b'
|
|
||||||
},
|
|
||||||
noEmptyKeys: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '=a&foo=b',
|
|
||||||
withEmptyKeys: { '': 'a', foo: 'b' },
|
|
||||||
noEmptyKeys: { foo: 'b' },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=a&foo=b',
|
|
||||||
indices: '=a&foo=b',
|
|
||||||
repeat: '=a&foo=b'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'a[]=b&a=c&=',
|
|
||||||
withEmptyKeys: { '': '', a: ['b', 'c'] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=&a[]=b&a[]=c',
|
|
||||||
indices: '=&a[0]=b&a[1]=c',
|
|
||||||
repeat: '=&a=b&a=c'
|
|
||||||
},
|
|
||||||
noEmptyKeys: { a: ['b', 'c'] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'a[]=b&a=c&=',
|
|
||||||
withEmptyKeys: { '': '', a: ['b', 'c'] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=&a[]=b&a[]=c',
|
|
||||||
indices: '=&a[0]=b&a[1]=c',
|
|
||||||
repeat: '=&a=b&a=c'
|
|
||||||
},
|
|
||||||
noEmptyKeys: { a: ['b', 'c'] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'a[0]=b&a=c&=',
|
|
||||||
withEmptyKeys: { '': '', a: ['b', 'c'] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=&a[]=b&a[]=c',
|
|
||||||
indices: '=&a[0]=b&a[1]=c',
|
|
||||||
repeat: '=&a=b&a=c'
|
|
||||||
},
|
|
||||||
noEmptyKeys: { a: ['b', 'c'] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'a=b&a[]=c&=',
|
|
||||||
withEmptyKeys: { '': '', a: ['b', 'c'] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=&a[]=b&a[]=c',
|
|
||||||
indices: '=&a[0]=b&a[1]=c',
|
|
||||||
repeat: '=&a=b&a=c'
|
|
||||||
},
|
|
||||||
noEmptyKeys: { a: ['b', 'c'] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'a=b&a[0]=c&=',
|
|
||||||
withEmptyKeys: { '': '', a: ['b', 'c'] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '=&a[]=b&a[]=c',
|
|
||||||
indices: '=&a[0]=b&a[1]=c',
|
|
||||||
repeat: '=&a=b&a=c'
|
|
||||||
},
|
|
||||||
noEmptyKeys: { a: ['b', 'c'] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '[]=a&[]=b& []=1',
|
|
||||||
withEmptyKeys: { '': ['a', 'b'], ' ': ['1'] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '[]=a&[]=b& []=1',
|
|
||||||
indices: '[0]=a&[1]=b& [0]=1',
|
|
||||||
repeat: '=a&=b& =1'
|
|
||||||
},
|
|
||||||
noEmptyKeys: { 0: 'a', 1: 'b', ' ': ['1'] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '[0]=a&[1]=b&a[0]=1&a[1]=2',
|
|
||||||
withEmptyKeys: { '': ['a', 'b'], a: ['1', '2'] },
|
|
||||||
noEmptyKeys: { 0: 'a', 1: 'b', a: ['1', '2'] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '[]=a&[]=b&a[]=1&a[]=2',
|
|
||||||
indices: '[0]=a&[1]=b&a[0]=1&a[1]=2',
|
|
||||||
repeat: '=a&=b&a=1&a=2'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '[deep]=a&[deep]=2',
|
|
||||||
withEmptyKeys: { '': { deep: ['a', '2'] }
|
|
||||||
},
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '[deep][]=a&[deep][]=2',
|
|
||||||
indices: '[deep][0]=a&[deep][1]=2',
|
|
||||||
repeat: '[deep]=a&[deep]=2'
|
|
||||||
},
|
|
||||||
noEmptyKeys: { deep: ['a', '2'] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: '%5B0%5D=a&%5B1%5D=b',
|
|
||||||
withEmptyKeys: { '': ['a', 'b'] },
|
|
||||||
stringifyOutput: {
|
|
||||||
brackets: '[]=a&[]=b',
|
|
||||||
indices: '[0]=a&[1]=b',
|
|
||||||
repeat: '=a&=b'
|
|
||||||
},
|
|
||||||
noEmptyKeys: { 0: 'a', 1: 'b' }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
-1654
File diff suppressed because it is too large
Load Diff
-1319
File diff suppressed because it is too large
Load Diff
-432
@@ -1,432 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var test = require('tape');
|
|
||||||
var inspect = require('object-inspect');
|
|
||||||
var SaferBuffer = require('safer-buffer').Buffer;
|
|
||||||
var forEach = require('for-each');
|
|
||||||
var v = require('es-value-fixtures');
|
|
||||||
|
|
||||||
var utils = require('../lib/utils');
|
|
||||||
|
|
||||||
test('merge()', function (t) {
|
|
||||||
t.deepEqual(utils.merge(null, true), [null, true], 'merges true into null');
|
|
||||||
|
|
||||||
t.deepEqual(utils.merge(null, [42]), [null, 42], 'merges null into an array');
|
|
||||||
|
|
||||||
t.deepEqual(utils.merge({ a: 'b' }, { a: 'c' }), { a: ['b', 'c'] }, 'merges two objects with the same key');
|
|
||||||
|
|
||||||
var oneMerged = utils.merge({ foo: 'bar' }, { foo: { first: '123' } });
|
|
||||||
t.deepEqual(oneMerged, { foo: ['bar', { first: '123' }] }, 'merges a standalone and an object into an array');
|
|
||||||
|
|
||||||
var twoMerged = utils.merge({ foo: ['bar', { first: '123' }] }, { foo: { second: '456' } });
|
|
||||||
t.deepEqual(twoMerged, { foo: { 0: 'bar', 1: { first: '123' }, second: '456' } }, 'merges a standalone and two objects into an array');
|
|
||||||
|
|
||||||
var sandwiched = utils.merge({ foo: ['bar', { first: '123', second: '456' }] }, { foo: 'baz' });
|
|
||||||
t.deepEqual(sandwiched, { foo: ['bar', { first: '123', second: '456' }, 'baz'] }, 'merges an object sandwiched by two standalones into an array');
|
|
||||||
|
|
||||||
var nestedArrays = utils.merge({ foo: ['baz'] }, { foo: ['bar', 'xyzzy'] });
|
|
||||||
t.deepEqual(nestedArrays, { foo: ['baz', 'bar', 'xyzzy'] });
|
|
||||||
|
|
||||||
var noOptionsNonObjectSource = utils.merge({ foo: 'baz' }, 'bar');
|
|
||||||
t.deepEqual(noOptionsNonObjectSource, { foo: 'baz', bar: true });
|
|
||||||
|
|
||||||
var func = function f() {};
|
|
||||||
func();
|
|
||||||
t.deepEqual(
|
|
||||||
utils.merge(func, { foo: 'bar' }),
|
|
||||||
[func, { foo: 'bar' }],
|
|
||||||
'functions can not be merged into'
|
|
||||||
);
|
|
||||||
|
|
||||||
func.bar = 'baz';
|
|
||||||
t.deepEqual(
|
|
||||||
utils.merge({ foo: 'bar' }, func),
|
|
||||||
{ foo: 'bar', bar: 'baz' },
|
|
||||||
'functions can be merge sources'
|
|
||||||
);
|
|
||||||
|
|
||||||
t.test(
|
|
||||||
'avoids invoking array setters unnecessarily',
|
|
||||||
{ skip: typeof Object.defineProperty !== 'function' },
|
|
||||||
function (st) {
|
|
||||||
var setCount = 0;
|
|
||||||
var getCount = 0;
|
|
||||||
var observed = [];
|
|
||||||
Object.defineProperty(observed, 0, {
|
|
||||||
get: function () {
|
|
||||||
getCount += 1;
|
|
||||||
return { bar: 'baz' };
|
|
||||||
},
|
|
||||||
set: function () { setCount += 1; }
|
|
||||||
});
|
|
||||||
utils.merge(observed, [null]);
|
|
||||||
st.equal(setCount, 0);
|
|
||||||
st.equal(getCount, 1);
|
|
||||||
observed[0] = observed[0]; // eslint-disable-line no-self-assign
|
|
||||||
st.equal(setCount, 1);
|
|
||||||
st.equal(getCount, 2);
|
|
||||||
st.end();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
t.test('with overflow objects (from arrayLimit)', function (st) {
|
|
||||||
// arrayLimit is max index, so with limit 0, max index 0 is allowed (1 element)
|
|
||||||
// To create overflow, need 2+ elements with limit 0, or 3+ with limit 1, etc.
|
|
||||||
st.test('merges primitive into overflow object at next index', function (s2t) {
|
|
||||||
// Create an overflow object via combine: 3 elements (indices 0-2) with limit 0
|
|
||||||
var overflow = utils.combine(['a', 'b'], 'c', 0, false);
|
|
||||||
s2t.ok(utils.isOverflow(overflow), 'overflow object is marked');
|
|
||||||
var merged = utils.merge(overflow, 'd');
|
|
||||||
s2t.deepEqual(merged, { 0: 'a', 1: 'b', 2: 'c', 3: 'd' }, 'adds primitive at next numeric index');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('merges primitive into regular object with numeric keys normally', function (s2t) {
|
|
||||||
var obj = { 0: 'a', 1: 'b' };
|
|
||||||
s2t.notOk(utils.isOverflow(obj), 'plain object is not marked as overflow');
|
|
||||||
var merged = utils.merge(obj, 'c');
|
|
||||||
s2t.deepEqual(merged, { 0: 'a', 1: 'b', c: true }, 'adds primitive as key (not at next index)');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('merges primitive into object with non-numeric keys normally', function (s2t) {
|
|
||||||
var obj = { foo: 'bar' };
|
|
||||||
var merged = utils.merge(obj, 'baz');
|
|
||||||
s2t.deepEqual(merged, { foo: 'bar', baz: true }, 'adds primitive as key with value true');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('with strictMerge, wraps object and primitive in array', function (s2t) {
|
|
||||||
var obj = { foo: 'bar' };
|
|
||||||
var merged = utils.merge(obj, 'baz', { strictMerge: true });
|
|
||||||
s2t.deepEqual(merged, [{ foo: 'bar' }, 'baz'], 'wraps in array with strictMerge');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('merges overflow object into primitive', function (s2t) {
|
|
||||||
// Create an overflow object via combine: 2 elements (indices 0-1) with limit 0
|
|
||||||
var overflow = utils.combine(['a'], 'b', 0, false);
|
|
||||||
s2t.ok(utils.isOverflow(overflow), 'overflow object is marked');
|
|
||||||
var merged = utils.merge('c', overflow);
|
|
||||||
s2t.ok(utils.isOverflow(merged), 'result is also marked as overflow');
|
|
||||||
s2t.deepEqual(merged, { 0: 'c', 1: 'a', 2: 'b' }, 'creates object with primitive at 0, source values shifted');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('merges overflow object into primitive with plainObjects', function (s2t) {
|
|
||||||
var overflow = utils.combine(['a'], 'b', 0, false);
|
|
||||||
s2t.ok(utils.isOverflow(overflow), 'overflow object is marked');
|
|
||||||
var merged = utils.merge('c', overflow, { plainObjects: true });
|
|
||||||
s2t.ok(utils.isOverflow(merged), 'result is also marked as overflow');
|
|
||||||
s2t.deepEqual(merged, { __proto__: null, 0: 'c', 1: 'a', 2: 'b' }, 'creates null-proto object with primitive at 0');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('merges overflow object with multiple values into primitive', function (s2t) {
|
|
||||||
// Create an overflow object via combine: 3 elements (indices 0-2) with limit 0
|
|
||||||
var overflow = utils.combine(['b', 'c'], 'd', 0, false);
|
|
||||||
s2t.ok(utils.isOverflow(overflow), 'overflow object is marked');
|
|
||||||
var merged = utils.merge('a', overflow);
|
|
||||||
s2t.deepEqual(merged, { 0: 'a', 1: 'b', 2: 'c', 3: 'd' }, 'shifts all source indices by 1');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('merges regular object into primitive as array', function (s2t) {
|
|
||||||
var obj = { foo: 'bar' };
|
|
||||||
var merged = utils.merge('a', obj);
|
|
||||||
s2t.deepEqual(merged, ['a', { foo: 'bar' }], 'creates array with primitive and object');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('merges primitive into array that exceeds arrayLimit', function (s2t) {
|
|
||||||
var arr = ['a', 'b', 'c'];
|
|
||||||
var merged = utils.merge(arr, 'd', { arrayLimit: 1 });
|
|
||||||
s2t.ok(utils.isOverflow(merged), 'result is marked as overflow');
|
|
||||||
s2t.deepEqual(merged, { 0: 'a', 1: 'b', 2: 'c', 3: 'd' }, 'converts to overflow object with primitive appended');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('merges array into primitive that exceeds arrayLimit', function (s2t) {
|
|
||||||
var merged = utils.merge('a', ['b', 'c'], { arrayLimit: 1 });
|
|
||||||
s2t.ok(utils.isOverflow(merged), 'result is marked as overflow');
|
|
||||||
s2t.deepEqual(merged, { 0: 'a', 1: 'b', 2: 'c' }, 'converts to overflow object');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('assign()', function (t) {
|
|
||||||
var target = { a: 1, b: 2 };
|
|
||||||
var source = { b: 3, c: 4 };
|
|
||||||
var result = utils.assign(target, source);
|
|
||||||
|
|
||||||
t.equal(result, target, 'returns the target');
|
|
||||||
t.deepEqual(target, { a: 1, b: 3, c: 4 }, 'target and source are merged');
|
|
||||||
t.deepEqual(source, { b: 3, c: 4 }, 'source is untouched');
|
|
||||||
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('combine()', function (t) {
|
|
||||||
t.test('both arrays', function (st) {
|
|
||||||
var a = [1];
|
|
||||||
var b = [2];
|
|
||||||
var combined = utils.combine(a, b);
|
|
||||||
|
|
||||||
st.deepEqual(a, [1], 'a is not mutated');
|
|
||||||
st.deepEqual(b, [2], 'b is not mutated');
|
|
||||||
st.notEqual(a, combined, 'a !== combined');
|
|
||||||
st.notEqual(b, combined, 'b !== combined');
|
|
||||||
st.deepEqual(combined, [1, 2], 'combined is a + b');
|
|
||||||
|
|
||||||
st.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
t.test('one array, one non-array', function (st) {
|
|
||||||
var aN = 1;
|
|
||||||
var a = [aN];
|
|
||||||
var bN = 2;
|
|
||||||
var b = [bN];
|
|
||||||
|
|
||||||
var combinedAnB = utils.combine(aN, b);
|
|
||||||
st.deepEqual(b, [bN], 'b is not mutated');
|
|
||||||
st.notEqual(aN, combinedAnB, 'aN + b !== aN');
|
|
||||||
st.notEqual(a, combinedAnB, 'aN + b !== a');
|
|
||||||
st.notEqual(bN, combinedAnB, 'aN + b !== bN');
|
|
||||||
st.notEqual(b, combinedAnB, 'aN + b !== b');
|
|
||||||
st.deepEqual([1, 2], combinedAnB, 'first argument is array-wrapped when not an array');
|
|
||||||
|
|
||||||
var combinedABn = utils.combine(a, bN);
|
|
||||||
st.deepEqual(a, [aN], 'a is not mutated');
|
|
||||||
st.notEqual(aN, combinedABn, 'a + bN !== aN');
|
|
||||||
st.notEqual(a, combinedABn, 'a + bN !== a');
|
|
||||||
st.notEqual(bN, combinedABn, 'a + bN !== bN');
|
|
||||||
st.notEqual(b, combinedABn, 'a + bN !== b');
|
|
||||||
st.deepEqual([1, 2], combinedABn, 'second argument is array-wrapped when not an array');
|
|
||||||
|
|
||||||
st.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
t.test('neither is an array', function (st) {
|
|
||||||
var combined = utils.combine(1, 2);
|
|
||||||
st.notEqual(1, combined, '1 + 2 !== 1');
|
|
||||||
st.notEqual(2, combined, '1 + 2 !== 2');
|
|
||||||
st.deepEqual([1, 2], combined, 'both arguments are array-wrapped when not an array');
|
|
||||||
|
|
||||||
st.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
t.test('with arrayLimit', function (st) {
|
|
||||||
st.test('under the limit', function (s2t) {
|
|
||||||
var combined = utils.combine(['a', 'b'], 'c', 10, false);
|
|
||||||
s2t.deepEqual(combined, ['a', 'b', 'c'], 'returns array when under limit');
|
|
||||||
s2t.ok(Array.isArray(combined), 'result is an array');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('exactly at the limit stays as array', function (s2t) {
|
|
||||||
var combined = utils.combine(['a', 'b'], 'c', 3, false);
|
|
||||||
s2t.deepEqual(combined, ['a', 'b', 'c'], 'stays as array when count equals limit');
|
|
||||||
s2t.ok(Array.isArray(combined), 'result is an array');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('over the limit', function (s2t) {
|
|
||||||
var combined = utils.combine(['a', 'b', 'c'], 'd', 3, false);
|
|
||||||
s2t.deepEqual(combined, { 0: 'a', 1: 'b', 2: 'c', 3: 'd' }, 'converts to object when over limit');
|
|
||||||
s2t.notOk(Array.isArray(combined), 'result is not an array');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('with arrayLimit 1', function (s2t) {
|
|
||||||
var combined = utils.combine([], 'a', 1, false);
|
|
||||||
s2t.deepEqual(combined, ['a'], 'stays as array when count equals limit');
|
|
||||||
s2t.ok(Array.isArray(combined), 'result is an array');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('with arrayLimit 0 converts single element to object', function (s2t) {
|
|
||||||
var combined = utils.combine([], 'a', 0, false);
|
|
||||||
s2t.deepEqual(combined, { 0: 'a' }, 'converts to object when count exceeds limit');
|
|
||||||
s2t.notOk(Array.isArray(combined), 'result is not an array');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('with arrayLimit 0 and two elements converts to object', function (s2t) {
|
|
||||||
var combined = utils.combine(['a'], 'b', 0, false);
|
|
||||||
s2t.deepEqual(combined, { 0: 'a', 1: 'b' }, 'converts to object when count exceeds limit');
|
|
||||||
s2t.notOk(Array.isArray(combined), 'result is not an array');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('with plainObjects option', function (s2t) {
|
|
||||||
var combined = utils.combine(['a', 'b'], 'c', 1, true);
|
|
||||||
var expected = { __proto__: null, 0: 'a', 1: 'b', 2: 'c' };
|
|
||||||
s2t.deepEqual(combined, expected, 'converts to object with null prototype');
|
|
||||||
s2t.equal(Object.getPrototypeOf(combined), null, 'result has null prototype when plainObjects is true');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
t.test('with existing overflow object', function (st) {
|
|
||||||
st.test('adds to existing overflow object at next index', function (s2t) {
|
|
||||||
// Create overflow object first via combine: 3 elements (indices 0-2) with limit 0
|
|
||||||
var overflow = utils.combine(['a', 'b'], 'c', 0, false);
|
|
||||||
s2t.ok(utils.isOverflow(overflow), 'initial object is marked as overflow');
|
|
||||||
|
|
||||||
var combined = utils.combine(overflow, 'd', 10, false);
|
|
||||||
s2t.equal(combined, overflow, 'returns the same object (mutated)');
|
|
||||||
s2t.deepEqual(combined, { 0: 'a', 1: 'b', 2: 'c', 3: 'd' }, 'adds value at next numeric index');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.test('does not treat plain object with numeric keys as overflow', function (s2t) {
|
|
||||||
var plainObj = { 0: 'a', 1: 'b' };
|
|
||||||
s2t.notOk(utils.isOverflow(plainObj), 'plain object is not marked as overflow');
|
|
||||||
|
|
||||||
// combine treats this as a regular value, not an overflow object to append to
|
|
||||||
var combined = utils.combine(plainObj, 'c', 10, false);
|
|
||||||
s2t.deepEqual(combined, [{ 0: 'a', 1: 'b' }, 'c'], 'concatenates as regular values');
|
|
||||||
s2t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
st.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('decode', function (t) {
|
|
||||||
t.equal(
|
|
||||||
utils.decode('a+b'),
|
|
||||||
'a b',
|
|
||||||
'decodes + to space'
|
|
||||||
);
|
|
||||||
|
|
||||||
t.equal(
|
|
||||||
utils.decode('name%2Eobj'),
|
|
||||||
'name.obj',
|
|
||||||
'decodes a string'
|
|
||||||
);
|
|
||||||
t.equal(
|
|
||||||
utils.decode('name%2Eobj%2Efoo', null, 'iso-8859-1'),
|
|
||||||
'name.obj.foo',
|
|
||||||
'decodes a string in iso-8859-1'
|
|
||||||
);
|
|
||||||
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('encode', function (t) {
|
|
||||||
forEach(v.nullPrimitives, function (nullish) {
|
|
||||||
t['throws'](
|
|
||||||
function () { utils.encode(nullish); },
|
|
||||||
TypeError,
|
|
||||||
inspect(nullish) + ' is not a string'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
t.equal(utils.encode(''), '', 'empty string returns itself');
|
|
||||||
t.deepEqual(utils.encode([]), [], 'empty array returns itself');
|
|
||||||
t.deepEqual(utils.encode({ length: 0 }), { length: 0 }, 'empty arraylike returns itself');
|
|
||||||
|
|
||||||
t.test('symbols', { skip: !v.hasSymbols }, function (st) {
|
|
||||||
st.equal(utils.encode(Symbol('x')), 'Symbol%28x%29', 'symbol is encoded');
|
|
||||||
|
|
||||||
st.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
t.equal(
|
|
||||||
utils.encode('(abc)'),
|
|
||||||
'%28abc%29',
|
|
||||||
'encodes parentheses'
|
|
||||||
);
|
|
||||||
t.equal(
|
|
||||||
utils.encode({ toString: function () { return '(abc)'; } }),
|
|
||||||
'%28abc%29',
|
|
||||||
'toStrings and encodes parentheses'
|
|
||||||
);
|
|
||||||
|
|
||||||
t.equal(
|
|
||||||
utils.encode('abc 123 💩', null, 'iso-8859-1'),
|
|
||||||
'abc%20123%20%26%2355357%3B%26%2356489%3B',
|
|
||||||
'encodes in iso-8859-1'
|
|
||||||
);
|
|
||||||
|
|
||||||
var longString = '';
|
|
||||||
var expectedString = '';
|
|
||||||
for (var i = 0; i < 1500; i++) {
|
|
||||||
longString += ' ';
|
|
||||||
expectedString += '%20';
|
|
||||||
}
|
|
||||||
|
|
||||||
t.equal(
|
|
||||||
utils.encode(longString),
|
|
||||||
expectedString,
|
|
||||||
'encodes a long string'
|
|
||||||
);
|
|
||||||
|
|
||||||
t.equal(
|
|
||||||
utils.encode('\x28\x29'),
|
|
||||||
'%28%29',
|
|
||||||
'encodes parens normally'
|
|
||||||
);
|
|
||||||
t.equal(
|
|
||||||
utils.encode('\x28\x29', null, null, null, 'RFC1738'),
|
|
||||||
'()',
|
|
||||||
'does not encode parens in RFC1738'
|
|
||||||
);
|
|
||||||
|
|
||||||
// todo RFC1738 format
|
|
||||||
|
|
||||||
t.equal(
|
|
||||||
utils.encode('Āက豈'),
|
|
||||||
'%C4%80%E1%80%80%EF%A4%80',
|
|
||||||
'encodes multibyte chars'
|
|
||||||
);
|
|
||||||
|
|
||||||
t.equal(
|
|
||||||
utils.encode('\uD83D \uDCA9'),
|
|
||||||
'%F0%9F%90%A0%F0%BA%90%80',
|
|
||||||
'encodes lone surrogates'
|
|
||||||
);
|
|
||||||
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('isBuffer()', function (t) {
|
|
||||||
var fn = function () {};
|
|
||||||
fn();
|
|
||||||
forEach([null, undefined, true, false, '', 'abc', 42, 0, NaN, {}, [], fn, /a/g], function (x) {
|
|
||||||
t.equal(utils.isBuffer(x), false, inspect(x) + ' is not a buffer');
|
|
||||||
});
|
|
||||||
|
|
||||||
var fakeBuffer = { constructor: Buffer };
|
|
||||||
t.equal(utils.isBuffer(fakeBuffer), false, 'fake buffer is not a buffer');
|
|
||||||
|
|
||||||
var saferBuffer = SaferBuffer.from('abc');
|
|
||||||
t.equal(utils.isBuffer(saferBuffer), true, 'SaferBuffer instance is a buffer');
|
|
||||||
|
|
||||||
var buffer = SaferBuffer.from('abc');
|
|
||||||
t.notEqual(saferBuffer, buffer, 'different buffer instances');
|
|
||||||
t.equal(utils.isBuffer(buffer), true, 'another Buffer instance is a buffer');
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('isRegExp()', function (t) {
|
|
||||||
t.equal(utils.isRegExp(/a/g), true, 'RegExp is a RegExp');
|
|
||||||
t.equal(utils.isRegExp(new RegExp('a', 'g')), true, 'new RegExp is a RegExp');
|
|
||||||
t.equal(utils.isRegExp(new Date()), false, 'Date is not a RegExp');
|
|
||||||
|
|
||||||
forEach(v.primitives, function (primitive) {
|
|
||||||
t.equal(utils.isRegExp(primitive), false, inspect(primitive) + ' is not a RegExp');
|
|
||||||
});
|
|
||||||
|
|
||||||
t.end();
|
|
||||||
});
|
|
||||||
-55
@@ -1,55 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "body-parser",
|
|
||||||
"description": "Node.js body parsing middleware",
|
|
||||||
"version": "1.20.5",
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-70
@@ -1,70 +0,0 @@
|
|||||||
# Authors
|
|
||||||
|
|
||||||
#### Ordered by first contribution.
|
|
||||||
|
|
||||||
- Romain Beauxis (toots@rastageeks.org)
|
|
||||||
- Tobias Koppers (tobias.koppers@googlemail.com)
|
|
||||||
- Janus (ysangkok@gmail.com)
|
|
||||||
- Rainer Dreyer (rdrey1@gmail.com)
|
|
||||||
- Tõnis Tiigi (tonistiigi@gmail.com)
|
|
||||||
- James Halliday (mail@substack.net)
|
|
||||||
- Michael Williamson (mike@zwobble.org)
|
|
||||||
- elliottcable (github@elliottcable.name)
|
|
||||||
- rafael (rvalle@livelens.net)
|
|
||||||
- Andrew Kelley (superjoe30@gmail.com)
|
|
||||||
- Andreas Madsen (amwebdk@gmail.com)
|
|
||||||
- Mike Brevoort (mike.brevoort@pearson.com)
|
|
||||||
- Brian White (mscdex@mscdex.net)
|
|
||||||
- Feross Aboukhadijeh (feross@feross.org)
|
|
||||||
- Ruben Verborgh (ruben@verborgh.org)
|
|
||||||
- eliang (eliang.cs@gmail.com)
|
|
||||||
- Jesse Tane (jesse.tane@gmail.com)
|
|
||||||
- Alfonso Boza (alfonso@cloud.com)
|
|
||||||
- Mathias Buus (mathiasbuus@gmail.com)
|
|
||||||
- Devon Govett (devongovett@gmail.com)
|
|
||||||
- Daniel Cousens (github@dcousens.com)
|
|
||||||
- Joseph Dykstra (josephdykstra@gmail.com)
|
|
||||||
- Parsha Pourkhomami (parshap+git@gmail.com)
|
|
||||||
- Damjan Košir (damjan.kosir@gmail.com)
|
|
||||||
- daverayment (dave.rayment@gmail.com)
|
|
||||||
- kawanet (u-suke@kawa.net)
|
|
||||||
- Linus Unnebäck (linus@folkdatorn.se)
|
|
||||||
- Nolan Lawson (nolan.lawson@gmail.com)
|
|
||||||
- Calvin Metcalf (calvin.metcalf@gmail.com)
|
|
||||||
- Koki Takahashi (hakatasiloving@gmail.com)
|
|
||||||
- Guy Bedford (guybedford@gmail.com)
|
|
||||||
- Jan Schär (jscissr@gmail.com)
|
|
||||||
- RaulTsc (tomescu.raul@gmail.com)
|
|
||||||
- Matthieu Monsch (monsch@alum.mit.edu)
|
|
||||||
- Dan Ehrenberg (littledan@chromium.org)
|
|
||||||
- Kirill Fomichev (fanatid@ya.ru)
|
|
||||||
- Yusuke Kawasaki (u-suke@kawa.net)
|
|
||||||
- DC (dcposch@dcpos.ch)
|
|
||||||
- John-David Dalton (john.david.dalton@gmail.com)
|
|
||||||
- adventure-yunfei (adventure030@gmail.com)
|
|
||||||
- Emil Bay (github@tixz.dk)
|
|
||||||
- Sam Sudar (sudar.sam@gmail.com)
|
|
||||||
- Volker Mische (volker.mische@gmail.com)
|
|
||||||
- David Walton (support@geekstocks.com)
|
|
||||||
- Сковорода Никита Андреевич (chalkerx@gmail.com)
|
|
||||||
- greenkeeper[bot] (greenkeeper[bot]@users.noreply.github.com)
|
|
||||||
- ukstv (sergey.ukustov@machinomy.com)
|
|
||||||
- Renée Kooi (renee@kooi.me)
|
|
||||||
- ranbochen (ranbochen@qq.com)
|
|
||||||
- Vladimir Borovik (bobahbdb@gmail.com)
|
|
||||||
- greenkeeper[bot] (23040076+greenkeeper[bot]@users.noreply.github.com)
|
|
||||||
- kumavis (aaron@kumavis.me)
|
|
||||||
- Sergey Ukustov (sergey.ukustov@machinomy.com)
|
|
||||||
- Fei Liu (liu.feiwood@gmail.com)
|
|
||||||
- Blaine Bublitz (blaine.bublitz@gmail.com)
|
|
||||||
- clement (clement@seald.io)
|
|
||||||
- Koushik Dutta (koushd@gmail.com)
|
|
||||||
- Jordan Harband (ljharb@gmail.com)
|
|
||||||
- Niklas Mischkulnig (mischnic@users.noreply.github.com)
|
|
||||||
- Nikolai Vavilov (vvnicholas@gmail.com)
|
|
||||||
- Fedor Nezhivoi (gyzerok@users.noreply.github.com)
|
|
||||||
- Peter Newman (peternewman@users.noreply.github.com)
|
|
||||||
- mathmakgakpak (44949126+mathmakgakpak@users.noreply.github.com)
|
|
||||||
- jkkang (jkkang@smartauth.kr)
|
|
||||||
|
|
||||||
#### Generated by bin/update-authors.sh.
|
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) Feross Aboukhadijeh, and other contributors.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user