mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-27 21:05:34 +00:00
fix: remove whatsapp bridge node_modules from git, add to gitignore
This commit is contained in:
@@ -89,3 +89,4 @@ CLAUDE.md
|
||||
# Research output
|
||||
research_mining_*
|
||||
.python-version
|
||||
src/openjarvis/channels/whatsapp_baileys_bridge/node_modules/
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
../pino/bin.js
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
../qrcode-terminal/bin/qrcode-terminal.js
|
||||
-1
@@ -1 +0,0 @@
|
||||
../semver/bin/semver.js
|
||||
-1
@@ -1 +0,0 @@
|
||||
../typescript/bin/tsc
|
||||
-1
@@ -1 +0,0 @@
|
||||
../typescript/bin/tsserver
|
||||
-1363
File diff suppressed because it is too large
Load Diff
Generated
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2025 Borewit
|
||||
|
||||
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.
|
||||
Generated
Vendored
-87
@@ -1,87 +0,0 @@
|
||||
[](https://github.com/Borewit/text-codec/actions/workflows/ci.yml)
|
||||
[](https://www.npmjs.com/package/@borewit/text-codec)
|
||||
[](https://npmcharts.com/compare/@borewit/text-codec?interval=30)
|
||||

|
||||
[](LICENSE.txt)
|
||||
|
||||
# `@borewit/text-codec`
|
||||
|
||||
A **lightweight polyfill for text encoders and decoders** covering a small set of commonly used encodings.
|
||||
|
||||
Some JavaScript runtimes provide limited or inconsistent encoding support through `TextEncoder` and `TextDecoder`.
|
||||
Examples include environments like **Hermes (React Native)** or certain **Node.js builds with limited ICU support**.
|
||||
|
||||
This module provides **reliable encode/decode support for a small set of encodings that may be missing or unreliable in those environments**.
|
||||
|
||||
- If a native UTF-8 `TextEncoder` / `TextDecoder` is available, it is used.
|
||||
- All other encodings are implemented by this library.
|
||||
|
||||
## Supported encodings
|
||||
|
||||
- `utf-8` / `utf8`
|
||||
- `utf-16le`
|
||||
- `ascii`
|
||||
- `latin1` / `iso-8859-1`
|
||||
- `windows-1252`
|
||||
|
||||
These encodings are commonly encountered in metadata formats and legacy text data.
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- Encoding and decoding utilities
|
||||
- Lightweight
|
||||
- Typed API
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
```sh
|
||||
npm install @borewit/text-codec
|
||||
```
|
||||
|
||||
# 📚 API Documentation
|
||||
|
||||
## `textDecode(bytes, encoding): string`
|
||||
|
||||
Decodes binary data into a JavaScript string.
|
||||
|
||||
**Parameters**
|
||||
- `bytes` (`Uint8Array`) — The binary data to decode.
|
||||
- `encoding` (`SupportedEncoding`, optional) — Encoding type. Defaults to `"utf-8"`.
|
||||
|
||||
**Returns**
|
||||
- `string` — The decoded text.
|
||||
|
||||
**Example**
|
||||
```js
|
||||
import { textDecode } from "@borewit/text-codec";
|
||||
|
||||
const bytes = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
|
||||
const text = textDecode(bytes, "ascii");
|
||||
console.log(text); // "Hello"
|
||||
```
|
||||
|
||||
## `textEncode(input, encoding): Uint8Array`
|
||||
|
||||
Encodes a JavaScript string into binary form using the specified encoding.
|
||||
|
||||
**Parameters**
|
||||
|
||||
- `input` (`string`) — The string to encode.
|
||||
- `encoding` (`SupportedEncoding`, optional) — Encoding type. Defaults to `"utf-8"`.
|
||||
|
||||
**Returns**
|
||||
|
||||
`Uint8Array` — The encoded binary data.
|
||||
|
||||
Example:
|
||||
```js
|
||||
import { textEncode } from "@borewit/text-codec";
|
||||
|
||||
const bytes = textEncode("Hello", "utf-16le");
|
||||
console.log(bytes); // Uint8Array([...])
|
||||
```
|
||||
|
||||
## 📜 Licence
|
||||
|
||||
This project is licensed under the [MIT License](LICENSE.txt). Feel free to use, modify, and distribute as needed.
|
||||
|
||||
Generated
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
export type SupportedEncoding = "utf-8" | "utf8" | "utf-16le" | "us-ascii" | "ascii" | "latin1" | "iso-8859-1" | "windows-1252";
|
||||
/**
|
||||
* Decode text from binary data
|
||||
*/
|
||||
export declare function textDecode(bytes: Uint8Array, encoding?: SupportedEncoding): string;
|
||||
export declare function textEncode(input?: string, encoding?: SupportedEncoding): Uint8Array;
|
||||
Generated
Vendored
-380
@@ -1,380 +0,0 @@
|
||||
const WINDOWS_1252_EXTRA = {
|
||||
0x80: "€", 0x82: "‚", 0x83: "ƒ", 0x84: "„", 0x85: "…", 0x86: "†",
|
||||
0x87: "‡", 0x88: "ˆ", 0x89: "‰", 0x8a: "Š", 0x8b: "‹", 0x8c: "Œ",
|
||||
0x8e: "Ž", 0x91: "‘", 0x92: "’", 0x93: "“", 0x94: "”", 0x95: "•",
|
||||
0x96: "–", 0x97: "—", 0x98: "˜", 0x99: "™", 0x9a: "š", 0x9b: "›",
|
||||
0x9c: "œ", 0x9e: "ž", 0x9f: "Ÿ",
|
||||
};
|
||||
const WINDOWS_1252_REVERSE = {};
|
||||
for (const [code, char] of Object.entries(WINDOWS_1252_EXTRA)) {
|
||||
WINDOWS_1252_REVERSE[char] = Number.parseInt(code, 10);
|
||||
}
|
||||
let _utf8Decoder;
|
||||
let _utf8Encoder;
|
||||
function utf8Decoder() {
|
||||
if (typeof globalThis.TextDecoder === "undefined")
|
||||
return undefined;
|
||||
return (_utf8Decoder !== null && _utf8Decoder !== void 0 ? _utf8Decoder : (_utf8Decoder = new globalThis.TextDecoder("utf-8")));
|
||||
}
|
||||
function utf8Encoder() {
|
||||
if (typeof globalThis.TextEncoder === "undefined")
|
||||
return undefined;
|
||||
return (_utf8Encoder !== null && _utf8Encoder !== void 0 ? _utf8Encoder : (_utf8Encoder = new globalThis.TextEncoder()));
|
||||
}
|
||||
const CHUNK = 32 * 1024;
|
||||
const REPLACEMENT = 0xfffd;
|
||||
/**
|
||||
* Decode text from binary data
|
||||
*/
|
||||
export function textDecode(bytes, encoding = "utf-8") {
|
||||
switch (encoding.toLowerCase()) {
|
||||
case "utf-8":
|
||||
case "utf8": {
|
||||
const dec = utf8Decoder();
|
||||
return dec ? dec.decode(bytes) : decodeUTF8(bytes);
|
||||
}
|
||||
case "utf-16le":
|
||||
return decodeUTF16LE(bytes);
|
||||
case "us-ascii":
|
||||
case "ascii":
|
||||
return decodeASCII(bytes);
|
||||
case "latin1":
|
||||
case "iso-8859-1":
|
||||
return decodeLatin1(bytes);
|
||||
case "windows-1252":
|
||||
return decodeWindows1252(bytes);
|
||||
default:
|
||||
throw new RangeError(`Encoding '${encoding}' not supported`);
|
||||
}
|
||||
}
|
||||
export function textEncode(input = "", encoding = "utf-8") {
|
||||
switch (encoding.toLowerCase()) {
|
||||
case "utf-8":
|
||||
case "utf8": {
|
||||
const enc = utf8Encoder();
|
||||
return enc ? enc.encode(input) : encodeUTF8(input);
|
||||
}
|
||||
case "utf-16le":
|
||||
return encodeUTF16LE(input);
|
||||
case "us-ascii":
|
||||
case "ascii":
|
||||
return encodeASCII(input);
|
||||
case "latin1":
|
||||
case "iso-8859-1":
|
||||
return encodeLatin1(input);
|
||||
case "windows-1252":
|
||||
return encodeWindows1252(input);
|
||||
default:
|
||||
throw new RangeError(`Encoding '${encoding}' not supported`);
|
||||
}
|
||||
}
|
||||
function appendCodePoint(out, cp) {
|
||||
if (cp <= 0xffff) {
|
||||
out.push(String.fromCharCode(cp));
|
||||
return;
|
||||
}
|
||||
cp -= 0x10000;
|
||||
out.push(String.fromCharCode(0xd800 + (cp >> 10), 0xdc00 + (cp & 0x3ff)));
|
||||
}
|
||||
function flushChunk(parts, chunk) {
|
||||
if (chunk.length === 0)
|
||||
return;
|
||||
parts.push(String.fromCharCode.apply(null, chunk));
|
||||
chunk.length = 0;
|
||||
}
|
||||
function pushCodeUnit(parts, chunk, codeUnit) {
|
||||
chunk.push(codeUnit);
|
||||
if (chunk.length >= CHUNK)
|
||||
flushChunk(parts, chunk);
|
||||
}
|
||||
function pushCodePoint(parts, chunk, cp) {
|
||||
if (cp <= 0xffff) {
|
||||
pushCodeUnit(parts, chunk, cp);
|
||||
return;
|
||||
}
|
||||
cp -= 0x10000;
|
||||
pushCodeUnit(parts, chunk, 0xd800 + (cp >> 10));
|
||||
pushCodeUnit(parts, chunk, 0xdc00 + (cp & 0x3ff));
|
||||
}
|
||||
function decodeUTF8(bytes) {
|
||||
const parts = [];
|
||||
const chunk = [];
|
||||
let i = 0;
|
||||
// Match TextDecoder("utf-8") default BOM handling
|
||||
if (bytes.length >= 3 &&
|
||||
bytes[0] === 0xef &&
|
||||
bytes[1] === 0xbb &&
|
||||
bytes[2] === 0xbf) {
|
||||
i = 3;
|
||||
}
|
||||
while (i < bytes.length) {
|
||||
const b1 = bytes[i];
|
||||
if (b1 <= 0x7f) {
|
||||
pushCodeUnit(parts, chunk, b1);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// Invalid leading bytes: continuation byte or impossible prefixes
|
||||
if (b1 < 0xc2 || b1 > 0xf4) {
|
||||
pushCodeUnit(parts, chunk, REPLACEMENT);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// 2-byte sequence
|
||||
if (b1 <= 0xdf) {
|
||||
if (i + 1 >= bytes.length) {
|
||||
pushCodeUnit(parts, chunk, REPLACEMENT);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const b2 = bytes[i + 1];
|
||||
if ((b2 & 0xc0) !== 0x80) {
|
||||
pushCodeUnit(parts, chunk, REPLACEMENT);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const cp = ((b1 & 0x1f) << 6) | (b2 & 0x3f);
|
||||
pushCodeUnit(parts, chunk, cp);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
// 3-byte sequence
|
||||
if (b1 <= 0xef) {
|
||||
if (i + 2 >= bytes.length) {
|
||||
pushCodeUnit(parts, chunk, REPLACEMENT);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const b2 = bytes[i + 1];
|
||||
const b3 = bytes[i + 2];
|
||||
const valid = (b2 & 0xc0) === 0x80 &&
|
||||
(b3 & 0xc0) === 0x80 &&
|
||||
!(b1 === 0xe0 && b2 < 0xa0) && // overlong
|
||||
!(b1 === 0xed && b2 >= 0xa0); // surrogate range
|
||||
if (!valid) {
|
||||
pushCodeUnit(parts, chunk, REPLACEMENT);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const cp = ((b1 & 0x0f) << 12) |
|
||||
((b2 & 0x3f) << 6) |
|
||||
(b3 & 0x3f);
|
||||
pushCodeUnit(parts, chunk, cp);
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
// 4-byte sequence
|
||||
if (i + 3 >= bytes.length) {
|
||||
pushCodeUnit(parts, chunk, REPLACEMENT);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const b2 = bytes[i + 1];
|
||||
const b3 = bytes[i + 2];
|
||||
const b4 = bytes[i + 3];
|
||||
const valid = (b2 & 0xc0) === 0x80 &&
|
||||
(b3 & 0xc0) === 0x80 &&
|
||||
(b4 & 0xc0) === 0x80 &&
|
||||
!(b1 === 0xf0 && b2 < 0x90) && // overlong
|
||||
!(b1 === 0xf4 && b2 > 0x8f); // > U+10FFFF
|
||||
if (!valid) {
|
||||
pushCodeUnit(parts, chunk, REPLACEMENT);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
const cp = ((b1 & 0x07) << 18) |
|
||||
((b2 & 0x3f) << 12) |
|
||||
((b3 & 0x3f) << 6) |
|
||||
(b4 & 0x3f);
|
||||
pushCodePoint(parts, chunk, cp);
|
||||
i += 4;
|
||||
}
|
||||
flushChunk(parts, chunk);
|
||||
return parts.join("");
|
||||
}
|
||||
function decodeUTF16LE(bytes) {
|
||||
const parts = [];
|
||||
const chunk = [];
|
||||
const len = bytes.length;
|
||||
let i = 0;
|
||||
while (i + 1 < len) {
|
||||
const u1 = bytes[i] | (bytes[i + 1] << 8);
|
||||
i += 2;
|
||||
// High surrogate
|
||||
if (u1 >= 0xd800 && u1 <= 0xdbff) {
|
||||
if (i + 1 < len) {
|
||||
const u2 = bytes[i] | (bytes[i + 1] << 8);
|
||||
if (u2 >= 0xdc00 && u2 <= 0xdfff) {
|
||||
pushCodeUnit(parts, chunk, u1);
|
||||
pushCodeUnit(parts, chunk, u2);
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
pushCodeUnit(parts, chunk, REPLACEMENT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
pushCodeUnit(parts, chunk, REPLACEMENT);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Lone low surrogate
|
||||
if (u1 >= 0xdc00 && u1 <= 0xdfff) {
|
||||
pushCodeUnit(parts, chunk, REPLACEMENT);
|
||||
continue;
|
||||
}
|
||||
pushCodeUnit(parts, chunk, u1);
|
||||
}
|
||||
// Odd trailing byte
|
||||
if (i < len) {
|
||||
pushCodeUnit(parts, chunk, REPLACEMENT);
|
||||
}
|
||||
flushChunk(parts, chunk);
|
||||
return parts.join("");
|
||||
}
|
||||
function decodeASCII(bytes) {
|
||||
const parts = [];
|
||||
for (let i = 0; i < bytes.length; i += CHUNK) {
|
||||
const end = Math.min(bytes.length, i + CHUNK);
|
||||
const codes = new Array(end - i);
|
||||
for (let j = i, k = 0; j < end; j++, k++) {
|
||||
codes[k] = bytes[j] & 0x7f;
|
||||
}
|
||||
parts.push(String.fromCharCode.apply(null, codes));
|
||||
}
|
||||
return parts.join("");
|
||||
}
|
||||
function decodeLatin1(bytes) {
|
||||
const parts = [];
|
||||
for (let i = 0; i < bytes.length; i += CHUNK) {
|
||||
const end = Math.min(bytes.length, i + CHUNK);
|
||||
const codes = new Array(end - i);
|
||||
for (let j = i, k = 0; j < end; j++, k++) {
|
||||
codes[k] = bytes[j];
|
||||
}
|
||||
parts.push(String.fromCharCode.apply(null, codes));
|
||||
}
|
||||
return parts.join("");
|
||||
}
|
||||
function decodeWindows1252(bytes) {
|
||||
const parts = [];
|
||||
let out = "";
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
const b = bytes[i];
|
||||
const extra = b >= 0x80 && b <= 0x9f ? WINDOWS_1252_EXTRA[b] : undefined;
|
||||
out += extra !== null && extra !== void 0 ? extra : String.fromCharCode(b);
|
||||
if (out.length >= CHUNK) {
|
||||
parts.push(out);
|
||||
out = "";
|
||||
}
|
||||
}
|
||||
if (out)
|
||||
parts.push(out);
|
||||
return parts.join("");
|
||||
}
|
||||
function encodeUTF8(str) {
|
||||
const out = [];
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
let cp = str.charCodeAt(i);
|
||||
// Valid surrogate pair
|
||||
if (cp >= 0xd800 && cp <= 0xdbff) {
|
||||
if (i + 1 < str.length) {
|
||||
const lo = str.charCodeAt(i + 1);
|
||||
if (lo >= 0xdc00 && lo <= 0xdfff) {
|
||||
cp = 0x10000 + ((cp - 0xd800) << 10) + (lo - 0xdc00);
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
cp = REPLACEMENT;
|
||||
}
|
||||
}
|
||||
else {
|
||||
cp = REPLACEMENT;
|
||||
}
|
||||
}
|
||||
else if (cp >= 0xdc00 && cp <= 0xdfff) {
|
||||
// Lone low surrogate
|
||||
cp = REPLACEMENT;
|
||||
}
|
||||
if (cp < 0x80) {
|
||||
out.push(cp);
|
||||
}
|
||||
else if (cp < 0x800) {
|
||||
out.push(0xc0 | (cp >> 6), 0x80 | (cp & 0x3f));
|
||||
}
|
||||
else if (cp < 0x10000) {
|
||||
out.push(0xe0 | (cp >> 12), 0x80 | ((cp >> 6) & 0x3f), 0x80 | (cp & 0x3f));
|
||||
}
|
||||
else {
|
||||
out.push(0xf0 | (cp >> 18), 0x80 | ((cp >> 12) & 0x3f), 0x80 | ((cp >> 6) & 0x3f), 0x80 | (cp & 0x3f));
|
||||
}
|
||||
}
|
||||
return new Uint8Array(out);
|
||||
}
|
||||
function encodeUTF16LE(str) {
|
||||
// Preserve JS string code units, but do not emit non-well-formed UTF-16.
|
||||
// Replace lone surrogates with U+FFFD.
|
||||
const units = [];
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const u = str.charCodeAt(i);
|
||||
if (u >= 0xd800 && u <= 0xdbff) {
|
||||
if (i + 1 < str.length) {
|
||||
const lo = str.charCodeAt(i + 1);
|
||||
if (lo >= 0xdc00 && lo <= 0xdfff) {
|
||||
units.push(u, lo);
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
units.push(REPLACEMENT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
units.push(REPLACEMENT);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (u >= 0xdc00 && u <= 0xdfff) {
|
||||
units.push(REPLACEMENT);
|
||||
continue;
|
||||
}
|
||||
units.push(u);
|
||||
}
|
||||
const out = new Uint8Array(units.length * 2);
|
||||
for (let i = 0; i < units.length; i++) {
|
||||
const code = units[i];
|
||||
const o = i * 2;
|
||||
out[o] = code & 0xff;
|
||||
out[o + 1] = code >>> 8;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function encodeASCII(str) {
|
||||
const out = new Uint8Array(str.length);
|
||||
for (let i = 0; i < str.length; i++)
|
||||
out[i] = str.charCodeAt(i) & 0x7f;
|
||||
return out;
|
||||
}
|
||||
function encodeLatin1(str) {
|
||||
const out = new Uint8Array(str.length);
|
||||
for (let i = 0; i < str.length; i++)
|
||||
out[i] = str.charCodeAt(i) & 0xff;
|
||||
return out;
|
||||
}
|
||||
function encodeWindows1252(str) {
|
||||
const out = new Uint8Array(str.length);
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const ch = str[i];
|
||||
const code = ch.charCodeAt(0);
|
||||
if (WINDOWS_1252_REVERSE[ch] !== undefined) {
|
||||
out[i] = WINDOWS_1252_REVERSE[ch];
|
||||
continue;
|
||||
}
|
||||
if ((code >= 0x00 && code <= 0x7f) ||
|
||||
(code >= 0xa0 && code <= 0xff)) {
|
||||
out[i] = code;
|
||||
continue;
|
||||
}
|
||||
out[i] = 0x3f; // '?'
|
||||
}
|
||||
return out;
|
||||
}
|
||||
Generated
Vendored
-70
@@ -1,70 +0,0 @@
|
||||
{
|
||||
"name": "@borewit/text-codec",
|
||||
"version": "0.2.2",
|
||||
"description": "Text Decoder",
|
||||
"type": "module",
|
||||
"exports": "./lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
"lib/index.d.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "del-cli lib/**/*.js lib/***.js.map test/**/*.d.ts test/**/*.js test/**/*.js.map",
|
||||
"build": "npm run compile",
|
||||
"prepublishOnly": "npm run build",
|
||||
"compile:src": "tsc --p lib --sourceMap false",
|
||||
"compile:test": "tsc --p test",
|
||||
"compile": "npm run compile:src && npm run compile:test",
|
||||
"lint": "biome check",
|
||||
"test": "mocha",
|
||||
"update-biome": "npm install --save-dev --save-exact @biomejs/biome@latest && npx @biomejs/biome migrate --write"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.4.6",
|
||||
"@types/chai": "^5.2.2",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"chai": "^6.2.2",
|
||||
"mocha": "^11.7.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"keywords": [
|
||||
"TextDecoder",
|
||||
"TextEncoder",
|
||||
"decoder",
|
||||
"decoding",
|
||||
"encod",
|
||||
"encoding",
|
||||
"decode",
|
||||
"text",
|
||||
"ascii",
|
||||
"utf-8",
|
||||
"utf8",
|
||||
"utf-16le",
|
||||
"latin1",
|
||||
"iso-8859-1",
|
||||
"windows-1252",
|
||||
"charset",
|
||||
"encoding",
|
||||
"decoding",
|
||||
"polyfill",
|
||||
"character-set",
|
||||
"latin",
|
||||
"hermes",
|
||||
"react"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Borewit/text-codec.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Borewit",
|
||||
"url": "https://github.com/Borewit"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
Generated
Vendored
-19
@@ -1,19 +0,0 @@
|
||||
MIT License & © Jared Wray
|
||||
|
||||
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.
|
||||
Generated
Vendored
-340
@@ -1,340 +0,0 @@
|
||||
[<img align="center" src="https://cacheable.org/logo.svg" alt="Cacheable" />](https://github.com/jaredwray/cacheable)
|
||||
|
||||
> High Performance Layer 1 / Layer 2 Caching with Keyv Storage
|
||||
|
||||
[](https://codecov.io/gh/jaredwray/cacheable)
|
||||
[](https://github.com/jaredwray/cacheable/actions/workflows/tests.yml)
|
||||
[](https://www.npmjs.com/package/@cacheable/memory)
|
||||
[](https://www.npmjs.com/package/@cacheable/memory)
|
||||
[](https://github.com/jaredwray/cacheable/blob/main/LICENSE)
|
||||
|
||||
You can use `CacheableMemory` as a standalone cache or as a primary store for `cacheable`. You can also set the `useClones` property to `false` if you want to use the same reference for the values. This is useful if you are using large objects and want to save memory. The `lruSize` property is the size of the LRU cache and is set to `0` by default which is unlimited. When setting the `lruSize` property it will limit the number of keys in the cache.
|
||||
|
||||
This simple in-memory cache uses multiple Map objects and a with `expiration` and `lru` policies if set to manage the in memory cache at scale.
|
||||
|
||||
By default we use lazy expiration deletion which means on `get` and `getMany` type functions we look if it is expired and then delete it. If you want to have a more aggressive expiration policy you can set the `checkInterval` property to a value greater than `0` which will check for expired keys at the interval you set.
|
||||
|
||||
Here are some of the main features of `CacheableMemory`:
|
||||
* High performance in-memory cache with a robust API and feature set. 🚀
|
||||
* Can scale past the `16,777,216 (2^24) keys` limit of a single `Map` via `hashStoreSize`. Default is `16` Map objects.
|
||||
* LRU (Least Recently Used) cache feature to limit the number of keys in the cache via `lruSize`. Limit to `16,777,216 (2^24) keys` total.
|
||||
* Expiration policy to delete expired keys with lazy deletion or aggressive deletion via `checkInterval`.
|
||||
* `Wrap` feature to memoize `sync` and `async` functions with stampede protection.
|
||||
* Ability to do many operations at once such as `setMany`, `getMany`, `deleteMany`, and `takeMany`.
|
||||
* Supports `raw` data retrieval with `getRaw` and `getManyRaw` methods to get the full metadata of the cache entry.
|
||||
|
||||
# Table of Contents
|
||||
* [Getting Started](#getting-started)
|
||||
* [CacheableMemory - In-Memory Cache](#cacheablememory---in-memory-cache)
|
||||
* [CacheableMemory Store Hashing](#cacheablememory-store-hashing)
|
||||
* [CacheableMemory LRU Feature](#cacheablememory-lru-feature)
|
||||
* [CacheableMemory Performance](#cacheablememory-performance)
|
||||
* [CacheableMemory Options](#cacheablememory-options)
|
||||
* [CacheableMemory - API](#cacheablememory---api)
|
||||
* [Keyv Storage Adapter - KeyvCacheableMemory](#keyv-storage-adapter---keyvcacheablememory)
|
||||
* [Wrap / Memoization for Sync and Async Functions](#wrap--memoization-for-sync-and-async-functions)
|
||||
* [Get Or Set Memoization Function](#get-or-set-memoization-function)
|
||||
* [How to Contribute](#how-to-contribute)
|
||||
* [License and Copyright](#license-and-copyright)
|
||||
|
||||
# Getting Started
|
||||
|
||||
```bash
|
||||
npm install @cacheable/memory
|
||||
```
|
||||
|
||||
# Basic Usage
|
||||
|
||||
```javascript
|
||||
import { CacheableMemory } from '@cacheable/memory';
|
||||
|
||||
const cacheable = new CacheableMemory();
|
||||
await cacheable.set('key', 'value', 1000);
|
||||
const value = await cacheable.get('key');
|
||||
```
|
||||
|
||||
In this example, the primary store we will use `lru-cache` and the secondary store is Redis. You can also set multiple stores in the options:
|
||||
|
||||
```javascript
|
||||
import { CacheableMemory } from '@cacheable/memory';
|
||||
|
||||
// we set the storeHashSize to 1 so that we only use a single Map object as the lru is limited to a single Map size
|
||||
const cache = new CacheableMemory({storeHashSize: 1, lruSize: 80000});
|
||||
|
||||
cache.set('key1', 'value1');
|
||||
const result = cache.get('key1');
|
||||
console.log(result); // 'value1'
|
||||
```
|
||||
|
||||
This is a more advanced example and not needed for most use cases.
|
||||
|
||||
# Shorthand for Time to Live (ttl)
|
||||
|
||||
By default `Cacheable` and `CacheableMemory` the `ttl` is in milliseconds but you can use shorthand for the time to live. Here are the following shorthand values:
|
||||
|
||||
* `ms`: Milliseconds such as (1ms = 1)
|
||||
* `s`: Seconds such as (1s = 1000)
|
||||
* `m`: Minutes such as (1m = 60000)
|
||||
* `h` or `hr`: Hours such as (1h = 3600000)
|
||||
* `d`: Days such as (1d = 86400000)
|
||||
|
||||
Here is an example of how to use the shorthand for the `ttl`:
|
||||
|
||||
```javascript
|
||||
import { CacheableMemory } from 'cacheable';
|
||||
const cache = new CacheableMemory({ ttl: '15m' }); //sets the default ttl to 15 minutes (900000 ms)
|
||||
cache.set('key', 'value', '1h'); //sets the ttl to 1 hour (3600000 ms) and overrides the default
|
||||
```
|
||||
|
||||
if you want to disable the `ttl` you can set it to `0` or `undefined`:
|
||||
|
||||
```javascript
|
||||
import { CacheableMemory } from 'cacheable';
|
||||
const cache = new CacheableMemory({ ttl: 0 }); //sets the default ttl to 0 which is disabled
|
||||
cache.set('key', 'value', 0); //sets the ttl to 0 which is disabled
|
||||
```
|
||||
|
||||
If you set the ttl to anything below `0` or `undefined` it will disable the ttl for the cache and the value that returns will be `undefined`. With no ttl set the value will be stored `indefinitely`.
|
||||
|
||||
```javascript
|
||||
import { CacheableMemory } from 'cacheable';
|
||||
const cache = new CacheableMemory({ ttl: 0 }); //sets the default ttl to 0 which is disabled
|
||||
console.log(cache.ttl); // undefined
|
||||
cache.ttl = '1h'; // sets the default ttl to 1 hour (3600000 ms)
|
||||
console.log(cache.ttl); // '1h'
|
||||
cache.ttl = -1; // sets the default ttl to 0 which is disabled
|
||||
console.log(cache.ttl); // undefined
|
||||
```
|
||||
|
||||
## Retrieving raw cache entries
|
||||
|
||||
The `getRaw` and `getManyRaw` methods return the full stored metadata (`StoredDataRaw<T>`) instead of just the value:
|
||||
|
||||
```typescript
|
||||
import { CacheableMemory } from 'cacheable';
|
||||
|
||||
const cache = new CacheableMemory();
|
||||
|
||||
// store a value
|
||||
await cache.set('user:1', { name: 'Alice' }, '1h'); // 1 hour
|
||||
|
||||
// default: only the value
|
||||
const user = await cache.get<{ name: string }>('user:1');
|
||||
console.log(user); // { name: 'Alice' }
|
||||
|
||||
// with raw: full record including expiration
|
||||
const raw = await cache.getRaw('user:1');
|
||||
console.log(raw.value); // { name: 'Alice' }
|
||||
console.log(raw.expires); // e.g. 1677628495000 or null
|
||||
```
|
||||
|
||||
## CacheableMemory Store Hashing
|
||||
|
||||
`CacheableMemory` uses `Map` objects to store the keys and values. To make this scale past the `16,777,216 (2^24) keys` limit of a single `Map` we use a hash to balance the data across multiple `Map` objects. This is done by hashing the key and using the hash to determine which `Map` object to use. The default hashing algorithm is `djb2` but you can change it by setting the `storeHashAlgorithm` property in the options. Supported algorithms include DJB2, FNV1, MURMER, and CRC32. By default we set the amount of `Map` objects to `16`.
|
||||
|
||||
NOTE: if you are using the LRU cache feature the `lruSize` no matter how many `Map` objects you have it will be limited to the `16,777,216 (2^24) keys` limit of a single `Map` object. This is because we use a double linked list to manage the LRU cache and it is not possible to have more than `16,777,216 (2^24) keys` in a single `Map` object.
|
||||
|
||||
Here is an example of how to set the number of `Map` objects and the hashing algorithm:
|
||||
|
||||
```javascript
|
||||
import { CacheableMemory } from '@cacheable/memory';
|
||||
const cache = new CacheableMemory({
|
||||
storeSize: 32, // set the number of Map objects to 32
|
||||
});
|
||||
cache.set('key', 'value');
|
||||
const value = cache.get('key'); // value
|
||||
```
|
||||
|
||||
Here is an example of how to use the `storeHashAlgorithm` property with supported algorithms:
|
||||
|
||||
```javascript
|
||||
import { CacheableMemory, HashAlgorithm } from '@cacheable/memory';
|
||||
|
||||
// Using DJB2 (default)
|
||||
const cache = new CacheableMemory({ storeHashAlgorithm: HashAlgorithm.DJB2 });
|
||||
|
||||
// Or other non-cryptographic algorithms for better performance
|
||||
const cache2 = new CacheableMemory({ storeHashAlgorithm: HashAlgorithm.FNV1 });
|
||||
const cache3 = new CacheableMemory({ storeHashAlgorithm: HashAlgorithm.MURMER });
|
||||
const cache4 = new CacheableMemory({ storeHashAlgorithm: HashAlgorithm.CRC32 });
|
||||
|
||||
cache.set('key', 'value');
|
||||
const value = cache.get('key'); // value
|
||||
```
|
||||
|
||||
**Available algorithms:** DJB2 (default), FNV1, MURMER, CRC32. Note: Cryptographic algorithms (SHA-256, SHA-384, SHA-512) are not recommended for store hashing due to performance overhead.
|
||||
|
||||
If you want to provide your own hashing function you can set the `storeHashAlgorithm` property to a function that takes an object and returns a `number` that is in the range of the amount of `Map` stores you have.
|
||||
|
||||
```javascript
|
||||
import { CacheableMemory } from 'cacheable';
|
||||
/**
|
||||
* Custom hash function that takes a key and the size of the store
|
||||
* and returns a number between 0 and storeHashSize - 1.
|
||||
* @param {string} key - The key to hash.
|
||||
* @param {number} storeHashSize - The size of the store (number of Map objects).
|
||||
* @returns {number} - A number between 0 and storeHashSize - 1.
|
||||
*/
|
||||
const customHash = (key, storeHashSize) => {
|
||||
// custom hashing logic
|
||||
return key.length % storeHashSize; // returns a number between 0 and 31 for 32 Map objects
|
||||
};
|
||||
const cache = new CacheableMemory({ storeHashAlgorithm: customHash, storeSize: 32 });
|
||||
cache.set('key', 'value');
|
||||
const value = cache.get('key'); // value
|
||||
```
|
||||
|
||||
## CacheableMemory LRU Feature
|
||||
|
||||
You can enable the LRU (Least Recently Used) feature in `CacheableMemory` by setting the `lruSize` property in the options. This will limit the number of keys in the cache to the size you set. When the cache reaches the limit it will remove the least recently used keys from the cache. This is useful if you want to limit the memory usage of the cache.
|
||||
|
||||
When you set the `lruSize` we use a double linked list to manage the LRU cache and also set the `hashStoreSize` to `1` which means we will only use a single `Map` object for the LRU cache. This is because the LRU cache is managed by the double linked list and it is not possible to have more than `16,777,216 (2^24) keys` in a single `Map` object.
|
||||
|
||||
```javascript
|
||||
import { CacheableMemory } from 'cacheable';
|
||||
const cache = new CacheableMemory({ lruSize: 1 }); // sets the LRU cache size to 1000 keys and hashStoreSize to 1
|
||||
cache.set('key1', 'value1');
|
||||
cache.set('key2', 'value2');
|
||||
const value1 = cache.get('key1');
|
||||
console.log(value1); // undefined if the cache is full and key1 is the least recently used
|
||||
const value2 = cache.get('key2');
|
||||
console.log(value2); // value2 if key2 is still in the cache
|
||||
console.log(cache.size()); // 1
|
||||
```
|
||||
|
||||
NOTE: if you set the `lruSize` property to `0` after it was enabled it will disable the LRU cache feature and will not limit the number of keys in the cache. This will remove the `16,777,216 (2^24) keys` limit of a single `Map` object and will allow you to store more keys in the cache.
|
||||
|
||||
## CacheableMemory Performance
|
||||
|
||||
Our goal with `cacheable` and `CacheableMemory` is to provide a high performance caching engine that is simple to use and has a robust API. We test it against other cacheing engines such that are less feature rich to make sure there is little difference. Here are some of the benchmarks we have run:
|
||||
|
||||
*Memory Benchmark Results:*
|
||||
| name | summary | ops/sec | time/op | margin | samples |
|
||||
|------------------------------------------|:---------:|----------:|----------:|:--------:|----------:|
|
||||
| Cacheable Memory (v1.10.0) - set / get | 🥇 | 152K | 7µs | ±0.94% | 147K |
|
||||
| Map (v22) - set / get | -1.1% | 151K | 7µs | ±0.69% | 145K |
|
||||
| Node Cache - set / get | -4.3% | 146K | 7µs | ±1.13% | 142K |
|
||||
| bentocache (v1.4.0) - set / get | -20% | 121K | 8µs | ±0.40% | 119K |
|
||||
|
||||
*Memory LRU Benchmark Results:*
|
||||
| name | summary | ops/sec | time/op | margin | samples |
|
||||
|------------------------------------------|:---------:|----------:|----------:|:--------:|----------:|
|
||||
| quick-lru (v7.0.1) - set / get | 🥇 | 118K | 9µs | ±0.85% | 112K |
|
||||
| Map (v22) - set / get | -0.56% | 117K | 9µs | ±1.35% | 110K |
|
||||
| lru.min (v1.1.2) - set / get | -1.7% | 116K | 9µs | ±0.90% | 110K |
|
||||
| Cacheable Memory (v1.10.0) - set / get | -3.3% | 114K | 9µs | ±1.16% | 108K |
|
||||
|
||||
As you can see from the benchmarks `CacheableMemory` is on par with other caching engines such as `Map`, `Node Cache`, and `bentocache`. We have also tested it against other LRU caching engines such as `quick-lru` and `lru.min` and it performs well against them too.
|
||||
|
||||
## CacheableMemory Options
|
||||
|
||||
* `ttl`: The time to live for the cache in milliseconds. Default is `undefined` which is means indefinitely.
|
||||
* `useClones`: If the cache should use clones for the values. Default is `true`.
|
||||
* `lruSize`: The size of the LRU cache. Default is `0` which is unlimited.
|
||||
* `checkInterval`: The interval to check for expired keys in milliseconds. Default is `0` which is disabled.
|
||||
* `storeHashSize`: The number of `Map` objects to use for the cache. Default is `16`.
|
||||
* `storeHashAlgorithm`: The hashing algorithm to use for the cache. Default is `djb2`. Supported: DJB2, FNV1, MURMER, CRC32.
|
||||
|
||||
## CacheableMemory - API
|
||||
|
||||
* `set(key, value, ttl?)`: Sets a value in the cache.
|
||||
* `setMany([{key, value, ttl?}])`: Sets multiple values in the cache from `CacheableItem`.
|
||||
* `get(key)`: Gets a value from the cache.
|
||||
* `getMany([keys])`: Gets multiple values from the cache.
|
||||
* `getRaw(key)`: Gets a value from the cache as `CacheableStoreItem`.
|
||||
* `getManyRaw([keys])`: Gets multiple values from the cache as `CacheableStoreItem`.
|
||||
* `has(key)`: Checks if a value exists in the cache.
|
||||
* `hasMany([keys])`: Checks if multiple values exist in the cache.
|
||||
* `delete(key)`: Deletes a value from the cache.
|
||||
* `deleteMany([keys])`: Deletes multiple values from the cache.
|
||||
* `take(key)`: Takes a value from the cache and deletes it.
|
||||
* `takeMany([keys])`: Takes multiple values from the cache and deletes them.
|
||||
* `wrap(function, WrapSyncOptions)`: Wraps a `sync` function in a cache.
|
||||
* `clear()`: Clears the cache.
|
||||
* `ttl`: The default time to live for the cache in milliseconds. Default is `undefined` which is disabled.
|
||||
* `useClones`: If the cache should use clones for the values. Default is `true`.
|
||||
* `lruSize`: The size of the LRU cache. Default is `0` which is unlimited.
|
||||
* `size`: The number of keys in the cache.
|
||||
* `checkInterval`: The interval to check for expired keys in milliseconds. Default is `0` which is disabled.
|
||||
* `storeHashSize`: The number of `Map` objects to use for the cache. Default is `16`.
|
||||
* `storeHashAlgorithm`: The hashing algorithm to use for the cache. Default is `djb2`. Supported: DJB2, FNV1, MURMER, CRC32.
|
||||
* `keys`: Get the keys in the cache. Not able to be set.
|
||||
* `items`: Get the items in the cache as `CacheableStoreItem` example `{ key, value, expires? }`.
|
||||
* `store`: The hash store for the cache which is an array of `Map` objects.
|
||||
* `checkExpired()`: Checks for expired keys in the cache. This is used by the `checkInterval` property.
|
||||
* `startIntervalCheck()`: Starts the interval check for expired keys if `checkInterval` is above 0 ms.
|
||||
* `stopIntervalCheck()`: Stops the interval check for expired keys.
|
||||
|
||||
# Keyv Storage Adapter - KeyvCacheableMemory
|
||||
|
||||
`cacheable` comes with a built-in storage adapter for Keyv called `KeyvCacheableMemory`. This takes `CacheableMemory` and creates a storage adapter for Keyv. This is useful if you want to use `CacheableMemory` as a storage adapter for Keyv. Here is an example of how to use `KeyvCacheableMemory`:
|
||||
|
||||
```javascript
|
||||
import { Keyv } from 'keyv';
|
||||
import { KeyvCacheableMemory } from 'cacheable';
|
||||
|
||||
const keyv = new Keyv({ store: new KeyvCacheableMemory() });
|
||||
await keyv.set('foo', 'bar');
|
||||
const value = await keyv.get('foo');
|
||||
console.log(value); // bar
|
||||
```
|
||||
|
||||
# Wrap / Memoization for Sync and Async Functions
|
||||
|
||||
`CacheableMemory` has a feature called `wrap` that allows you to wrap a function in a cache. This is useful for memoization and caching the results of a function. You can wrap a `sync` function in a cache. Here is an example of how to use the `wrap` function:
|
||||
|
||||
```javascript
|
||||
import { CacheableMemory } from 'cacheable';
|
||||
const syncFunction = (value: number) => {
|
||||
return value * 2;
|
||||
};
|
||||
|
||||
const cache = new CacheableMemory();
|
||||
const wrappedFunction = cache.wrap(syncFunction, { ttl: '1h', key: 'syncFunction' });
|
||||
console.log(wrappedFunction(2)); // 4
|
||||
console.log(wrappedFunction(2)); // 4 from cache
|
||||
```
|
||||
|
||||
In this example we are wrapping a `sync` function in a cache with a `ttl` of `1 hour`. This will cache the result of the function for `1 hour` and then expire the value. You can also set the `key` property in the `wrap()` options to set a custom key for the cache.
|
||||
|
||||
When an error occurs in the function it will not cache the value and will return the error. This is useful if you want to cache the results of a function but not cache the error. If you want it to cache the error you can set the `cacheError` property to `true` in the `wrap()` options. This is disabled by default.
|
||||
|
||||
```javascript
|
||||
import { CacheableMemory } from 'cacheable';
|
||||
const syncFunction = (value: number) => {
|
||||
throw new Error('error');
|
||||
};
|
||||
|
||||
const cache = new CacheableMemory();
|
||||
const wrappedFunction = cache.wrap(syncFunction, { ttl: '1h', key: 'syncFunction', cacheError: true });
|
||||
console.log(wrappedFunction()); // error
|
||||
console.log(wrappedFunction()); // error from cache
|
||||
```
|
||||
|
||||
If you would like to generate your own key for the wrapped function you can set the `createKey` property in the `wrap()` options. This is useful if you want to generate a key based on the arguments of the function or any other criteria.
|
||||
|
||||
```javascript
|
||||
const cache = new CacheableMemory();
|
||||
const options: WrapOptions = {
|
||||
cache,
|
||||
keyPrefix: 'test',
|
||||
createKey: (function_, arguments_, options: WrapOptions) => `customKey:${options?.keyPrefix}:${arguments_[0]}`,
|
||||
};
|
||||
|
||||
const wrapped = wrap((argument: string) => `Result for ${argument}`, options);
|
||||
|
||||
const result1 = wrapped('arg1');
|
||||
const result2 = wrapped('arg1'); // Should hit the cache
|
||||
|
||||
console.log(result1); // Result for arg1
|
||||
console.log(result2); // Result for arg1 (from cache)
|
||||
```
|
||||
|
||||
We will pass in the `function` that is being wrapped, the `arguments` passed to the function, and the `options` used to wrap the function. You can then use these to generate a custom key for the cache.
|
||||
|
||||
# How to Contribute
|
||||
|
||||
You can contribute by forking the repo and submitting a pull request. Please make sure to add tests and update the documentation. To learn more about how to contribute go to our main README [https://github.com/jaredwray/cacheable](https://github.com/jaredwray/cacheable). This will talk about how to `Open a Pull Request`, `Ask a Question`, or `Post an Issue`.
|
||||
|
||||
# License and Copyright
|
||||
[MIT © Jared Wray](./LICENSE)
|
||||
Generated
Vendored
-69
@@ -1,69 +0,0 @@
|
||||
{
|
||||
"name": "@cacheable/memory",
|
||||
"version": "2.0.8",
|
||||
"description": "High Performance In-Memory Cache for Node.js",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/index.d.cts",
|
||||
"default": "./dist/index.cjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jaredwray/cacheable.git",
|
||||
"directory": "packages/cacheable"
|
||||
},
|
||||
"author": "Jared Wray <me@jaredwray.com>",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^10.3.0",
|
||||
"@types/node": "^25.3.0",
|
||||
"rimraf": "^6.1.3",
|
||||
"tsup": "^8.5.1",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@keyv/bigmap": "^1.3.1",
|
||||
"hookified": "^1.15.1",
|
||||
"keyv": "^5.6.0",
|
||||
"@cacheable/utils": "^2.4.0"
|
||||
},
|
||||
"keywords": [
|
||||
"cacheable",
|
||||
"high performance",
|
||||
"distributed caching",
|
||||
"Keyv storage engine",
|
||||
"keyv",
|
||||
"memory caching",
|
||||
"LRU cache",
|
||||
"memory",
|
||||
"in-memory",
|
||||
"scalable cache",
|
||||
"in-memory cache",
|
||||
"lruSize",
|
||||
"lru"
|
||||
],
|
||||
"files": [
|
||||
"dist",
|
||||
"LICENSE"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean",
|
||||
"prepublish": "pnpm build",
|
||||
"lint": "biome check --write --error-on-warnings",
|
||||
"test": "pnpm lint && vitest run --coverage",
|
||||
"test:ci": "biome check --error-on-warnings && vitest run --coverage",
|
||||
"clean": "rimraf ./dist ./coverage ./node_modules"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
-19
@@ -1,19 +0,0 @@
|
||||
MIT License & © Jared Wray
|
||||
|
||||
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.
|
||||
Generated
Vendored
-352
@@ -1,352 +0,0 @@
|
||||
[<img align="center" src="https://cacheable.org/symbol.svg" alt="Cacheable" />](https://github.com/jaredwray/cacheable)
|
||||
|
||||
# Node-Cache
|
||||
|
||||
> Simple and Maintained fast Node.js caching
|
||||
|
||||
[](https://codecov.io/gh/jaredwray/cacheable)
|
||||
[](https://github.com/jaredwray/cacheable/actions/workflows/tests.yml)
|
||||
[](https://www.npmjs.com/package/@cacheable/node-cache)
|
||||
[](https://www.npmjs.com/package/@cacheable/node-cache)
|
||||
[](https://github.com/jaredwray/cacheable/blob/main/LICENSE)
|
||||
|
||||
`@cacheable/node-cache` is compatible with the [node-cache](https://www.npmjs.com/package/node-cache) package with regular maintenance and additional functionality (async/await and storage adapters). The only thing not implemented is the `enableLegacyCallbacks` option and functions. If you need them we are happy to take a PR to add them.
|
||||
|
||||
* Fully Compatible with `node-cache` using `{NodeCache}`
|
||||
* Faster than the original `node-cache` package 🚀
|
||||
* Async/Await functionality with `{NodeCacheStore}`
|
||||
* Storage Adapters via [Keyv](https://keyv.org) with `{NodeCacheStore}`
|
||||
* Maintained and Updated Regularly! 🎉
|
||||
|
||||
# Table of Contents
|
||||
* [Getting Started](#getting-started)
|
||||
* [Basic Usage](#basic-usage)
|
||||
* [NodeCache Performance](#nodecache-performance)
|
||||
* [NodeCache API](#nodecache-api)
|
||||
* [NodeCacheStore](#nodecachestore)
|
||||
* [NodeCacheStore API](#nodecachestore-api)
|
||||
* [How to Contribute](#how-to-contribute)
|
||||
* [License and Copyright](#license-and-copyright)
|
||||
|
||||
# Getting Started
|
||||
|
||||
```bash
|
||||
npm install @cacheable/node-cache --save
|
||||
```
|
||||
|
||||
# Basic Usage
|
||||
|
||||
```javascript
|
||||
import NodeCache from '@cacheable/node-cache';
|
||||
|
||||
const cache = new NodeCache();
|
||||
cache.set('foo', 'bar');
|
||||
cache.get('foo'); // 'bar'
|
||||
|
||||
cache.set('foo', 'bar', 10); // 10 seconds
|
||||
|
||||
cache.del('foo'); // true
|
||||
|
||||
cache.set('bar', 'baz', '35m'); // 35 minutes using shorthand
|
||||
```
|
||||
|
||||
The `NodeCache` is not the default export, so you need to import it like this:
|
||||
|
||||
```javascript
|
||||
import {NodeCache} from '@cacheable/node-cache';
|
||||
|
||||
const cache = new NodeCache();
|
||||
cache.set('foo', 'bar');
|
||||
cache.get('foo'); // 'bar'
|
||||
```
|
||||
|
||||
`NodeCache` also offers the ability to set the type of values that can be cached in Typescript environments.
|
||||
|
||||
```typescript
|
||||
import {NodeCache} from '@cacheable/node-cache';
|
||||
|
||||
const cache = new NodeCache<string>();
|
||||
cache.set('foo', 'bar');
|
||||
cache.get('foo'); // 'bar'
|
||||
```
|
||||
|
||||
# NodeCache Performance
|
||||
|
||||
The performance is comparable if not faster to the original `node-cache` package, but with additional features and improvements.
|
||||
|
||||
| name | summary | ops/sec | time/op | margin | samples |
|
||||
|-----------------------------------|:---------:|----------:|----------:|:--------:|----------:|
|
||||
| Cacheable NodeCache - set / get | 🥇 | 117K | 9µs | ±1.01% | 111K |
|
||||
| Node Cache - set / get | -4.6% | 112K | 9µs | ±1.31% | 106K |
|
||||
|
||||
# NodeCache API
|
||||
|
||||
## `constructor(options?: NodeCacheOptions)`
|
||||
|
||||
Create a new cache instance. You can pass in options to set the configuration:
|
||||
|
||||
```javascript
|
||||
export type NodeCacheOptions = {
|
||||
stdTTL?: number;
|
||||
checkperiod?: number;
|
||||
useClones?: boolean;
|
||||
deleteOnExpire?: boolean;
|
||||
maxKeys?: number;
|
||||
};
|
||||
```
|
||||
|
||||
Here is a description of the options:
|
||||
|
||||
| Option | Default Setting | Description |
|
||||
|--------|----------------|-------------|
|
||||
| `stdTTL` | `0` | The standard time to live (TTL) in seconds for every generated cache element. If set to `0`, it means unlimited. If a string is provided, it will be parsed as shorthand and default to milliseconds if it is a number as a string. |
|
||||
| `checkperiod` | `600` | The interval in seconds to check for expired keys. If set to `0`, it means no periodic check will be performed. |
|
||||
| `useClones` | `true` | If set to `true`, the cache will clone the returned items via `get()` functions. This means that every time you set a value into the cache, `node-cache` makes a deep clone of it. When you get that value back, you receive another deep clone. This mimics the behavior of an external cache like Redis or Memcached, meaning mutations to the returned object do not affect the cached copy (and vice versa). If set to `false`, the original object will be returned, and mutations will affect the cached copy. |
|
||||
| `deleteOnExpire` | `true` | If set to `true`, the key will be deleted when it expires. If set to `false`, the key will remain in the cache, but the value returned by `get()` will be `undefined`. You can manage the key with the `on('expired')` event. |
|
||||
| `maxKeys` | `-1` | If set to a positive number, it will limit the number of keys in the cache. If the number of keys exceeds this limit, it will throw an error when trying to set more keys than the maximum. If set to `-1`, it means unlimited keys are allowed. |
|
||||
|
||||
When initializing the cache you can pass in the options to set the configuration like the example below where we set the `stdTTL` to 10 seconds and `checkperiod` to 5 seconds.:
|
||||
|
||||
```javascript
|
||||
const cache = new NodeCache({stdTTL: 10, checkperiod: 5});
|
||||
```
|
||||
|
||||
When setting `deleteOnExpire` to `true` it will delete the key when it expires. If you set it to `false` it will keep the key but the value on `get()` will be `undefined`. You can manage the key with `on('expired')` event.
|
||||
|
||||
```javascript
|
||||
const cache = new NodeCache({deleteOnExpire: false});
|
||||
cache.on('expired', (key, value) => {
|
||||
console.log(`Key ${key} has expired with value ${value}`);
|
||||
});
|
||||
```
|
||||
|
||||
## `set(key: string | number, value: any, ttl?: number): boolean`
|
||||
|
||||
Set a key value pair with an optional ttl (in seconds). Will return true on success. If the ttl is not set it will default to 0 (no ttl).
|
||||
|
||||
```javascript
|
||||
cache.set('foo', 'bar', 10); // true
|
||||
```
|
||||
|
||||
## `mset(data: Array<NodeCacheItem>): boolean`
|
||||
|
||||
Set multiple key value pairs at once. This will take an array of objects with the key, value, and optional ttl.
|
||||
|
||||
```javascript
|
||||
cache.mset([{key: 'foo', value: 'bar', ttl: 10}, {key: 'bar', value: 'baz'}]); // true
|
||||
```
|
||||
|
||||
the `NodeCacheItem` is defined as:
|
||||
|
||||
```javascript
|
||||
export type NodeCacheItem = {
|
||||
key: string;
|
||||
value: any;
|
||||
ttl?: number;
|
||||
};
|
||||
```
|
||||
|
||||
## `get<T>(key: string | number): T | undefined`
|
||||
|
||||
Get a value from the cache by key. If the key does not exist it will return `undefined`.
|
||||
|
||||
```javascript
|
||||
cache.get('foo'); // 'bar'
|
||||
```
|
||||
|
||||
## `mget<T>(keys: Array<string | number>): Record<string, T | undefined>`
|
||||
|
||||
Get multiple values from the cache by keys. This will return an object with the keys and values.
|
||||
|
||||
```javascript
|
||||
const obj = { my: 'value', my2: 'value2' };
|
||||
const obj2 = { special: 'value3', life: 'value4' };
|
||||
cache.set('my', obj);
|
||||
cache.set('my2', obj2);
|
||||
cache.mget(['my', 'my2']); // { my: { my: 'value', my2: 'value2' }, my2: { special: 'value3', life: 'value4' } }
|
||||
```
|
||||
|
||||
## `take<T>(key: string | number): T | undefined`
|
||||
|
||||
Get a value from the cache by key and delete it. If the key does not exist it will return `undefined`.
|
||||
|
||||
```javascript
|
||||
cache.set('foo', 'bar');
|
||||
cache.take('foo'); // 'bar'
|
||||
cache.get('foo'); // undefined
|
||||
```
|
||||
|
||||
## `del(key: string | number | Array<string | number>): number`
|
||||
|
||||
Delete a key from the cache. Will return the number of deleted entries and never fail. You can also pass in an array of keys to delete multiple keys. All examples assume that you have initialized the cache like `const cache = new NodeCache();`.
|
||||
|
||||
```javascript
|
||||
cache.del('foo'); // true
|
||||
```
|
||||
|
||||
passing in an array of keys:
|
||||
|
||||
```javascript
|
||||
cache.del(['foo', 'bar']); // true
|
||||
```
|
||||
|
||||
## `mdel(keys: Array<string | number>): number`
|
||||
|
||||
Delete multiple keys from the cache. Will return the number of deleted entries and never fail.
|
||||
|
||||
```javascript
|
||||
cache.mdel(['foo', 'bar']); // true
|
||||
```
|
||||
|
||||
## `ttl(key: string | number, ttl?: number): boolean`
|
||||
|
||||
Redefine the ttl of a key. Returns true if the key has been found and changed. Otherwise returns false. If the ttl-argument isn't passed the default-TTL will be used.
|
||||
|
||||
```javascript
|
||||
cache.ttl('foo', 10); // true
|
||||
```
|
||||
|
||||
## `getTtl(key: string | number): number | undefined`
|
||||
|
||||
Get the ttl expiration from `Date.now()` of a key. If the key does not exist it will return `undefined`.
|
||||
|
||||
```javascript
|
||||
cache.getTtl('foo'); // 1725993344859
|
||||
```
|
||||
|
||||
## `has(key: string | number): boolean`
|
||||
|
||||
Check if a key exists in the cache.
|
||||
|
||||
```javascript
|
||||
cache.set('foo', 'bar');
|
||||
cache.has('foo'); // true
|
||||
```
|
||||
|
||||
## `keys(): string[]`
|
||||
|
||||
Get all keys from the cache.
|
||||
|
||||
```javascript
|
||||
await cache.keys(); // ['foo', 'bar']
|
||||
```
|
||||
|
||||
## `getStats(): NodeCacheStats`
|
||||
|
||||
Get the stats of the cache.
|
||||
|
||||
```javascript
|
||||
cache.getStats(); // {hits: 1, misses: 1, keys: 1, ksize: 2, vsize: 3}
|
||||
```
|
||||
|
||||
## `flushAll(): void`
|
||||
|
||||
Flush the cache. Will remove all keys and reset the stats.
|
||||
|
||||
```javascript
|
||||
cache.flushAll();
|
||||
await cache.keys(); // []
|
||||
cache.getStats(); // {hits: 0, misses: 0, keys: 0, ksize: 0, vsize: 0}
|
||||
```
|
||||
|
||||
## `flushStats(): void`
|
||||
|
||||
Flush the stats. Will reset the stats but keep the keys.
|
||||
|
||||
```javascript
|
||||
await cache.set('foo', 'bar');
|
||||
cache.flushStats();
|
||||
cache.getStats(); // {hits: 0, misses: 0, keys: 0, ksize: 0, vsize: 0}
|
||||
await cache.keys(); // ['foo']
|
||||
```
|
||||
|
||||
## `on(event: string, callback: Function): void`
|
||||
|
||||
Listen to events. Here are the events that you can listen to:
|
||||
* `set` - when a key is set and it will pass in the `key` and `value`.
|
||||
* `expired` - when a key is expired and it will pass in the `key` and `value`.
|
||||
* `flush` - when the cache is flushed
|
||||
* `flush_stats` - when the stats are flushed
|
||||
* `del` - when a key is deleted and it will pass in the `key` and `value`.
|
||||
|
||||
```javascript
|
||||
cache.on('set', (key, value) => {
|
||||
console.log(`Key ${key} has been set with value ${value}`);
|
||||
});
|
||||
```
|
||||
|
||||
# NodeCacheStore
|
||||
|
||||
`NodeCacheStore` has a similar API to `NodeCache` but it is using `async / await` as it uses the `Keyv` storage adapters under the hood. This means that you can use all the storage adapters that are available in `Keyv` and it will work seamlessly with the `NodeCacheStore`. To learn more about the `Keyv` storage adapters you can check out the [Keyv documentation](https://keyv.org).
|
||||
|
||||
```javascript
|
||||
import {NodeCacheStore} from '@cacheable/node-cache';
|
||||
|
||||
const cache = new NodeCacheStore();
|
||||
await cache.set('foo', 'bar');
|
||||
await cache.get('foo'); // 'bar'
|
||||
```
|
||||
|
||||
Here is an example of how to use the `NodeCacheStore` with a primary and secondary storage adapter:
|
||||
|
||||
```javascript
|
||||
import {NodeCacheStore} from '@cacheable/node-cache';
|
||||
import {Keyv} from 'keyv';
|
||||
import {KeyvRedis} from '@keyv/redis';
|
||||
|
||||
const primary = new Keyv(); // In-memory storage as primary
|
||||
const secondary = new Keyv({store: new KeyvRedis('redis://user:pass@localhost:6379')});
|
||||
const cache = new NodeCacheStore({primary, secondary});
|
||||
|
||||
// with storage you have the same functionality as the NodeCache but will be using async/await
|
||||
await cache.set('foo', 'bar');
|
||||
await cache.get('foo'); // 'bar'
|
||||
|
||||
// if you call getStats() this will now only be for the single instance of the adapter as it is in memory
|
||||
cache.getStats(); // {hits: 1, misses: 1, keys: 1, ksize: 2, vsize: 3}
|
||||
```
|
||||
|
||||
When initializing the cache you can pass in the options below:
|
||||
|
||||
```javascript
|
||||
export type NodeCacheStoreOptions = {
|
||||
ttl?: number; // The standard ttl as number in milliseconds for every generated cache element. 0 = unlimited
|
||||
primary?: Keyv; // The primary storage adapter
|
||||
secondary?: Keyv; // The secondary storage adapter
|
||||
maxKeys?: number; // Default is 0 (unlimited). If this is set it will throw and error if you try to set more keys than the max.
|
||||
stats?: boolean; // Default is true, if this is set to false it will not track stats
|
||||
};
|
||||
```
|
||||
|
||||
Note: the `ttl` is now in milliseconds and not seconds like `stdTTL` in `NodeCache`. You can learn more about using shorthand also in the [cacheable documentation](https://github.com/jaredwray/cacheable/blob/main/packages/cacheable/README.md#shorthand-for-time-to-live-ttl) as it is fully supported. Here is an example:
|
||||
|
||||
```javascript
|
||||
const cache = new NodeCacheStore({ttl: 60000 }); // 1 minute as it defaults to milliseconds
|
||||
await cache.set('foo', 'bar', '1h'); // 1 hour
|
||||
await cache.set('longfoo', 'bar', '1d'); // 1 day
|
||||
```
|
||||
|
||||
## NodeCacheStore API
|
||||
|
||||
* `set(key: string | number, value: any, ttl?: number): Promise<boolean>` - Set a key value pair with an optional ttl (in milliseconds). Will return true on success. If the ttl is not set it will default to 0 (no ttl)
|
||||
* `mset(data: Array<NodeCacheItem>): Promise<boolean>` - Set multiple key value pairs at once
|
||||
* `get<T>(key: string | number): Promise<T>` - Get a value from the cache by key
|
||||
* `mget(keys: Array<string | number>): Promise<Record<string, unknown>>` - Get multiple values from the cache by keys
|
||||
* `take<T>(key: string | number): Promise<T>` - Get a value from the cache by key and delete it
|
||||
* `del(key: string | number): Promise<boolean>` - Delete a key
|
||||
* `mdel(keys: Array<string | number>): Promise<boolean>` - Delete multiple keys
|
||||
* `clear(): Promise<void>` - Clear the cache
|
||||
* `setTtl(key: string | number, ttl: number): Promise<boolean>` - Set the ttl of a key
|
||||
* `disconnect(): Promise<void>` - Disconnect the storage adapters
|
||||
* `stats`: `NodeCacheStats` - Get the stats of the cache
|
||||
* `ttl`: `number` | `string` - The standard ttl as number in seconds for every generated cache element. `< 0` or `undefined` = unlimited
|
||||
* `primary`: `Keyv` - The primary storage adapter
|
||||
* `secondary`: `Keyv` - The secondary storage adapter
|
||||
* `maxKeys`: `number` - If this is set it will throw and error if you try to set more keys than the max
|
||||
|
||||
# How to Contribute
|
||||
|
||||
You can contribute by forking the repo and submitting a pull request. Please make sure to add tests and update the documentation. To learn more about how to contribute go to our main README [https://github.com/jaredwray/cacheable](https://github.com/jaredwray/cacheable). This will talk about how to `Open a Pull Request`, `Ask a Question`, or `Post an Issue`.
|
||||
|
||||
# License and Copyright
|
||||
[MIT © Jared Wray](./LICENSE)
|
||||
Generated
Vendored
-69
@@ -1,69 +0,0 @@
|
||||
{
|
||||
"name": "@cacheable/node-cache",
|
||||
"version": "1.7.6",
|
||||
"description": "Simple and Maintained fast NodeJS internal caching",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/index.d.cts",
|
||||
"default": "./dist/index.cjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jaredwray/cacheable.git",
|
||||
"directory": "packages/node-cache"
|
||||
},
|
||||
"author": "Jared Wray <me@jaredwray.com>",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"keywords": [
|
||||
"cache",
|
||||
"caching",
|
||||
"node",
|
||||
"nodejs",
|
||||
"cacheable",
|
||||
"cacheable-node-cache",
|
||||
"node-cache",
|
||||
"cacheable-node"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.3.8",
|
||||
"@faker-js/faker": "^10.1.0",
|
||||
"@types/node": "^25.0.2",
|
||||
"@vitest/coverage-v8": "^4.0.15",
|
||||
"rimraf": "^6.1.2",
|
||||
"tsup": "^8.5.1",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^4.0.15"
|
||||
},
|
||||
"dependencies": {
|
||||
"hookified": "^1.14.0",
|
||||
"keyv": "^5.5.5",
|
||||
"cacheable": "^2.3.1"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"license"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean",
|
||||
"prepublish": "pnpm build",
|
||||
"lint": "biome check --write --error-on-warnings",
|
||||
"test": "pnpm lint && vitest run --coverage",
|
||||
"test:ci": "biome check --error-on-warnings && vitest run --coverage",
|
||||
"clean": "rimraf ./dist ./coverage ./node_modules"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
-19
@@ -1,19 +0,0 @@
|
||||
MIT License & © Jared Wray
|
||||
|
||||
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.
|
||||
Generated
Vendored
-520
@@ -1,520 +0,0 @@
|
||||
[<img align="center" src="https://cacheable.org/logo.svg" alt="Cacheable" />](https://github.com/jaredwray/cacheable)
|
||||
|
||||
> Cacheble Utils
|
||||
|
||||
[](https://codecov.io/gh/jaredwray/cacheable)
|
||||
[](https://github.com/jaredwray/cacheable/actions/workflows/tests.yml)
|
||||
[](https://www.npmjs.com/package/@cacheable/utils)
|
||||
[](https://www.npmjs.com/package/@cacheable/utils)
|
||||
[](https://github.com/jaredwray/cacheable/blob/main/LICENSE)
|
||||
|
||||
`@cacheable/utils` is a collecton of utility functions, helpers, and types for `cacheable` and other caching libraries. It provides a robust set of features to enhance caching capabilities, including:
|
||||
|
||||
* Data Types for Caching Items
|
||||
* Hash Functions for Key Generation
|
||||
* Coalesce Async for Handling Multiple Promises
|
||||
* Stats Helpers for Caching Statistics
|
||||
* Sleep / Delay for Testing and Timing
|
||||
* Memoization for wraping or get / set options
|
||||
* Time to Live (TTL) Helpers
|
||||
|
||||
# Table of Contents
|
||||
* [Getting Started](#getting-started)
|
||||
* [Cacheable Types](#cacheable-types)
|
||||
* [Coalesce Async](#coalesce-async)
|
||||
* [Hash Functions](#hash-functions)
|
||||
* [Shorthand Time Helpers](#shorthand-time-helpers)
|
||||
* [Sleep Helper](#sleep-helper)
|
||||
* [Stats Helpers](#stats-helpers)
|
||||
* [Time to Live (TTL) Helpers](#time-to-live-ttl-helpers)
|
||||
* [Run if Function Helper](#run-if-function-helper)
|
||||
* [Less Than Helper](#less-than-helper)
|
||||
* [Is Object Helper](#is-object-helper)
|
||||
* [Wrap / Memoization for Sync and Async Functions](#wrap--memoization-for-sync-and-async-functions)
|
||||
* [Get Or Set Memoization Function](#get-or-set-memoization-function)
|
||||
* [How to Contribute](#how-to-contribute)
|
||||
* [License and Copyright](#license-and-copyright)
|
||||
|
||||
# Getting Started
|
||||
|
||||
```bash
|
||||
npm install @cacheable/utils --save
|
||||
```
|
||||
|
||||
# Cacheable Types
|
||||
|
||||
The `@cacheable/utils` package provides various types that are used throughout the caching library. These types help in defining the structure of cached items, ensuring type safety and consistency across your caching operations.
|
||||
|
||||
```typescript
|
||||
|
||||
/**
|
||||
* CacheableItem
|
||||
* @typedef {Object} CacheableItem
|
||||
* @property {string} key - The key of the cacheable item
|
||||
* @property {any} value - The value of the cacheable item
|
||||
* @property {number|string} [ttl] - Time to Live - If you set a number it is miliseconds, if you set a string it is a human-readable
|
||||
* format such as `1s` for 1 second or `1h` for 1 hour. Setting undefined means that it will use the default time-to-live. If both are
|
||||
* undefined then it will not have a time-to-live.
|
||||
*/
|
||||
export type CacheableItem = {
|
||||
key: string;
|
||||
value: any;
|
||||
ttl?: number | string;
|
||||
};
|
||||
|
||||
/**
|
||||
* CacheableStoreItem
|
||||
* @typedef {Object} CacheableStoreItem
|
||||
* @property {string} key - The key of the cacheable store item
|
||||
* @property {any} value - The value of the cacheable store item
|
||||
* @property {number} [expires] - The expiration time in milliseconds since epoch. If not set, the item does not expire.
|
||||
*/
|
||||
export type CacheableStoreItem = {
|
||||
key: string;
|
||||
value: any;
|
||||
expires?: number;
|
||||
};
|
||||
```
|
||||
|
||||
# Coalesce Async
|
||||
|
||||
The `coalesceAsync` function is a utility that allows you to handle multiple asynchronous operations efficiently. It was designed by `Douglas Cayers` https://github.com/douglascayers/promise-coalesce. It helps in coalescing multiple promises into a single promise, ensuring that only one operation is executed at a time for the same key.
|
||||
|
||||
```typescript
|
||||
import { coalesceAsync } from '@cacheable/utils';
|
||||
|
||||
const fetchData = async (key: string) => {
|
||||
// Simulate an asynchronous operation
|
||||
return new Promise((resolve) => setTimeout(() => resolve(`Data for ${key}`), 1000));
|
||||
};
|
||||
|
||||
const result = await Promise.all([
|
||||
coalesceAsync('my-key', fetchData),
|
||||
coalesceAsync('my-key', fetchData),
|
||||
coalesceAsync('my-key', fetchData),
|
||||
]);
|
||||
console.log(result); // Data for my-key only executed once
|
||||
```
|
||||
|
||||
# Hash Functions
|
||||
|
||||
The `@cacheable/utils` package provides hash functions that can be used to generate unique keys for caching operations. These functions are useful for creating consistent and unique identifiers for cached items.
|
||||
|
||||
The hashing API provides both **async** (for cryptographic algorithms) and **sync** (for non-cryptographic algorithms) methods.
|
||||
|
||||
## Async Hashing (Cryptographic Algorithms)
|
||||
|
||||
Use `hash()` and `hashToNumber()` for cryptographic algorithms like SHA-256, SHA-384, and SHA-512:
|
||||
|
||||
```typescript
|
||||
import { hash, hashToNumber, HashAlgorithm } from '@cacheable/utils';
|
||||
|
||||
// Hash using SHA-256 (default)
|
||||
const key = await hash('my-cache-key');
|
||||
console.log(key); // Unique hash for 'my-cache-key'
|
||||
|
||||
// Hash with specific algorithm
|
||||
const sha512Hash = await hash('my-data', { algorithm: HashAlgorithm.SHA512 });
|
||||
|
||||
// Convert hash to number within range
|
||||
const min = 0;
|
||||
const max = 10;
|
||||
const result = await hashToNumber({foo: 'bar'}, { min, max, algorithm: HashAlgorithm.SHA256 });
|
||||
console.log(result); // A number between 0 and 10 based on the hash value
|
||||
```
|
||||
|
||||
## Sync Hashing (Non-Cryptographic Algorithms)
|
||||
|
||||
Use `hashSync()` and `hashToNumberSync()` for faster, non-cryptographic algorithms like DJB2, FNV1, MURMER, and CRC32:
|
||||
|
||||
```typescript
|
||||
import { hashSync, hashToNumberSync, HashAlgorithm } from '@cacheable/utils';
|
||||
|
||||
// Hash using DJB2 (default for sync)
|
||||
const key = hashSync('my-cache-key');
|
||||
console.log(key); // Unique hash for 'my-cache-key'
|
||||
|
||||
// Hash with specific algorithm
|
||||
const fnv1Hash = hashSync('my-data', { algorithm: HashAlgorithm.FNV1 });
|
||||
|
||||
// Convert hash to number within range
|
||||
const min = 0;
|
||||
const max = 10;
|
||||
const result = hashToNumberSync({foo: 'bar'}, { min, max, algorithm: HashAlgorithm.DJB2 });
|
||||
console.log(result); // A number between 0 and 10 based on the hash value
|
||||
```
|
||||
|
||||
## Available Hash Algorithms
|
||||
|
||||
**Cryptographic (Async):**
|
||||
- `HashAlgorithm.SHA256` - SHA-256 (default for async methods)
|
||||
- `HashAlgorithm.SHA384` - SHA-384
|
||||
- `HashAlgorithm.SHA512` - SHA-512
|
||||
|
||||
**Non-Cryptographic (Sync):**
|
||||
- `HashAlgorithm.DJB2` - DJB2 (default for sync methods)
|
||||
- `HashAlgorithm.FNV1` - FNV-1
|
||||
- `HashAlgorithm.MURMER` - Murmur hash
|
||||
- `HashAlgorithm.CRC32` - CRC32
|
||||
|
||||
# Shorthand Time Helpers
|
||||
|
||||
The `@cacheable/utils` package provides a shorthand function to convert human-readable time strings into milliseconds. This is useful for setting time-to-live (TTL) values in caching operations.
|
||||
|
||||
You can also use the `shorthandToMilliseconds` function:
|
||||
|
||||
```typescript
|
||||
import { shorthandToMilliseconds } from '@cacheable/utils';
|
||||
|
||||
const milliseconds = shorthandToMilliseconds('1h');
|
||||
console.log(milliseconds); // 3600000
|
||||
```
|
||||
|
||||
You can also use the `shorthandToTime` function to get the current date plus the shorthand time:
|
||||
|
||||
```typescript
|
||||
import { shorthandToTime } from '@cacheable/utils';
|
||||
|
||||
const currentDate = new Date();
|
||||
const timeInMs = shorthandToTime('1h', currentDate);
|
||||
console.log(timeInMs); // Current date + 1 hour in milliseconds since epoch
|
||||
```
|
||||
|
||||
# Sleep Helper
|
||||
|
||||
The `sleep` function is a utility that allows you to pause execution for a specified duration. This can be useful in testing scenarios or when you need to introduce delays in your code.
|
||||
|
||||
```typescript
|
||||
import { sleep } from '@cacheable/utils';
|
||||
|
||||
await sleep(1000); // Pause for 1 second
|
||||
console.log('Execution resumed after 1 second');
|
||||
```
|
||||
|
||||
# Stats Helpers
|
||||
|
||||
The `@cacheable/utils` package provides statistics helpers that can be used to track and analyze caching operations. These helpers can be used to gather metrics such as hit rates, miss rates, and other performance-related statistics.
|
||||
|
||||
```typescript
|
||||
import { stats } from '@cacheable/utils';
|
||||
|
||||
const cacheStats = stats();
|
||||
cacheStats.incrementHits();
|
||||
console.log(cacheStats.hits); // Get the hit rate of the cache
|
||||
```
|
||||
|
||||
# Time to Live (TTL) Helpers
|
||||
|
||||
The `@cacheable/utils` package provides helpers for managing time-to-live (TTL) values for cached items.
|
||||
|
||||
You can use the `calculateTtlFromExpiration` function to calculate the TTL based on an expiration date:
|
||||
|
||||
```typescript
|
||||
import { calculateTtlFromExpiration } from '@cacheable/utils';
|
||||
|
||||
const expirationDate = new Date(Date.now() + 1000 * 60 * 5); // 5 minutes from now
|
||||
const ttl = calculateTtlFromExpiration(Date.now(), expirationDate);
|
||||
console.log(ttl); // 300000
|
||||
```
|
||||
|
||||
You can also use `getTtlFromExpires` to get the TTL from an expiration date:
|
||||
|
||||
```typescript
|
||||
import { getTtlFromExpires } from '@cacheable/utils';
|
||||
|
||||
const expirationDate = new Date(Date.now() + 1000 * 60 * 5); // 5 minutes from now
|
||||
const ttl = getTtlFromExpires(expirationDate);
|
||||
console.log(ttl); // 300000
|
||||
```
|
||||
|
||||
You can use `getCascadingTtl` to get the TTL for cascading cache operations:
|
||||
|
||||
```typescript
|
||||
import { getCascadingTtl } from '@cacheable/utils';
|
||||
const cacheableTtl = 1000 * 60 * 5; // 5 minutes
|
||||
const primaryTtl = 1000 * 60 * 2; // 2 minutes
|
||||
const secondaryTtl = 1000 * 60; // 1 minute
|
||||
const ttl = getCascadingTtl(cacheableTtl, primaryTtl, secondaryTtl);
|
||||
```
|
||||
|
||||
# Run if Function Helper
|
||||
|
||||
The `runIfFn` utility function provides a convenient way to conditionally execute functions or return values based on whether the input is a function or not. This pattern is commonly used in UI libraries and configuration systems where values can be either static or computed.
|
||||
|
||||
```typescript
|
||||
import { runIfFn } from '@cacheable/utils';
|
||||
|
||||
// Static value - returns the value as-is
|
||||
const staticValue = runIfFn('hello world');
|
||||
console.log(staticValue); // 'hello world'
|
||||
|
||||
// Function with no arguments - executes the function
|
||||
const dynamicValue = runIfFn(() => new Date().toISOString());
|
||||
console.log(dynamicValue); // Current timestamp
|
||||
|
||||
// Function with arguments - executes with provided arguments
|
||||
const sum = runIfFn((a: number, b: number) => a + b, 5, 10);
|
||||
console.log(sum); // 15
|
||||
|
||||
// Complex example with conditional logic
|
||||
const getConfig = (isDevelopment: boolean) => ({
|
||||
apiUrl: isDevelopment ? 'http://localhost:3000' : 'https://api.example.com',
|
||||
timeout: isDevelopment ? 5000 : 30000
|
||||
});
|
||||
|
||||
const config = runIfFn(getConfig, true);
|
||||
console.log(config); // { apiUrl: 'http://localhost:3000', timeout: 5000 }
|
||||
```
|
||||
|
||||
# Less Than Helper
|
||||
|
||||
The `lessThan` utility function provides a safe way to compare two values and determine if the first value is less than the second. It only performs the comparison if both values are valid numbers, returning `false` for any non-number inputs.
|
||||
|
||||
```typescript
|
||||
import { lessThan } from '@cacheable/utils';
|
||||
|
||||
// Basic number comparisons
|
||||
console.log(lessThan(1, 2)); // true
|
||||
console.log(lessThan(2, 1)); // false
|
||||
console.log(lessThan(1, 1)); // false
|
||||
|
||||
// Works with negative numbers
|
||||
console.log(lessThan(-1, 0)); // true
|
||||
console.log(lessThan(-2, -1)); // true
|
||||
|
||||
// Works with decimal numbers
|
||||
console.log(lessThan(1.5, 2.5)); // true
|
||||
console.log(lessThan(2.7, 2.7)); // false
|
||||
|
||||
// Safe handling of non-number values
|
||||
console.log(lessThan("1", 2)); // false
|
||||
console.log(lessThan(1, "2")); // false
|
||||
console.log(lessThan(null, 1)); // false
|
||||
console.log(lessThan(undefined, 1)); // false
|
||||
console.log(lessThan(NaN, 1)); // false
|
||||
|
||||
// Useful in filtering and sorting operations
|
||||
const numbers = [5, 2, 8, 1, 9];
|
||||
const lessThanFive = numbers.filter(n => lessThan(n, 5));
|
||||
console.log(lessThanFive); // [2, 1]
|
||||
|
||||
// Safe comparison in conditional logic
|
||||
function processValue(a?: number, b?: number) {
|
||||
if (lessThan(a, b)) {
|
||||
return `${a} is less than ${b}`;
|
||||
}
|
||||
return 'Invalid comparison or a >= b';
|
||||
}
|
||||
```
|
||||
|
||||
This utility is particularly useful when dealing with potentially undefined or invalid numeric values, ensuring type safety in comparison operations.
|
||||
|
||||
# Is Object Helper
|
||||
|
||||
The `isObject` utility function provides a type-safe way to determine if a value is a plain object. It returns `true` for objects but `false` for arrays, `null`, functions, and primitive types. This function also serves as a TypeScript type guard.
|
||||
|
||||
```typescript
|
||||
import { isObject } from '@cacheable/utils';
|
||||
|
||||
// Basic object detection
|
||||
console.log(isObject({})); // true
|
||||
console.log(isObject({ name: 'John', age: 30 })); // true
|
||||
console.log(isObject(Object.create(null))); // true
|
||||
|
||||
// Arrays are not considered objects
|
||||
console.log(isObject([])); // false
|
||||
console.log(isObject([1, 2, 3])); // false
|
||||
|
||||
// null is not considered an object (despite typeof null === 'object')
|
||||
console.log(isObject(null)); // false
|
||||
|
||||
// Primitive types return false
|
||||
console.log(isObject('string')); // false
|
||||
console.log(isObject(123)); // false
|
||||
console.log(isObject(true)); // false
|
||||
console.log(isObject(undefined)); // false
|
||||
|
||||
// Functions return false
|
||||
console.log(isObject(() => {})); // false
|
||||
console.log(isObject(Date)); // false
|
||||
|
||||
// Built-in object types return true
|
||||
console.log(isObject(new Date())); // true
|
||||
console.log(isObject(/regex/)); // true
|
||||
console.log(isObject(new Error('test'))); // true
|
||||
console.log(isObject(new Map())); // true
|
||||
|
||||
// TypeScript type guard usage
|
||||
function processValue(value: unknown) {
|
||||
if (isObject<{ name: string; age: number }>(value)) {
|
||||
// TypeScript now knows value is an object with name and age properties
|
||||
console.log(`Name: ${value.name}, Age: ${value.age}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Useful for configuration validation
|
||||
function validateConfig(config: unknown) {
|
||||
if (!isObject(config)) {
|
||||
throw new Error('Configuration must be an object');
|
||||
}
|
||||
|
||||
// Safe to access object properties
|
||||
return config;
|
||||
}
|
||||
|
||||
// Filtering arrays for objects only
|
||||
const mixedArray = [1, 'string', {}, [], null, { valid: true }];
|
||||
const objectsOnly = mixedArray.filter(isObject);
|
||||
console.log(objectsOnly); // [{}', { valid: true }]
|
||||
```
|
||||
|
||||
This utility is particularly useful for:
|
||||
- **Type validation** - Ensuring values are objects before accessing properties
|
||||
- **TypeScript type guarding** - Narrowing types in conditional blocks
|
||||
- **Configuration parsing** - Validating that configuration values are objects
|
||||
- **Data filtering** - Separating objects from other data types
|
||||
|
||||
# Wrap / Memoization for Sync and Async Functions
|
||||
|
||||
The `@cacheable/utils` package provides two main functions: `wrap` and `wrapSync`. These functions are used to memoize asynchronous and synchronous functions, respectively.
|
||||
|
||||
```javascript
|
||||
import { Cacheable } from 'cacheable';
|
||||
const asyncFunction = async (value: number) => {
|
||||
return Math.random() * value;
|
||||
};
|
||||
|
||||
const cache = new Cacheable();
|
||||
const options = {
|
||||
ttl: '1h', // 1 hour
|
||||
keyPrefix: 'p1', // key prefix. This is used if you have multiple functions and need to set a unique prefix.
|
||||
cache,
|
||||
}
|
||||
const wrappedFunction = wrap(asyncFunction, options);
|
||||
console.log(await wrappedFunction(2)); // 4
|
||||
console.log(await wrappedFunction(2)); // 4 from cache
|
||||
```
|
||||
With `wrap` we have also included stampede protection so that a `Promise` based call will only be called once if multiple requests of the same are executed at the same time. Here is an example of how to test for stampede protection:
|
||||
|
||||
```javascript
|
||||
import { Cacheable } from 'cacheable';
|
||||
const asyncFunction = async (value: number) => {
|
||||
return value;
|
||||
};
|
||||
|
||||
const cache = new Cacheable();
|
||||
const options = {
|
||||
ttl: '1h', // 1 hour
|
||||
keyPrefix: 'p1', // key prefix. This is used if you have multiple functions and need to set a unique prefix.
|
||||
cache,
|
||||
}
|
||||
|
||||
const wrappedFunction = wrap(asyncFunction, options);
|
||||
const promises = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
promises.push(wrappedFunction(i));
|
||||
}
|
||||
|
||||
const results = await Promise.all(promises); // all results should be the same
|
||||
|
||||
console.log(results); // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
```
|
||||
|
||||
In this example we are wrapping an `async` function in a cache with a `ttl` of `1 hour`. This will cache the result of the function for `1 hour` and then expire the value. You can also wrap a `sync` function in a cache:
|
||||
|
||||
```javascript
|
||||
import { CacheableMemory } from 'cacheable';
|
||||
const syncFunction = (value: number) => {
|
||||
return value * 2;
|
||||
};
|
||||
|
||||
const cache = new CacheableMemory();
|
||||
const wrappedFunction = wrap(syncFunction, { ttl: '1h', key: 'syncFunction', cache });
|
||||
console.log(wrappedFunction(2)); // 4
|
||||
console.log(wrappedFunction(2)); // 4 from cache
|
||||
```
|
||||
|
||||
In this example we are wrapping a `sync` function in a cache with a `ttl` of `1 hour`. This will cache the result of the function for `1 hour` and then expire the value. You can also set the `key` property in the `wrap()` options to set a custom key for the cache.
|
||||
|
||||
When an error occurs in the function it will not cache the value and will return the error. This is useful if you want to cache the results of a function but not cache the error. If you want it to cache the error you can set the `cacheError` property to `true` in the `wrap()` options. This is disabled by default.
|
||||
|
||||
```javascript
|
||||
import { CacheableMemory } from 'cacheable';
|
||||
const syncFunction = (value: number) => {
|
||||
throw new Error('error');
|
||||
};
|
||||
|
||||
const cache = new CacheableMemory();
|
||||
const wrappedFunction = wrap(syncFunction, { ttl: '1h', key: 'syncFunction', cacheError: true, cache });
|
||||
console.log(wrappedFunction()); // error
|
||||
console.log(wrappedFunction()); // error from cache
|
||||
```
|
||||
|
||||
If you would like to generate your own key for the wrapped function you can set the `createKey` property in the `wrap()` options. This is useful if you want to generate a key based on the arguments of the function or any other criteria.
|
||||
|
||||
```javascript
|
||||
const cache = new Cacheable();
|
||||
const options: WrapOptions = {
|
||||
cache,
|
||||
keyPrefix: 'test',
|
||||
createKey: (function_, arguments_, options: WrapOptions) => `customKey:${options?.keyPrefix}:${arguments_[0]}`,
|
||||
};
|
||||
|
||||
const wrapped = wrap((argument: string) => `Result for ${argument}`, options);
|
||||
|
||||
const result1 = await wrapped('arg1');
|
||||
const result2 = await wrapped('arg1'); // Should hit the cache
|
||||
|
||||
console.log(result1); // Result for arg1
|
||||
console.log(result2); // Result for arg1 (from cache)
|
||||
```
|
||||
|
||||
We will pass in the `function` that is being wrapped, the `arguments` passed to the function, and the `options` used to wrap the function. You can then use these to generate a custom key for the cache.
|
||||
|
||||
# Get Or Set Memoization Function
|
||||
|
||||
The `getOrSet` method provides a convenient way to implement the cache-aside pattern. It attempts to retrieve a value from cache, and if not found, calls the provided function to compute the value and store it in cache before returning it. Here are the options:
|
||||
|
||||
```typescript
|
||||
export type GetOrSetFunctionOptions = {
|
||||
ttl?: number | string;
|
||||
cacheErrors?: boolean;
|
||||
throwErrors?: boolean;
|
||||
nonBlocking?: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
The `nonBlocking` option allows you to override the instance-level `nonBlocking` setting for the `get` call within `getOrSet`. When set to `false`, the `get` will block and wait for a response from the secondary store before deciding whether to call the provided function. When set to `true`, the primary store returns immediately and syncs from secondary in the background.
|
||||
|
||||
Here is an example of how to use the `getOrSet` method:
|
||||
|
||||
```javascript
|
||||
import { Cacheable } from 'cacheable';
|
||||
const cache = new Cacheable();
|
||||
// Use getOrSet to fetch user data
|
||||
const function_ = async () => Math.random() * 100;
|
||||
const value = await getOrSet('randomValue', function_, { ttl: '1h', cache });
|
||||
console.log(value); // e.g. 42.123456789
|
||||
```
|
||||
|
||||
You can also use a function to compute the key for the function:
|
||||
|
||||
```javascript
|
||||
import { Cacheable, GetOrSetOptions } from 'cacheable';
|
||||
const cache = new Cacheable();
|
||||
|
||||
// Function to generate a key based on options
|
||||
const generateKey = (options?: GetOrSetOptions) => {
|
||||
return `custom_key_:${options?.cacheId || 'default'}`;
|
||||
};
|
||||
|
||||
const function_ = async () => Math.random() * 100;
|
||||
const value = await getOrSet(generateKey(), function_, { ttl: '1h', cache });
|
||||
```
|
||||
|
||||
# How to Contribute
|
||||
|
||||
You can contribute by forking the repo and submitting a pull request. Please make sure to add tests and update the documentation. To learn more about how to contribute go to our main README [https://github.com/jaredwray/cacheable](https://github.com/jaredwray/cacheable). This will talk about how to `Open a Pull Request`, `Ask a Question`, or `Post an Issue`.
|
||||
|
||||
# License and Copyright
|
||||
[MIT © Jared Wray](./LICENSE)
|
||||
Generated
Vendored
-56
@@ -1,56 +0,0 @@
|
||||
{
|
||||
"name": "@cacheable/utils",
|
||||
"version": "2.4.1",
|
||||
"description": "Cacheable Utilities for Caching Libraries",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/index.d.cts",
|
||||
"default": "./dist/index.cjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jaredwray/cacheable.git",
|
||||
"directory": "packages/utils"
|
||||
},
|
||||
"author": "Jared Wray <me@jaredwray.com>",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"dependencies": {
|
||||
"hashery": "^1.5.1",
|
||||
"keyv": "^5.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsup": "^8.5.1",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"keywords": [
|
||||
"cacheable",
|
||||
"caching",
|
||||
"utilities",
|
||||
"hashing",
|
||||
"keyv",
|
||||
"cache utils"
|
||||
],
|
||||
"files": [
|
||||
"dist",
|
||||
"LICENSE"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean",
|
||||
"lint": "biome check --write --error-on-warnings",
|
||||
"test": "pnpm lint && vitest run --coverage",
|
||||
"test:ci": "biome check --error-on-warnings && vitest run --coverage",
|
||||
"clean": "rimraf ./dist ./coverage ./node_modules"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
Copyright (c) 2012-2020, Sideway Inc, and project contributors
|
||||
Copyright (c) 2012-2014, Walmart.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* 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.
|
||||
* The names of any contributors may not 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 HOLDERS AND 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 OFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Generated
Vendored
-17
@@ -1,17 +0,0 @@
|
||||
<a href="https://hapi.dev"><img src="https://raw.githubusercontent.com/hapijs/assets/master/images/family.png" width="180px" align="right" /></a>
|
||||
|
||||
# @hapi/boom
|
||||
|
||||
#### HTTP-friendly error objects.
|
||||
|
||||
**boom** is part of the **hapi** ecosystem and was designed to work seamlessly with the [hapi web framework](https://hapi.dev) and its other components (but works great on its own or with other frameworks). If you are using a different web framework and find this module useful, check out [hapi](https://hapi.dev) – they work even better together.
|
||||
|
||||
### Visit the [hapi.dev](https://hapi.dev) Developer Portal for tutorials, documentation, and support
|
||||
|
||||
## Useful resources
|
||||
|
||||
- [Documentation and API](https://hapi.dev/family/boom/)
|
||||
- [Version status](https://hapi.dev/resources/status/#boom) (builds, dependencies, node versions, licenses, eol)
|
||||
- [Changelog](https://hapi.dev/family/boom/changelog/)
|
||||
- [Project policies](https://hapi.dev/policies/)
|
||||
- [Free and commercial support options](https://hapi.dev/support/)
|
||||
Generated
Vendored
-549
@@ -1,549 +0,0 @@
|
||||
/**
|
||||
* An Error object used to return an HTTP response error (4xx, 5xx)
|
||||
*/
|
||||
export class Boom<Data = any> extends Error {
|
||||
|
||||
/**
|
||||
* Creates a new Boom object using the provided message
|
||||
*/
|
||||
constructor(message?: string | Error, options?: Options<Data>);
|
||||
|
||||
/**
|
||||
* Custom error data with additional information specific to the error type
|
||||
*/
|
||||
data?: Data;
|
||||
|
||||
/**
|
||||
* isBoom - if true, indicates this is a Boom object instance.
|
||||
*/
|
||||
isBoom: boolean;
|
||||
|
||||
/**
|
||||
* Convenience boolean indicating status code >= 500
|
||||
*/
|
||||
isServer: boolean;
|
||||
|
||||
/**
|
||||
* The error message
|
||||
*/
|
||||
message: string;
|
||||
|
||||
/**
|
||||
* The formatted response
|
||||
*/
|
||||
output: Output;
|
||||
|
||||
/**
|
||||
* The constructor used to create the error
|
||||
*/
|
||||
typeof: Function;
|
||||
|
||||
/**
|
||||
* Specifies if an error object is a valid boom object
|
||||
*
|
||||
* @param debug - A boolean that, when true, does not hide the original 500 error message. Defaults to false.
|
||||
*/
|
||||
reformat(debug?: boolean): string;
|
||||
}
|
||||
|
||||
|
||||
export interface Options<Data> {
|
||||
/**
|
||||
* The HTTP status code
|
||||
*
|
||||
* @default 500
|
||||
*/
|
||||
statusCode?: number;
|
||||
|
||||
/**
|
||||
* Additional error information
|
||||
*/
|
||||
data?: Data;
|
||||
|
||||
/**
|
||||
* Constructor reference used to crop the exception call stack output
|
||||
*/
|
||||
ctor?: Function;
|
||||
|
||||
/**
|
||||
* Error message string
|
||||
*
|
||||
* @default none
|
||||
*/
|
||||
message?: string;
|
||||
|
||||
/**
|
||||
* If false, the err provided is a Boom object, and a statusCode or message are provided, the values are ignored
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
override?: boolean;
|
||||
}
|
||||
|
||||
|
||||
export interface Decorate<Decoration> {
|
||||
|
||||
/**
|
||||
* An option with extra properties to set on the error object
|
||||
*/
|
||||
decorate?: Decoration;
|
||||
}
|
||||
|
||||
|
||||
export interface Payload {
|
||||
/**
|
||||
* The HTTP status code derived from error.output.statusCode
|
||||
*/
|
||||
statusCode: number;
|
||||
|
||||
/**
|
||||
* The HTTP status message derived from statusCode
|
||||
*/
|
||||
error: string;
|
||||
|
||||
/**
|
||||
* The error message derived from error.message
|
||||
*/
|
||||
message: string;
|
||||
|
||||
/**
|
||||
* Custom properties
|
||||
*/
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
|
||||
export interface Output {
|
||||
/**
|
||||
* The HTTP status code
|
||||
*/
|
||||
statusCode: number;
|
||||
|
||||
/**
|
||||
* An object containing any HTTP headers where each key is a header name and value is the header content
|
||||
*/
|
||||
headers: { [header: string]: string | string[] | number | undefined };
|
||||
|
||||
/**
|
||||
* The formatted object used as the response payload (stringified)
|
||||
*/
|
||||
payload: Payload;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specifies if an object is a valid boom object
|
||||
*
|
||||
* @param obj - The object to assess
|
||||
* @param statusCode - Optional status code
|
||||
*
|
||||
* @returns Returns a boolean stating if the error object is a valid boom object and it has the provided statusCode (if present)
|
||||
*/
|
||||
export function isBoom(obj: unknown, statusCode?: number): obj is Boom;
|
||||
|
||||
|
||||
/**
|
||||
* Specifies if an error object is a valid boom object
|
||||
*
|
||||
* @param err - The error object to decorate
|
||||
* @param options - Options object
|
||||
*
|
||||
* @returns A decorated boom object
|
||||
*/
|
||||
export function boomify<Data, Decoration>(err: Error, options?: Options<Data> & Decorate<Decoration>): Boom<Data> & Decoration;
|
||||
|
||||
|
||||
// 4xx Errors
|
||||
|
||||
/**
|
||||
* Returns a 400 Bad Request error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 400 bad request error
|
||||
*/
|
||||
export function badRequest<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 401 Unauthorized error
|
||||
*
|
||||
* @param message - Optional message
|
||||
*
|
||||
* @returns A 401 Unauthorized error
|
||||
*/
|
||||
export function unauthorized<Data>(message?: string | null): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 401 Unauthorized error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param scheme - the authentication scheme name
|
||||
* @param attributes - an object of values used to construct the 'WWW-Authenticate' header
|
||||
*
|
||||
* @returns A 401 Unauthorized error
|
||||
*/
|
||||
export function unauthorized<Data>(message: '' | null, scheme: string, attributes?: string | unauthorized.Attributes): Boom<Data> & unauthorized.MissingAuth;
|
||||
export function unauthorized<Data>(message: string | null, scheme: string, attributes?: string | unauthorized.Attributes): Boom<Data>;
|
||||
|
||||
|
||||
export namespace unauthorized {
|
||||
|
||||
interface Attributes {
|
||||
[index: string]: number | string | null | undefined;
|
||||
}
|
||||
|
||||
interface MissingAuth {
|
||||
|
||||
/**
|
||||
* Indicate whether the 401 unauthorized error is due to missing credentials (vs. invalid)
|
||||
*/
|
||||
isMissing: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 401 Unauthorized error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param wwwAuthenticate - array of string values used to construct the wwwAuthenticate header
|
||||
*
|
||||
* @returns A 401 Unauthorized error
|
||||
*/
|
||||
export function unauthorized<Data>(message: string | null, wwwAuthenticate: string[]): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 402 Payment Required error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 402 Payment Required error
|
||||
*/
|
||||
export function paymentRequired<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 403 Forbidden error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 403 Forbidden error
|
||||
*/
|
||||
export function forbidden<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 404 Not Found error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 404 Not Found error
|
||||
*/
|
||||
export function notFound<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 405 Method Not Allowed error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
* @param allow - Optional string or array of strings which is used to set the 'Allow' header
|
||||
*
|
||||
* @returns A 405 Method Not Allowed error
|
||||
*/
|
||||
export function methodNotAllowed<Data>(message?: string, data?: Data, allow?: string | string[]): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 406 Not Acceptable error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 406 Not Acceptable error
|
||||
*/
|
||||
export function notAcceptable<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 407 Proxy Authentication error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 407 Proxy Authentication error
|
||||
*/
|
||||
export function proxyAuthRequired<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 408 Request Time-out error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 408 Request Time-out error
|
||||
*/
|
||||
export function clientTimeout<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 409 Conflict error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 409 Conflict error
|
||||
*/
|
||||
export function conflict<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 410 Gone error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 410 gone error
|
||||
*/
|
||||
export function resourceGone<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 411 Length Required error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 411 Length Required error
|
||||
*/
|
||||
export function lengthRequired<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 412 Precondition Failed error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 412 Precondition Failed error
|
||||
*/
|
||||
export function preconditionFailed<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 413 Request Entity Too Large error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 413 Request Entity Too Large error
|
||||
*/
|
||||
export function entityTooLarge<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 414 Request-URI Too Large error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 414 Request-URI Too Large error
|
||||
*/
|
||||
export function uriTooLong<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 415 Unsupported Media Type error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 415 Unsupported Media Type error
|
||||
*/
|
||||
export function unsupportedMediaType<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 416 Request Range Not Satisfiable error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 416 Request Range Not Satisfiable error
|
||||
*/
|
||||
export function rangeNotSatisfiable<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 417 Expectation Failed error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 417 Expectation Failed error
|
||||
*/
|
||||
export function expectationFailed<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 418 I'm a Teapot error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 418 I'm a Teapot error
|
||||
*/
|
||||
export function teapot<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 422 Unprocessable Entity error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 422 Unprocessable Entity error
|
||||
*/
|
||||
export function badData<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 423 Locked error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 423 Locked error
|
||||
*/
|
||||
export function locked<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 424 Failed Dependency error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 424 Failed Dependency error
|
||||
*/
|
||||
export function failedDependency<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
/**
|
||||
* Returns a 425 Too Early error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 425 Too Early error
|
||||
*/
|
||||
export function tooEarly<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 428 Precondition Required error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 428 Precondition Required error
|
||||
*/
|
||||
export function preconditionRequired<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 429 Too Many Requests error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 429 Too Many Requests error
|
||||
*/
|
||||
export function tooManyRequests<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 451 Unavailable For Legal Reasons error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 451 Unavailable for Legal Reasons error
|
||||
*/
|
||||
export function illegal<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
// 5xx Errors
|
||||
|
||||
/**
|
||||
* Returns a internal error (defaults to 500)
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
* @param statusCode - Optional status code override. Defaults to 500.
|
||||
*
|
||||
* @returns A 500 Internal Server error
|
||||
*/
|
||||
export function internal<Data>(message?: string, data?: Data, statusCode?: number): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 500 Internal Server Error error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 500 Internal Server error
|
||||
*/
|
||||
export function badImplementation<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 501 Not Implemented error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 501 Not Implemented error
|
||||
*/
|
||||
export function notImplemented<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 502 Bad Gateway error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 502 Bad Gateway error
|
||||
*/
|
||||
export function badGateway<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 503 Service Unavailable error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 503 Service Unavailable error
|
||||
*/
|
||||
export function serverUnavailable<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a 504 Gateway Time-out error
|
||||
*
|
||||
* @param message - Optional message
|
||||
* @param data - Optional additional error data
|
||||
*
|
||||
* @returns A 504 Gateway Time-out error
|
||||
*/
|
||||
export function gatewayTimeout<Data>(message?: string, data?: Data): Boom<Data>;
|
||||
Generated
Vendored
-469
@@ -1,469 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
|
||||
const internals = {
|
||||
codes: new Map([
|
||||
[100, 'Continue'],
|
||||
[101, 'Switching Protocols'],
|
||||
[102, 'Processing'],
|
||||
[200, 'OK'],
|
||||
[201, 'Created'],
|
||||
[202, 'Accepted'],
|
||||
[203, 'Non-Authoritative Information'],
|
||||
[204, 'No Content'],
|
||||
[205, 'Reset Content'],
|
||||
[206, 'Partial Content'],
|
||||
[207, 'Multi-Status'],
|
||||
[300, 'Multiple Choices'],
|
||||
[301, 'Moved Permanently'],
|
||||
[302, 'Moved Temporarily'],
|
||||
[303, 'See Other'],
|
||||
[304, 'Not Modified'],
|
||||
[305, 'Use Proxy'],
|
||||
[307, 'Temporary Redirect'],
|
||||
[400, 'Bad Request'],
|
||||
[401, 'Unauthorized'],
|
||||
[402, 'Payment Required'],
|
||||
[403, 'Forbidden'],
|
||||
[404, 'Not Found'],
|
||||
[405, 'Method Not Allowed'],
|
||||
[406, 'Not Acceptable'],
|
||||
[407, 'Proxy Authentication Required'],
|
||||
[408, 'Request Time-out'],
|
||||
[409, 'Conflict'],
|
||||
[410, 'Gone'],
|
||||
[411, 'Length Required'],
|
||||
[412, 'Precondition Failed'],
|
||||
[413, 'Request Entity Too Large'],
|
||||
[414, 'Request-URI Too Large'],
|
||||
[415, 'Unsupported Media Type'],
|
||||
[416, 'Requested Range Not Satisfiable'],
|
||||
[417, 'Expectation Failed'],
|
||||
[418, 'I\'m a teapot'],
|
||||
[422, 'Unprocessable Entity'],
|
||||
[423, 'Locked'],
|
||||
[424, 'Failed Dependency'],
|
||||
[425, 'Too Early'],
|
||||
[426, 'Upgrade Required'],
|
||||
[428, 'Precondition Required'],
|
||||
[429, 'Too Many Requests'],
|
||||
[431, 'Request Header Fields Too Large'],
|
||||
[451, 'Unavailable For Legal Reasons'],
|
||||
[500, 'Internal Server Error'],
|
||||
[501, 'Not Implemented'],
|
||||
[502, 'Bad Gateway'],
|
||||
[503, 'Service Unavailable'],
|
||||
[504, 'Gateway Time-out'],
|
||||
[505, 'HTTP Version Not Supported'],
|
||||
[506, 'Variant Also Negotiates'],
|
||||
[507, 'Insufficient Storage'],
|
||||
[509, 'Bandwidth Limit Exceeded'],
|
||||
[510, 'Not Extended'],
|
||||
[511, 'Network Authentication Required']
|
||||
])
|
||||
};
|
||||
|
||||
|
||||
exports.Boom = class extends Error {
|
||||
|
||||
constructor(message, options = {}) {
|
||||
|
||||
if (message instanceof Error) {
|
||||
return exports.boomify(Hoek.clone(message), options);
|
||||
}
|
||||
|
||||
const { statusCode = 500, data = null, ctor = exports.Boom } = options;
|
||||
const error = new Error(message ? message : undefined); // Avoids settings null message
|
||||
Error.captureStackTrace(error, ctor); // Filter the stack to our external API
|
||||
error.data = data;
|
||||
const boom = internals.initialize(error, statusCode);
|
||||
|
||||
Object.defineProperty(boom, 'typeof', { value: ctor });
|
||||
|
||||
if (options.decorate) {
|
||||
Object.assign(boom, options.decorate);
|
||||
}
|
||||
|
||||
return boom;
|
||||
}
|
||||
|
||||
static [Symbol.hasInstance](instance) {
|
||||
|
||||
if (this === exports.Boom) {
|
||||
return exports.isBoom(instance);
|
||||
}
|
||||
|
||||
// Cannot use 'instanceof' as it creates infinite recursion
|
||||
|
||||
return this.prototype.isPrototypeOf(instance);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
exports.isBoom = function (err, statusCode) {
|
||||
|
||||
return err instanceof Error && !!err.isBoom && (!statusCode || err.output.statusCode === statusCode);
|
||||
};
|
||||
|
||||
|
||||
exports.boomify = function (err, options) {
|
||||
|
||||
Hoek.assert(err instanceof Error, 'Cannot wrap non-Error object');
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (options.data !== undefined) {
|
||||
err.data = options.data;
|
||||
}
|
||||
|
||||
if (options.decorate) {
|
||||
Object.assign(err, options.decorate);
|
||||
}
|
||||
|
||||
if (!err.isBoom) {
|
||||
return internals.initialize(err, options.statusCode || 500, options.message);
|
||||
}
|
||||
|
||||
if (options.override === false || // Defaults to true
|
||||
!options.statusCode && !options.message) {
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
return internals.initialize(err, options.statusCode || err.output.statusCode, options.message);
|
||||
};
|
||||
|
||||
|
||||
// 4xx Client Errors
|
||||
|
||||
exports.badRequest = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 400, data, ctor: exports.badRequest });
|
||||
};
|
||||
|
||||
|
||||
exports.unauthorized = function (message, scheme, attributes) { // Or (message, wwwAuthenticate[])
|
||||
|
||||
const err = new exports.Boom(message, { statusCode: 401, ctor: exports.unauthorized });
|
||||
|
||||
// function (message)
|
||||
|
||||
if (!scheme) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// function (message, wwwAuthenticate[])
|
||||
|
||||
if (typeof scheme !== 'string') {
|
||||
err.output.headers['WWW-Authenticate'] = scheme.join(', ');
|
||||
return err;
|
||||
}
|
||||
|
||||
// function (message, scheme, attributes)
|
||||
|
||||
let wwwAuthenticate = `${scheme}`;
|
||||
|
||||
if (attributes ||
|
||||
message) {
|
||||
|
||||
err.output.payload.attributes = {};
|
||||
}
|
||||
|
||||
if (attributes) {
|
||||
if (typeof attributes === 'string') {
|
||||
wwwAuthenticate += ' ' + Hoek.escapeHeaderAttribute(attributes);
|
||||
err.output.payload.attributes = attributes;
|
||||
}
|
||||
else {
|
||||
wwwAuthenticate += ' ' + Object.keys(attributes).map((name) => {
|
||||
|
||||
let value = attributes[name];
|
||||
if (value === null ||
|
||||
value === undefined) {
|
||||
|
||||
value = '';
|
||||
}
|
||||
|
||||
err.output.payload.attributes[name] = value;
|
||||
return `${name}="${Hoek.escapeHeaderAttribute(value.toString())}"`;
|
||||
})
|
||||
.join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
if (message) {
|
||||
if (attributes) {
|
||||
wwwAuthenticate += ',';
|
||||
}
|
||||
|
||||
wwwAuthenticate += ` error="${Hoek.escapeHeaderAttribute(message)}"`;
|
||||
err.output.payload.attributes.error = message;
|
||||
}
|
||||
else {
|
||||
err.isMissing = true;
|
||||
}
|
||||
|
||||
err.output.headers['WWW-Authenticate'] = wwwAuthenticate;
|
||||
return err;
|
||||
};
|
||||
|
||||
|
||||
exports.paymentRequired = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 402, data, ctor: exports.paymentRequired });
|
||||
};
|
||||
|
||||
|
||||
exports.forbidden = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 403, data, ctor: exports.forbidden });
|
||||
};
|
||||
|
||||
|
||||
exports.notFound = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 404, data, ctor: exports.notFound });
|
||||
};
|
||||
|
||||
|
||||
exports.methodNotAllowed = function (message, data, allow) {
|
||||
|
||||
const err = new exports.Boom(message, { statusCode: 405, data, ctor: exports.methodNotAllowed });
|
||||
|
||||
if (typeof allow === 'string') {
|
||||
allow = [allow];
|
||||
}
|
||||
|
||||
if (Array.isArray(allow)) {
|
||||
err.output.headers.Allow = allow.join(', ');
|
||||
}
|
||||
|
||||
return err;
|
||||
};
|
||||
|
||||
|
||||
exports.notAcceptable = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 406, data, ctor: exports.notAcceptable });
|
||||
};
|
||||
|
||||
|
||||
exports.proxyAuthRequired = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 407, data, ctor: exports.proxyAuthRequired });
|
||||
};
|
||||
|
||||
|
||||
exports.clientTimeout = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 408, data, ctor: exports.clientTimeout });
|
||||
};
|
||||
|
||||
|
||||
exports.conflict = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 409, data, ctor: exports.conflict });
|
||||
};
|
||||
|
||||
|
||||
exports.resourceGone = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 410, data, ctor: exports.resourceGone });
|
||||
};
|
||||
|
||||
|
||||
exports.lengthRequired = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 411, data, ctor: exports.lengthRequired });
|
||||
};
|
||||
|
||||
|
||||
exports.preconditionFailed = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 412, data, ctor: exports.preconditionFailed });
|
||||
};
|
||||
|
||||
|
||||
exports.entityTooLarge = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 413, data, ctor: exports.entityTooLarge });
|
||||
};
|
||||
|
||||
|
||||
exports.uriTooLong = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 414, data, ctor: exports.uriTooLong });
|
||||
};
|
||||
|
||||
|
||||
exports.unsupportedMediaType = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 415, data, ctor: exports.unsupportedMediaType });
|
||||
};
|
||||
|
||||
|
||||
exports.rangeNotSatisfiable = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 416, data, ctor: exports.rangeNotSatisfiable });
|
||||
};
|
||||
|
||||
|
||||
exports.expectationFailed = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 417, data, ctor: exports.expectationFailed });
|
||||
};
|
||||
|
||||
|
||||
exports.teapot = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 418, data, ctor: exports.teapot });
|
||||
};
|
||||
|
||||
|
||||
exports.badData = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 422, data, ctor: exports.badData });
|
||||
};
|
||||
|
||||
|
||||
exports.locked = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 423, data, ctor: exports.locked });
|
||||
};
|
||||
|
||||
|
||||
exports.failedDependency = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 424, data, ctor: exports.failedDependency });
|
||||
};
|
||||
|
||||
exports.tooEarly = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 425, data, ctor: exports.tooEarly });
|
||||
};
|
||||
|
||||
|
||||
exports.preconditionRequired = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 428, data, ctor: exports.preconditionRequired });
|
||||
};
|
||||
|
||||
|
||||
exports.tooManyRequests = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 429, data, ctor: exports.tooManyRequests });
|
||||
};
|
||||
|
||||
|
||||
exports.illegal = function (message, data) {
|
||||
|
||||
return new exports.Boom(message, { statusCode: 451, data, ctor: exports.illegal });
|
||||
};
|
||||
|
||||
|
||||
// 5xx Server Errors
|
||||
|
||||
exports.internal = function (message, data, statusCode = 500) {
|
||||
|
||||
return internals.serverError(message, data, statusCode, exports.internal);
|
||||
};
|
||||
|
||||
|
||||
exports.notImplemented = function (message, data) {
|
||||
|
||||
return internals.serverError(message, data, 501, exports.notImplemented);
|
||||
};
|
||||
|
||||
|
||||
exports.badGateway = function (message, data) {
|
||||
|
||||
return internals.serverError(message, data, 502, exports.badGateway);
|
||||
};
|
||||
|
||||
|
||||
exports.serverUnavailable = function (message, data) {
|
||||
|
||||
return internals.serverError(message, data, 503, exports.serverUnavailable);
|
||||
};
|
||||
|
||||
|
||||
exports.gatewayTimeout = function (message, data) {
|
||||
|
||||
return internals.serverError(message, data, 504, exports.gatewayTimeout);
|
||||
};
|
||||
|
||||
|
||||
exports.badImplementation = function (message, data) {
|
||||
|
||||
const err = internals.serverError(message, data, 500, exports.badImplementation);
|
||||
err.isDeveloperError = true;
|
||||
return err;
|
||||
};
|
||||
|
||||
|
||||
internals.initialize = function (err, statusCode, message) {
|
||||
|
||||
const numberCode = parseInt(statusCode, 10);
|
||||
Hoek.assert(!isNaN(numberCode) && numberCode >= 400, 'First argument must be a number (400+):', statusCode);
|
||||
|
||||
err.isBoom = true;
|
||||
err.isServer = numberCode >= 500;
|
||||
|
||||
if (!err.hasOwnProperty('data')) {
|
||||
err.data = null;
|
||||
}
|
||||
|
||||
err.output = {
|
||||
statusCode: numberCode,
|
||||
payload: {},
|
||||
headers: {}
|
||||
};
|
||||
|
||||
Object.defineProperty(err, 'reformat', { value: internals.reformat, configurable: true });
|
||||
|
||||
if (!message &&
|
||||
!err.message) {
|
||||
|
||||
err.reformat();
|
||||
message = err.output.payload.error;
|
||||
}
|
||||
|
||||
if (message) {
|
||||
const props = Object.getOwnPropertyDescriptor(err, 'message') || Object.getOwnPropertyDescriptor(Object.getPrototypeOf(err), 'message');
|
||||
Hoek.assert(!props || props.configurable && !props.get, 'The error is not compatible with boom');
|
||||
|
||||
err.message = message + (err.message ? ': ' + err.message : '');
|
||||
err.output.payload.message = err.message;
|
||||
}
|
||||
|
||||
err.reformat();
|
||||
return err;
|
||||
};
|
||||
|
||||
|
||||
internals.reformat = function (debug = false) {
|
||||
|
||||
this.output.payload.statusCode = this.output.statusCode;
|
||||
this.output.payload.error = internals.codes.get(this.output.statusCode) || 'Unknown';
|
||||
|
||||
if (this.output.statusCode === 500 && debug !== true) {
|
||||
this.output.payload.message = 'An internal server error occurred'; // Hide actual error from user
|
||||
}
|
||||
else if (this.message) {
|
||||
this.output.payload.message = this.message;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.serverError = function (message, data, statusCode, ctor) {
|
||||
|
||||
if (data instanceof Error &&
|
||||
!data.isBoom) {
|
||||
|
||||
return exports.boomify(data, { statusCode, message });
|
||||
}
|
||||
|
||||
return new exports.Boom(message, { statusCode, data, ctor });
|
||||
};
|
||||
Generated
Vendored
-28
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"name": "@hapi/boom",
|
||||
"description": "HTTP-friendly error objects",
|
||||
"version": "9.1.4",
|
||||
"repository": "git://github.com/hapijs/boom",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"keywords": [
|
||||
"error",
|
||||
"http"
|
||||
],
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "9.x.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hapi/code": "8.x.x",
|
||||
"@hapi/lab": "24.x.x",
|
||||
"typescript": "~4.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "lab -a @hapi/code -t 100 -L -Y",
|
||||
"test-cov-html": "lab -a @hapi/code -t 100 -L -r html -o coverage.html"
|
||||
},
|
||||
"license": "BSD-3-Clause"
|
||||
}
|
||||
Generated
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
Copyright (c) 2011-2020, Sideway Inc, and project contributors
|
||||
Copyright (c) 2011-2014, Walmart
|
||||
Copyright (c) 2011, Yahoo Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* 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.
|
||||
* The names of any contributors may not 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 HOLDERS AND 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 OFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Generated
Vendored
-19
@@ -1,19 +0,0 @@
|
||||
<a href="https://hapi.dev"><img src="https://raw.githubusercontent.com/hapijs/assets/master/images/family.png" width="180px" align="right" /></a>
|
||||
|
||||
# @hapi/hoek
|
||||
|
||||
#### Utility methods for the hapi ecosystem.
|
||||
|
||||
**hoek** is part of the **hapi** ecosystem and was designed to work seamlessly with the [hapi web framework](https://hapi.dev) and its other components (but works great on its own or with other frameworks). If you are using a different web framework and find this module useful, check out [hapi](https://hapi.dev) – they work even better together.
|
||||
|
||||
This module is not intended to solve every problem for everyone, but rather as a central place to store hapi-specific methods. If you're looking for a general purpose utility module, check out [lodash](https://github.com/lodash/lodash).
|
||||
|
||||
### Visit the [hapi.dev](https://hapi.dev) Developer Portal for tutorials, documentation, and support
|
||||
|
||||
## Useful resources
|
||||
|
||||
- [Documentation and API](https://hapi.dev/family/hoek/)
|
||||
- [Version status](https://hapi.dev/resources/status/#hoek) (builds, dependencies, node versions, licenses, eol)
|
||||
- [Changelog](https://hapi.dev/family/hoek/changelog/)
|
||||
- [Project policies](https://hapi.dev/policies/)
|
||||
- [Free and commercial support options](https://hapi.dev/support/)
|
||||
Generated
Vendored
-102
@@ -1,102 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Assert = require('./assert');
|
||||
const Clone = require('./clone');
|
||||
const Merge = require('./merge');
|
||||
const Reach = require('./reach');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (defaults, source, options = {}) {
|
||||
|
||||
Assert(defaults && typeof defaults === 'object', 'Invalid defaults value: must be an object');
|
||||
Assert(!source || source === true || typeof source === 'object', 'Invalid source value: must be true, falsy or an object');
|
||||
Assert(typeof options === 'object', 'Invalid options: must be an object');
|
||||
|
||||
if (!source) { // If no source, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
if (options.shallow) {
|
||||
return internals.applyToDefaultsWithShallow(defaults, source, options);
|
||||
}
|
||||
|
||||
const copy = Clone(defaults);
|
||||
|
||||
if (source === true) { // If source is set to true, use defaults
|
||||
return copy;
|
||||
}
|
||||
|
||||
const nullOverride = options.nullOverride !== undefined ? options.nullOverride : false;
|
||||
return Merge(copy, source, { nullOverride, mergeArrays: false });
|
||||
};
|
||||
|
||||
|
||||
internals.applyToDefaultsWithShallow = function (defaults, source, options) {
|
||||
|
||||
const keys = options.shallow;
|
||||
Assert(Array.isArray(keys), 'Invalid keys');
|
||||
|
||||
const seen = new Map();
|
||||
const merge = source === true ? null : new Set();
|
||||
|
||||
for (let key of keys) {
|
||||
key = Array.isArray(key) ? key : key.split('.'); // Pre-split optimization
|
||||
|
||||
const ref = Reach(defaults, key);
|
||||
if (ref &&
|
||||
typeof ref === 'object') {
|
||||
|
||||
seen.set(ref, merge && Reach(source, key) || ref);
|
||||
}
|
||||
else if (merge) {
|
||||
merge.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
const copy = Clone(defaults, {}, seen);
|
||||
|
||||
if (!merge) {
|
||||
return copy;
|
||||
}
|
||||
|
||||
for (const key of merge) {
|
||||
internals.reachCopy(copy, source, key);
|
||||
}
|
||||
|
||||
const nullOverride = options.nullOverride !== undefined ? options.nullOverride : false;
|
||||
return Merge(copy, source, { nullOverride, mergeArrays: false });
|
||||
};
|
||||
|
||||
|
||||
internals.reachCopy = function (dst, src, path) {
|
||||
|
||||
for (const segment of path) {
|
||||
if (!(segment in src)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const val = src[segment];
|
||||
|
||||
if (typeof val !== 'object' || val === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
src = val;
|
||||
}
|
||||
|
||||
const value = src;
|
||||
let ref = dst;
|
||||
for (let i = 0; i < path.length - 1; ++i) {
|
||||
const segment = path[i];
|
||||
if (typeof ref[segment] !== 'object') {
|
||||
ref[segment] = {};
|
||||
}
|
||||
|
||||
ref = ref[segment];
|
||||
}
|
||||
|
||||
ref[path[path.length - 1]] = value;
|
||||
};
|
||||
Generated
Vendored
-22
@@ -1,22 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const AssertError = require('./error');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (condition, ...args) {
|
||||
|
||||
if (condition) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length === 1 &&
|
||||
args[0] instanceof Error) {
|
||||
|
||||
throw args[0];
|
||||
}
|
||||
|
||||
throw new AssertError(args);
|
||||
};
|
||||
Generated
Vendored
-29
@@ -1,29 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = internals.Bench = class {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.ts = 0;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
this.ts = internals.Bench.now();
|
||||
}
|
||||
|
||||
elapsed() {
|
||||
|
||||
return internals.Bench.now() - this.ts;
|
||||
}
|
||||
|
||||
static now() {
|
||||
|
||||
const ts = process.hrtime();
|
||||
return (ts[0] * 1e3) + (ts[1] / 1e6);
|
||||
}
|
||||
};
|
||||
Generated
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Ignore = require('./ignore');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function () {
|
||||
|
||||
return new Promise(Ignore);
|
||||
};
|
||||
Generated
Vendored
-176
@@ -1,176 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Reach = require('./reach');
|
||||
const Types = require('./types');
|
||||
const Utils = require('./utils');
|
||||
|
||||
|
||||
const internals = {
|
||||
needsProtoHack: new Set([Types.set, Types.map, Types.weakSet, Types.weakMap])
|
||||
};
|
||||
|
||||
|
||||
module.exports = internals.clone = function (obj, options = {}, _seen = null) {
|
||||
|
||||
if (typeof obj !== 'object' ||
|
||||
obj === null) {
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
let clone = internals.clone;
|
||||
let seen = _seen;
|
||||
|
||||
if (options.shallow) {
|
||||
if (options.shallow !== true) {
|
||||
return internals.cloneWithShallow(obj, options);
|
||||
}
|
||||
|
||||
clone = (value) => value;
|
||||
}
|
||||
else if (seen) {
|
||||
const lookup = seen.get(obj);
|
||||
if (lookup) {
|
||||
return lookup;
|
||||
}
|
||||
}
|
||||
else {
|
||||
seen = new Map();
|
||||
}
|
||||
|
||||
// Built-in object types
|
||||
|
||||
const baseProto = Types.getInternalProto(obj);
|
||||
if (baseProto === Types.buffer) {
|
||||
return Buffer && Buffer.from(obj); // $lab:coverage:ignore$
|
||||
}
|
||||
|
||||
if (baseProto === Types.date) {
|
||||
return new Date(obj.getTime());
|
||||
}
|
||||
|
||||
if (baseProto === Types.regex) {
|
||||
return new RegExp(obj);
|
||||
}
|
||||
|
||||
// Generic objects
|
||||
|
||||
const newObj = internals.base(obj, baseProto, options);
|
||||
if (newObj === obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (seen) {
|
||||
seen.set(obj, newObj); // Set seen, since obj could recurse
|
||||
}
|
||||
|
||||
if (baseProto === Types.set) {
|
||||
for (const value of obj) {
|
||||
newObj.add(clone(value, options, seen));
|
||||
}
|
||||
}
|
||||
else if (baseProto === Types.map) {
|
||||
for (const [key, value] of obj) {
|
||||
newObj.set(key, clone(value, options, seen));
|
||||
}
|
||||
}
|
||||
|
||||
const keys = Utils.keys(obj, options);
|
||||
for (const key of keys) {
|
||||
if (key === '__proto__') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (baseProto === Types.array &&
|
||||
key === 'length') {
|
||||
|
||||
newObj.length = obj.length;
|
||||
continue;
|
||||
}
|
||||
|
||||
const descriptor = Object.getOwnPropertyDescriptor(obj, key);
|
||||
if (descriptor) {
|
||||
if (descriptor.get ||
|
||||
descriptor.set) {
|
||||
|
||||
Object.defineProperty(newObj, key, descriptor);
|
||||
}
|
||||
else if (descriptor.enumerable) {
|
||||
newObj[key] = clone(obj[key], options, seen);
|
||||
}
|
||||
else {
|
||||
Object.defineProperty(newObj, key, { enumerable: false, writable: true, configurable: true, value: clone(obj[key], options, seen) });
|
||||
}
|
||||
}
|
||||
else {
|
||||
Object.defineProperty(newObj, key, {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: clone(obj[key], options, seen)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return newObj;
|
||||
};
|
||||
|
||||
|
||||
internals.cloneWithShallow = function (source, options) {
|
||||
|
||||
const keys = options.shallow;
|
||||
options = Object.assign({}, options);
|
||||
options.shallow = false;
|
||||
|
||||
const seen = new Map();
|
||||
|
||||
for (const key of keys) {
|
||||
const ref = Reach(source, key);
|
||||
if (typeof ref === 'object' ||
|
||||
typeof ref === 'function') {
|
||||
|
||||
seen.set(ref, ref);
|
||||
}
|
||||
}
|
||||
|
||||
return internals.clone(source, options, seen);
|
||||
};
|
||||
|
||||
|
||||
internals.base = function (obj, baseProto, options) {
|
||||
|
||||
if (options.prototype === false) { // Defaults to true
|
||||
if (internals.needsProtoHack.has(baseProto)) {
|
||||
return new baseProto.constructor();
|
||||
}
|
||||
|
||||
return baseProto === Types.array ? [] : {};
|
||||
}
|
||||
|
||||
const proto = Object.getPrototypeOf(obj);
|
||||
if (proto &&
|
||||
proto.isImmutable) {
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (baseProto === Types.array) {
|
||||
const newObj = [];
|
||||
if (proto !== baseProto) {
|
||||
Object.setPrototypeOf(newObj, proto);
|
||||
}
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
||||
if (internals.needsProtoHack.has(baseProto)) {
|
||||
const newObj = new proto.constructor();
|
||||
if (proto !== baseProto) {
|
||||
Object.setPrototypeOf(newObj, proto);
|
||||
}
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
||||
return Object.create(proto);
|
||||
};
|
||||
Generated
Vendored
-307
@@ -1,307 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Assert = require('./assert');
|
||||
const DeepEqual = require('./deepEqual');
|
||||
const EscapeRegex = require('./escapeRegex');
|
||||
const Utils = require('./utils');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (ref, values, options = {}) { // options: { deep, once, only, part, symbols }
|
||||
|
||||
/*
|
||||
string -> string(s)
|
||||
array -> item(s)
|
||||
object -> key(s)
|
||||
object -> object (key:value)
|
||||
*/
|
||||
|
||||
if (typeof values !== 'object') {
|
||||
values = [values];
|
||||
}
|
||||
|
||||
Assert(!Array.isArray(values) || values.length, 'Values array cannot be empty');
|
||||
|
||||
// String
|
||||
|
||||
if (typeof ref === 'string') {
|
||||
return internals.string(ref, values, options);
|
||||
}
|
||||
|
||||
// Array
|
||||
|
||||
if (Array.isArray(ref)) {
|
||||
return internals.array(ref, values, options);
|
||||
}
|
||||
|
||||
// Object
|
||||
|
||||
Assert(typeof ref === 'object', 'Reference must be string or an object');
|
||||
return internals.object(ref, values, options);
|
||||
};
|
||||
|
||||
|
||||
internals.array = function (ref, values, options) {
|
||||
|
||||
if (!Array.isArray(values)) {
|
||||
values = [values];
|
||||
}
|
||||
|
||||
if (!ref.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.only &&
|
||||
options.once &&
|
||||
ref.length !== values.length) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
let compare;
|
||||
|
||||
// Map values
|
||||
|
||||
const map = new Map();
|
||||
for (const value of values) {
|
||||
if (!options.deep ||
|
||||
!value ||
|
||||
typeof value !== 'object') {
|
||||
|
||||
const existing = map.get(value);
|
||||
if (existing) {
|
||||
++existing.allowed;
|
||||
}
|
||||
else {
|
||||
map.set(value, { allowed: 1, hits: 0 });
|
||||
}
|
||||
}
|
||||
else {
|
||||
compare = compare || internals.compare(options);
|
||||
|
||||
let found = false;
|
||||
for (const [key, existing] of map.entries()) {
|
||||
if (compare(key, value)) {
|
||||
++existing.allowed;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
map.set(value, { allowed: 1, hits: 0 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup values
|
||||
|
||||
let hits = 0;
|
||||
for (const item of ref) {
|
||||
let match;
|
||||
if (!options.deep ||
|
||||
!item ||
|
||||
typeof item !== 'object') {
|
||||
|
||||
match = map.get(item);
|
||||
}
|
||||
else {
|
||||
compare = compare || internals.compare(options);
|
||||
|
||||
for (const [key, existing] of map.entries()) {
|
||||
if (compare(key, item)) {
|
||||
match = existing;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
++match.hits;
|
||||
++hits;
|
||||
|
||||
if (options.once &&
|
||||
match.hits > match.allowed) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate results
|
||||
|
||||
if (options.only &&
|
||||
hits !== ref.length) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const match of map.values()) {
|
||||
if (match.hits === match.allowed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match.hits < match.allowed &&
|
||||
!options.part) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !!hits;
|
||||
};
|
||||
|
||||
|
||||
internals.object = function (ref, values, options) {
|
||||
|
||||
Assert(options.once === undefined, 'Cannot use option once with object');
|
||||
|
||||
const keys = Utils.keys(ref, options);
|
||||
if (!keys.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keys list
|
||||
|
||||
if (Array.isArray(values)) {
|
||||
return internals.array(keys, values, options);
|
||||
}
|
||||
|
||||
// Key value pairs
|
||||
|
||||
const symbols = Object.getOwnPropertySymbols(values).filter((sym) => values.propertyIsEnumerable(sym));
|
||||
const targets = [...Object.keys(values), ...symbols];
|
||||
|
||||
const compare = internals.compare(options);
|
||||
const set = new Set(targets);
|
||||
|
||||
for (const key of keys) {
|
||||
if (!set.has(key)) {
|
||||
if (options.only) {
|
||||
return false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!compare(values[key], ref[key])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set.delete(key);
|
||||
}
|
||||
|
||||
if (set.size) {
|
||||
return options.part ? set.size < targets.length : false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
internals.string = function (ref, values, options) {
|
||||
|
||||
// Empty string
|
||||
|
||||
if (ref === '') {
|
||||
return values.length === 1 && values[0] === '' || // '' contains ''
|
||||
!options.once && !values.some((v) => v !== ''); // '' contains multiple '' if !once
|
||||
}
|
||||
|
||||
// Map values
|
||||
|
||||
const map = new Map();
|
||||
const patterns = [];
|
||||
|
||||
for (const value of values) {
|
||||
Assert(typeof value === 'string', 'Cannot compare string reference to non-string value');
|
||||
|
||||
if (value) {
|
||||
const existing = map.get(value);
|
||||
if (existing) {
|
||||
++existing.allowed;
|
||||
}
|
||||
else {
|
||||
map.set(value, { allowed: 1, hits: 0 });
|
||||
patterns.push(EscapeRegex(value));
|
||||
}
|
||||
}
|
||||
else if (options.once ||
|
||||
options.only) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!patterns.length) { // Non-empty string contains unlimited empty string
|
||||
return true;
|
||||
}
|
||||
|
||||
// Match patterns
|
||||
|
||||
const regex = new RegExp(`(${patterns.join('|')})`, 'g');
|
||||
const leftovers = ref.replace(regex, ($0, $1) => {
|
||||
|
||||
++map.get($1).hits;
|
||||
return ''; // Remove from string
|
||||
});
|
||||
|
||||
// Validate results
|
||||
|
||||
if (options.only &&
|
||||
leftovers) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
let any = false;
|
||||
for (const match of map.values()) {
|
||||
if (match.hits) {
|
||||
any = true;
|
||||
}
|
||||
|
||||
if (match.hits === match.allowed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match.hits < match.allowed &&
|
||||
!options.part) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// match.hits > match.allowed
|
||||
|
||||
if (options.once) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !!any;
|
||||
};
|
||||
|
||||
|
||||
internals.compare = function (options) {
|
||||
|
||||
if (!options.deep) {
|
||||
return internals.shallow;
|
||||
}
|
||||
|
||||
const hasOnly = options.only !== undefined;
|
||||
const hasPart = options.part !== undefined;
|
||||
|
||||
const flags = {
|
||||
prototype: hasOnly ? options.only : hasPart ? !options.part : false,
|
||||
part: hasOnly ? !options.only : hasPart ? options.part : false
|
||||
};
|
||||
|
||||
return (a, b) => DeepEqual(a, b, flags);
|
||||
};
|
||||
|
||||
|
||||
internals.shallow = function (a, b) {
|
||||
|
||||
return a === b;
|
||||
};
|
||||
Generated
Vendored
-317
@@ -1,317 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Types = require('./types');
|
||||
|
||||
|
||||
const internals = {
|
||||
mismatched: null
|
||||
};
|
||||
|
||||
|
||||
module.exports = function (obj, ref, options) {
|
||||
|
||||
options = Object.assign({ prototype: true }, options);
|
||||
|
||||
return !!internals.isDeepEqual(obj, ref, options, []);
|
||||
};
|
||||
|
||||
|
||||
internals.isDeepEqual = function (obj, ref, options, seen) {
|
||||
|
||||
if (obj === ref) { // Copied from Deep-eql, copyright(c) 2013 Jake Luer, jake@alogicalparadox.com, MIT Licensed, https://github.com/chaijs/deep-eql
|
||||
return obj !== 0 || 1 / obj === 1 / ref;
|
||||
}
|
||||
|
||||
const type = typeof obj;
|
||||
|
||||
if (type !== typeof ref) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj === null ||
|
||||
ref === null) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type === 'function') {
|
||||
if (!options.deepFunction ||
|
||||
obj.toString() !== ref.toString()) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Continue as object
|
||||
}
|
||||
else if (type !== 'object') {
|
||||
return obj !== obj && ref !== ref; // NaN
|
||||
}
|
||||
|
||||
const instanceType = internals.getSharedType(obj, ref, !!options.prototype);
|
||||
switch (instanceType) {
|
||||
case Types.buffer:
|
||||
return Buffer && Buffer.prototype.equals.call(obj, ref); // $lab:coverage:ignore$
|
||||
case Types.promise:
|
||||
return obj === ref;
|
||||
case Types.regex:
|
||||
return obj.toString() === ref.toString();
|
||||
case internals.mismatched:
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = seen.length - 1; i >= 0; --i) {
|
||||
if (seen[i].isSame(obj, ref)) {
|
||||
return true; // If previous comparison failed, it would have stopped execution
|
||||
}
|
||||
}
|
||||
|
||||
seen.push(new internals.SeenEntry(obj, ref));
|
||||
|
||||
try {
|
||||
return !!internals.isDeepEqualObj(instanceType, obj, ref, options, seen);
|
||||
}
|
||||
finally {
|
||||
seen.pop();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.getSharedType = function (obj, ref, checkPrototype) {
|
||||
|
||||
if (checkPrototype) {
|
||||
if (Object.getPrototypeOf(obj) !== Object.getPrototypeOf(ref)) {
|
||||
return internals.mismatched;
|
||||
}
|
||||
|
||||
return Types.getInternalProto(obj);
|
||||
}
|
||||
|
||||
const type = Types.getInternalProto(obj);
|
||||
if (type !== Types.getInternalProto(ref)) {
|
||||
return internals.mismatched;
|
||||
}
|
||||
|
||||
return type;
|
||||
};
|
||||
|
||||
|
||||
internals.valueOf = function (obj) {
|
||||
|
||||
const objValueOf = obj.valueOf;
|
||||
if (objValueOf === undefined) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
try {
|
||||
return objValueOf.call(obj);
|
||||
}
|
||||
catch (err) {
|
||||
return err;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.hasOwnEnumerableProperty = function (obj, key) {
|
||||
|
||||
return Object.prototype.propertyIsEnumerable.call(obj, key);
|
||||
};
|
||||
|
||||
|
||||
internals.isSetSimpleEqual = function (obj, ref) {
|
||||
|
||||
for (const entry of Set.prototype.values.call(obj)) {
|
||||
if (!Set.prototype.has.call(ref, entry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
internals.isDeepEqualObj = function (instanceType, obj, ref, options, seen) {
|
||||
|
||||
const { isDeepEqual, valueOf, hasOwnEnumerableProperty } = internals;
|
||||
const { keys, getOwnPropertySymbols } = Object;
|
||||
|
||||
if (instanceType === Types.array) {
|
||||
if (options.part) {
|
||||
|
||||
// Check if any index match any other index
|
||||
|
||||
for (const objValue of obj) {
|
||||
for (const refValue of ref) {
|
||||
if (isDeepEqual(objValue, refValue, options, seen)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (obj.length !== ref.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < obj.length; ++i) {
|
||||
if (!isDeepEqual(obj[i], ref[i], options, seen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (instanceType === Types.set) {
|
||||
if (obj.size !== ref.size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!internals.isSetSimpleEqual(obj, ref)) {
|
||||
|
||||
// Check for deep equality
|
||||
|
||||
const ref2 = new Set(Set.prototype.values.call(ref));
|
||||
for (const objEntry of Set.prototype.values.call(obj)) {
|
||||
if (ref2.delete(objEntry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let found = false;
|
||||
for (const refEntry of ref2) {
|
||||
if (isDeepEqual(objEntry, refEntry, options, seen)) {
|
||||
ref2.delete(refEntry);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (instanceType === Types.map) {
|
||||
if (obj.size !== ref.size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const [key, value] of Map.prototype.entries.call(obj)) {
|
||||
if (value === undefined && !Map.prototype.has.call(ref, key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isDeepEqual(value, Map.prototype.get.call(ref, key), options, seen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (instanceType === Types.error) {
|
||||
|
||||
// Always check name and message
|
||||
|
||||
if (obj.name !== ref.name ||
|
||||
obj.message !== ref.message) {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check .valueOf()
|
||||
|
||||
const valueOfObj = valueOf(obj);
|
||||
const valueOfRef = valueOf(ref);
|
||||
if ((obj !== valueOfObj || ref !== valueOfRef) &&
|
||||
!isDeepEqual(valueOfObj, valueOfRef, options, seen)) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check properties
|
||||
|
||||
const objKeys = keys(obj);
|
||||
if (!options.part &&
|
||||
objKeys.length !== keys(ref).length &&
|
||||
!options.skip) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
let skipped = 0;
|
||||
for (const key of objKeys) {
|
||||
if (options.skip &&
|
||||
options.skip.includes(key)) {
|
||||
|
||||
if (ref[key] === undefined) {
|
||||
++skipped;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hasOwnEnumerableProperty(ref, key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isDeepEqual(obj[key], ref[key], options, seen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.part &&
|
||||
objKeys.length - skipped !== keys(ref).length) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check symbols
|
||||
|
||||
if (options.symbols !== false) { // Defaults to true
|
||||
const objSymbols = getOwnPropertySymbols(obj);
|
||||
const refSymbols = new Set(getOwnPropertySymbols(ref));
|
||||
|
||||
for (const key of objSymbols) {
|
||||
if (!options.skip ||
|
||||
!options.skip.includes(key)) {
|
||||
|
||||
if (hasOwnEnumerableProperty(obj, key)) {
|
||||
if (!hasOwnEnumerableProperty(ref, key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isDeepEqual(obj[key], ref[key], options, seen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (hasOwnEnumerableProperty(ref, key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
refSymbols.delete(key);
|
||||
}
|
||||
|
||||
for (const key of refSymbols) {
|
||||
if (hasOwnEnumerableProperty(ref, key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
internals.SeenEntry = class {
|
||||
|
||||
constructor(obj, ref) {
|
||||
|
||||
this.obj = obj;
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
isSame(obj, ref) {
|
||||
|
||||
return this.obj === obj && this.ref === ref;
|
||||
}
|
||||
};
|
||||
Generated
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Stringify = require('./stringify');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = class extends Error {
|
||||
|
||||
constructor(args) {
|
||||
|
||||
const msgs = args
|
||||
.filter((arg) => arg !== '')
|
||||
.map((arg) => {
|
||||
|
||||
return typeof arg === 'string' ? arg : arg instanceof Error ? arg.message : Stringify(arg);
|
||||
});
|
||||
|
||||
super(msgs.join(' ') || 'Unknown error');
|
||||
|
||||
if (typeof Error.captureStackTrace === 'function') { // $lab:coverage:ignore$
|
||||
Error.captureStackTrace(this, exports.assert);
|
||||
}
|
||||
}
|
||||
};
|
||||
src/openjarvis/channels/whatsapp_baileys_bridge/node_modules/@hapi/hoek/lib/escapeHeaderAttribute.js
Generated
Vendored
-16
@@ -1,16 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Assert = require('./assert');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (attribute) {
|
||||
|
||||
// Allowed value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9, \, "
|
||||
|
||||
Assert(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~\"\\]*$/.test(attribute), 'Bad attribute value (' + attribute + ')');
|
||||
|
||||
return attribute.replace(/\\/g, '\\\\').replace(/\"/g, '\\"'); // Escape quotes and slash
|
||||
};
|
||||
Generated
Vendored
-87
@@ -1,87 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (input) {
|
||||
|
||||
if (!input) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let escaped = '';
|
||||
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
|
||||
const charCode = input.charCodeAt(i);
|
||||
|
||||
if (internals.isSafe(charCode)) {
|
||||
escaped += input[i];
|
||||
}
|
||||
else {
|
||||
escaped += internals.escapeHtmlChar(charCode);
|
||||
}
|
||||
}
|
||||
|
||||
return escaped;
|
||||
};
|
||||
|
||||
|
||||
internals.escapeHtmlChar = function (charCode) {
|
||||
|
||||
const namedEscape = internals.namedHtml.get(charCode);
|
||||
if (namedEscape) {
|
||||
return namedEscape;
|
||||
}
|
||||
|
||||
if (charCode >= 256) {
|
||||
return '&#' + charCode + ';';
|
||||
}
|
||||
|
||||
const hexValue = charCode.toString(16).padStart(2, '0');
|
||||
return `&#x${hexValue};`;
|
||||
};
|
||||
|
||||
|
||||
internals.isSafe = function (charCode) {
|
||||
|
||||
return internals.safeCharCodes.has(charCode);
|
||||
};
|
||||
|
||||
|
||||
internals.namedHtml = new Map([
|
||||
[38, '&'],
|
||||
[60, '<'],
|
||||
[62, '>'],
|
||||
[34, '"'],
|
||||
[160, ' '],
|
||||
[162, '¢'],
|
||||
[163, '£'],
|
||||
[164, '¤'],
|
||||
[169, '©'],
|
||||
[174, '®']
|
||||
]);
|
||||
|
||||
|
||||
internals.safeCharCodes = (function () {
|
||||
|
||||
const safe = new Set();
|
||||
|
||||
for (let i = 32; i < 123; ++i) {
|
||||
|
||||
if ((i >= 97) || // a-z
|
||||
(i >= 65 && i <= 90) || // A-Z
|
||||
(i >= 48 && i <= 57) || // 0-9
|
||||
i === 32 || // space
|
||||
i === 46 || // .
|
||||
i === 44 || // ,
|
||||
i === 45 || // -
|
||||
i === 58 || // :
|
||||
i === 95) { // _
|
||||
|
||||
safe.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
return safe;
|
||||
}());
|
||||
Generated
Vendored
-28
@@ -1,28 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (input) {
|
||||
|
||||
if (!input) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return input.replace(/[<>&\u2028\u2029]/g, internals.escape);
|
||||
};
|
||||
|
||||
|
||||
internals.escape = function (char) {
|
||||
|
||||
return internals.replacements.get(char);
|
||||
};
|
||||
|
||||
|
||||
internals.replacements = new Map([
|
||||
['<', '\\u003c'],
|
||||
['>', '\\u003e'],
|
||||
['&', '\\u0026'],
|
||||
['\u2028', '\\u2028'],
|
||||
['\u2029', '\\u2029']
|
||||
]);
|
||||
Generated
Vendored
-11
@@ -1,11 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (string) {
|
||||
|
||||
// Escape ^$.*+-?=!:|\/()[]{},
|
||||
|
||||
return string.replace(/[\^\$\.\*\+\-\?\=\!\:\|\\\/\(\)\[\]\{\}\,]/g, '\\$&');
|
||||
};
|
||||
Generated
Vendored
-20
@@ -1,20 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = internals.flatten = function (array, target) {
|
||||
|
||||
const result = target || [];
|
||||
|
||||
for (const entry of array) {
|
||||
if (Array.isArray(entry)) {
|
||||
internals.flatten(entry, result);
|
||||
}
|
||||
else {
|
||||
result.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
Generated
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function () { };
|
||||
Generated
Vendored
-471
@@ -1,471 +0,0 @@
|
||||
/// <reference types="node" />
|
||||
|
||||
|
||||
/**
|
||||
* Performs a deep comparison of the two values including support for circular dependencies, prototype, and enumerable properties.
|
||||
*
|
||||
* @param obj - The value being compared.
|
||||
* @param ref - The reference value used for comparison.
|
||||
*
|
||||
* @return true when the two values are equal, otherwise false.
|
||||
*/
|
||||
export function deepEqual(obj: any, ref: any, options?: deepEqual.Options): boolean;
|
||||
|
||||
export namespace deepEqual {
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* Compare functions with difference references by comparing their internal code and properties.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly deepFunction?: boolean;
|
||||
|
||||
/**
|
||||
* Allow partial match.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly part?: boolean;
|
||||
|
||||
/**
|
||||
* Compare the objects' prototypes.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly prototype?: boolean;
|
||||
|
||||
/**
|
||||
* List of object keys to ignore different values of.
|
||||
*
|
||||
* @default null
|
||||
*/
|
||||
readonly skip?: (string | symbol)[];
|
||||
|
||||
/**
|
||||
* Compare symbol properties.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly symbols?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clone any value, object, or array.
|
||||
*
|
||||
* @param obj - The value being cloned.
|
||||
* @param options - Optional settings.
|
||||
*
|
||||
* @returns A deep clone of `obj`.
|
||||
*/
|
||||
export function clone<T>(obj: T, options?: clone.Options): T;
|
||||
|
||||
export namespace clone {
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* Clone the object's prototype.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly prototype?: boolean;
|
||||
|
||||
/**
|
||||
* Include symbol properties.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly symbols?: boolean;
|
||||
|
||||
/**
|
||||
* Shallow clone the specified keys.
|
||||
*
|
||||
* @default undefined
|
||||
*/
|
||||
readonly shallow?: string[] | string[][] | boolean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merge all the properties of source into target.
|
||||
*
|
||||
* @param target - The object being modified.
|
||||
* @param source - The object used to copy properties from.
|
||||
* @param options - Optional settings.
|
||||
*
|
||||
* @returns The `target` object.
|
||||
*/
|
||||
export function merge<T1 extends object, T2 extends object>(target: T1, source: T2, options?: merge.Options): T1 & T2;
|
||||
|
||||
export namespace merge {
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* When true, null value from `source` overrides existing value in `target`.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly nullOverride?: boolean;
|
||||
|
||||
/**
|
||||
* When true, array value from `source` is merged with the existing value in `target`.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly mergeArrays?: boolean;
|
||||
|
||||
/**
|
||||
* Compare symbol properties.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly symbols?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply source to a copy of the defaults.
|
||||
*
|
||||
* @param defaults - An object with the default values to use of `options` does not contain the same keys.
|
||||
* @param source - The source used to override the `defaults`.
|
||||
* @param options - Optional settings.
|
||||
*
|
||||
* @returns A copy of `defaults` with `source` keys overriding any conflicts.
|
||||
*/
|
||||
export function applyToDefaults<T extends object>(defaults: Partial<T>, source: Partial<T> | boolean | null, options?: applyToDefaults.Options): Partial<T>;
|
||||
|
||||
export namespace applyToDefaults {
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* When true, null value from `source` overrides existing value in `target`.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly nullOverride?: boolean;
|
||||
|
||||
/**
|
||||
* Shallow clone the specified keys.
|
||||
*
|
||||
* @default undefined
|
||||
*/
|
||||
readonly shallow?: string[] | string[][];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the common unique items in two arrays.
|
||||
*
|
||||
* @param array1 - The first array to compare.
|
||||
* @param array2 - The second array to compare.
|
||||
* @param options - Optional settings.
|
||||
*
|
||||
* @return - An array of the common items. If `justFirst` is true, returns the first common item.
|
||||
*/
|
||||
export function intersect<T1, T2>(array1: intersect.Array<T1>, array2: intersect.Array<T2>, options?: intersect.Options): Array<T1 | T2>;
|
||||
export function intersect<T1, T2>(array1: intersect.Array<T1>, array2: intersect.Array<T2>, options?: intersect.Options): T1 | T2;
|
||||
|
||||
export namespace intersect {
|
||||
|
||||
type Array<T> = ArrayLike<T> | Set<T> | null;
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* When true, return the first overlapping value.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly first?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the reference value contains the provided values.
|
||||
*
|
||||
* @param ref - The reference string, array, or object.
|
||||
* @param values - A single or array of values to find within `ref`. If `ref` is an object, `values` can be a key name, an array of key names, or an object with key-value pairs to compare.
|
||||
*
|
||||
* @return true if the value contains the provided values, otherwise false.
|
||||
*/
|
||||
export function contain(ref: string, values: string | string[], options?: contain.Options): boolean;
|
||||
export function contain(ref: any[], values: any, options?: contain.Options): boolean;
|
||||
export function contain(ref: object, values: string | string[] | object, options?: Omit<contain.Options, 'once'>): boolean;
|
||||
|
||||
export namespace contain {
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* Perform a deep comparison.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly deep?: boolean;
|
||||
|
||||
/**
|
||||
* Allow only one occurrence of each value.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly once?: boolean;
|
||||
|
||||
/**
|
||||
* Allow only values explicitly listed.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly only?: boolean;
|
||||
|
||||
/**
|
||||
* Allow partial match.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly part?: boolean;
|
||||
|
||||
/**
|
||||
* Include symbol properties.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly symbols?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flatten an array with sub arrays
|
||||
*
|
||||
* @param array - an array of items or other arrays to flatten.
|
||||
* @param target - if provided, an array to shallow copy the flattened `array` items to
|
||||
*
|
||||
* @return a flat array of the provided values (appended to `target` is provided).
|
||||
*/
|
||||
export function flatten<T>(array: ArrayLike<T | ReadonlyArray<T>>, target?: ArrayLike<T | ReadonlyArray<T>>): T[];
|
||||
|
||||
|
||||
/**
|
||||
* Convert an object key chain string to reference.
|
||||
*
|
||||
* @param obj - the object from which to look up the value.
|
||||
* @param chain - the string path of the requested value. The chain string is split into key names using `options.separator`, or an array containing each individual key name. A chain including negative numbers will work like a negative index on an array.
|
||||
*
|
||||
* @return The value referenced by the chain if found, otherwise undefined. If chain is null, undefined, or false, the object itself will be returned.
|
||||
*/
|
||||
export function reach(obj: object | null, chain: string | (string | number)[] | false | null | undefined, options?: reach.Options): any;
|
||||
|
||||
export namespace reach {
|
||||
|
||||
interface Options {
|
||||
|
||||
/**
|
||||
* String to split chain path on. Defaults to '.'.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly separator?: string;
|
||||
|
||||
/**
|
||||
* Value to return if the path or value is not present. No default value.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly default?: any;
|
||||
|
||||
/**
|
||||
* If true, will throw an error on missing member in the chain. Default to false.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly strict?: boolean;
|
||||
|
||||
/**
|
||||
* If true, allows traversing functions for properties. false will throw an error if a function is part of the chain.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
readonly functions?: boolean;
|
||||
|
||||
/**
|
||||
* If true, allows traversing Set and Map objects for properties. false will return undefined regardless of the Set or Map passed.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
readonly iterables?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace string parameters (using format "{path.to.key}") with their corresponding object key values using `Hoek.reach()`.
|
||||
*
|
||||
* @param obj - the object from which to look up the value.
|
||||
* @param template - the string containing {} enclosed key paths to be replaced.
|
||||
*
|
||||
* @return The template string with the {} enclosed keys replaced with looked-up values.
|
||||
*/
|
||||
export function reachTemplate(obj: object | null, template: string, options?: reach.Options): string;
|
||||
|
||||
|
||||
/**
|
||||
* Throw an error if condition is falsy.
|
||||
*
|
||||
* @param condition - If `condition` is not truthy, an exception is thrown.
|
||||
* @param error - The error thrown if the condition fails.
|
||||
*
|
||||
* @return Does not return a value but throws if the `condition` is falsy.
|
||||
*/
|
||||
export function assert(condition: any, error: Error): void;
|
||||
|
||||
|
||||
/**
|
||||
* Throw an error if condition is falsy.
|
||||
*
|
||||
* @param condition - If `condition` is not truthy, an exception is thrown.
|
||||
* @param args - Any number of values, concatenated together (space separated) to create the error message.
|
||||
*
|
||||
* @return Does not return a value but throws if the `condition` is falsy.
|
||||
*/
|
||||
export function assert(condition: any, ...args: any): void;
|
||||
|
||||
|
||||
/**
|
||||
* A benchmarking timer, using the internal node clock for maximum accuracy.
|
||||
*/
|
||||
export class Bench {
|
||||
|
||||
constructor();
|
||||
|
||||
/** The starting timestamp expressed in the number of milliseconds since the epoch. */
|
||||
ts: number;
|
||||
|
||||
/** The time in milliseconds since the object was created. */
|
||||
elapsed(): number;
|
||||
|
||||
/** Reset the `ts` value to now. */
|
||||
reset(): void;
|
||||
|
||||
/** The current time in milliseconds since the epoch. */
|
||||
static now(): number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escape string for Regex construction by prefixing all reserved characters with a backslash.
|
||||
*
|
||||
* @param string - The string to be escaped.
|
||||
*
|
||||
* @return The escaped string.
|
||||
*/
|
||||
export function escapeRegex(string: string): string;
|
||||
|
||||
|
||||
/**
|
||||
* Escape string for usage as an attribute value in HTTP headers.
|
||||
*
|
||||
* @param attribute - The string to be escaped.
|
||||
*
|
||||
* @return The escaped string. Will throw on invalid characters that are not supported to be escaped.
|
||||
*/
|
||||
export function escapeHeaderAttribute(attribute: string): string;
|
||||
|
||||
|
||||
/**
|
||||
* Escape string for usage in HTML.
|
||||
*
|
||||
* @param string - The string to be escaped.
|
||||
*
|
||||
* @return The escaped string.
|
||||
*/
|
||||
export function escapeHtml(string: string): string;
|
||||
|
||||
|
||||
/**
|
||||
* Escape string for usage in JSON.
|
||||
*
|
||||
* @param string - The string to be escaped.
|
||||
*
|
||||
* @return The escaped string.
|
||||
*/
|
||||
export function escapeJson(string: string): string;
|
||||
|
||||
|
||||
/**
|
||||
* Wraps a function to ensure it can only execute once.
|
||||
*
|
||||
* @param method - The function to be wrapped.
|
||||
*
|
||||
* @return The wrapped function.
|
||||
*/
|
||||
export function once<T extends Function>(method: T): T;
|
||||
|
||||
|
||||
/**
|
||||
* A reusable no-op function.
|
||||
*/
|
||||
export function ignore(...ignore: any): void;
|
||||
|
||||
|
||||
/**
|
||||
* Converts a JavaScript value to a JavaScript Object Notation (JSON) string with protection against thrown errors.
|
||||
*
|
||||
* @param value A JavaScript value, usually an object or array, to be converted.
|
||||
* @param replacer The JSON.stringify() `replacer` argument.
|
||||
* @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
|
||||
*
|
||||
* @return The JSON string. If the operation fails, an error string value is returned (no exception thrown).
|
||||
*/
|
||||
export function stringify(value: any, replacer?: any, space?: string | number): string;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves after the requested timeout.
|
||||
*
|
||||
* @param timeout - The number of milliseconds to wait before resolving the Promise.
|
||||
* @param returnValue - The value that the Promise will resolve to.
|
||||
*
|
||||
* @return A Promise that resolves with `returnValue`.
|
||||
*/
|
||||
export function wait<T>(timeout?: number, returnValue?: T): Promise<T>;
|
||||
|
||||
|
||||
/**
|
||||
* Returns a Promise that never resolves.
|
||||
*/
|
||||
export function block(): Promise<void>;
|
||||
|
||||
|
||||
/**
|
||||
* Determines if an object is a promise.
|
||||
*
|
||||
* @param promise - the object tested.
|
||||
*
|
||||
* @returns true if the object is a promise, otherwise false.
|
||||
*/
|
||||
export function isPromise(promise: any): boolean;
|
||||
|
||||
|
||||
export namespace ts {
|
||||
|
||||
/**
|
||||
* Defines a type that can must be one of T or U but not both.
|
||||
*/
|
||||
type XOR<T, U> = (T | U) extends object ? (internals.Without<T, U> & U) | (internals.Without<U, T> & T) : T | U;
|
||||
}
|
||||
|
||||
|
||||
declare namespace internals {
|
||||
|
||||
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
|
||||
}
|
||||
Generated
Vendored
-45
@@ -1,45 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
exports.applyToDefaults = require('./applyToDefaults');
|
||||
|
||||
exports.assert = require('./assert');
|
||||
|
||||
exports.Bench = require('./bench');
|
||||
|
||||
exports.block = require('./block');
|
||||
|
||||
exports.clone = require('./clone');
|
||||
|
||||
exports.contain = require('./contain');
|
||||
|
||||
exports.deepEqual = require('./deepEqual');
|
||||
|
||||
exports.Error = require('./error');
|
||||
|
||||
exports.escapeHeaderAttribute = require('./escapeHeaderAttribute');
|
||||
|
||||
exports.escapeHtml = require('./escapeHtml');
|
||||
|
||||
exports.escapeJson = require('./escapeJson');
|
||||
|
||||
exports.escapeRegex = require('./escapeRegex');
|
||||
|
||||
exports.flatten = require('./flatten');
|
||||
|
||||
exports.ignore = require('./ignore');
|
||||
|
||||
exports.intersect = require('./intersect');
|
||||
|
||||
exports.isPromise = require('./isPromise');
|
||||
|
||||
exports.merge = require('./merge');
|
||||
|
||||
exports.once = require('./once');
|
||||
|
||||
exports.reach = require('./reach');
|
||||
|
||||
exports.reachTemplate = require('./reachTemplate');
|
||||
|
||||
exports.stringify = require('./stringify');
|
||||
|
||||
exports.wait = require('./wait');
|
||||
Generated
Vendored
-41
@@ -1,41 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (array1, array2, options = {}) {
|
||||
|
||||
if (!array1 ||
|
||||
!array2) {
|
||||
|
||||
return (options.first ? null : []);
|
||||
}
|
||||
|
||||
const common = [];
|
||||
const hash = (Array.isArray(array1) ? new Set(array1) : array1);
|
||||
const found = new Set();
|
||||
for (const value of array2) {
|
||||
if (internals.has(hash, value) &&
|
||||
!found.has(value)) {
|
||||
|
||||
if (options.first) {
|
||||
return value;
|
||||
}
|
||||
|
||||
common.push(value);
|
||||
found.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
return (options.first ? null : common);
|
||||
};
|
||||
|
||||
|
||||
internals.has = function (ref, key) {
|
||||
|
||||
if (typeof ref.has === 'function') {
|
||||
return ref.has(key);
|
||||
}
|
||||
|
||||
return ref[key] !== undefined;
|
||||
};
|
||||
Generated
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (promise) {
|
||||
|
||||
return !!promise && typeof promise.then === 'function';
|
||||
};
|
||||
Generated
Vendored
-78
@@ -1,78 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Assert = require('./assert');
|
||||
const Clone = require('./clone');
|
||||
const Utils = require('./utils');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = internals.merge = function (target, source, options) {
|
||||
|
||||
Assert(target && typeof target === 'object', 'Invalid target value: must be an object');
|
||||
Assert(source === null || source === undefined || typeof source === 'object', 'Invalid source value: must be null, undefined, or an object');
|
||||
|
||||
if (!source) {
|
||||
return target;
|
||||
}
|
||||
|
||||
options = Object.assign({ nullOverride: true, mergeArrays: true }, options);
|
||||
|
||||
if (Array.isArray(source)) {
|
||||
Assert(Array.isArray(target), 'Cannot merge array onto an object');
|
||||
if (!options.mergeArrays) {
|
||||
target.length = 0; // Must not change target assignment
|
||||
}
|
||||
|
||||
for (let i = 0; i < source.length; ++i) {
|
||||
target.push(Clone(source[i], { symbols: options.symbols }));
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
const keys = Utils.keys(source, options);
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
const key = keys[i];
|
||||
if (key === '__proto__' ||
|
||||
!Object.prototype.propertyIsEnumerable.call(source, key)) {
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = source[key];
|
||||
if (value &&
|
||||
typeof value === 'object') {
|
||||
|
||||
if (target[key] === value) {
|
||||
continue; // Can occur for shallow merges
|
||||
}
|
||||
|
||||
if (!target[key] ||
|
||||
typeof target[key] !== 'object' ||
|
||||
(Array.isArray(target[key]) !== Array.isArray(value)) ||
|
||||
value instanceof Date ||
|
||||
(Buffer && Buffer.isBuffer(value)) || // $lab:coverage:ignore$
|
||||
value instanceof RegExp) {
|
||||
|
||||
target[key] = Clone(value, { symbols: options.symbols });
|
||||
}
|
||||
else {
|
||||
internals.merge(target[key], value, options);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (value !== null &&
|
||||
value !== undefined) { // Explicit to preserve empty strings
|
||||
|
||||
target[key] = value;
|
||||
}
|
||||
else if (options.nullOverride) {
|
||||
target[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
Generated
Vendored
-25
@@ -1,25 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {
|
||||
wrapped: Symbol('wrapped')
|
||||
};
|
||||
|
||||
|
||||
module.exports = function (method) {
|
||||
|
||||
if (method[internals.wrapped]) {
|
||||
return method;
|
||||
}
|
||||
|
||||
let once = false;
|
||||
const wrappedFn = function (...args) {
|
||||
|
||||
if (!once) {
|
||||
once = true;
|
||||
method(...args);
|
||||
}
|
||||
};
|
||||
|
||||
wrappedFn[internals.wrapped] = true;
|
||||
return wrappedFn;
|
||||
};
|
||||
Generated
Vendored
-76
@@ -1,76 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Assert = require('./assert');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (obj, chain, options) {
|
||||
|
||||
if (chain === false ||
|
||||
chain === null ||
|
||||
chain === undefined) {
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
if (typeof options === 'string') {
|
||||
options = { separator: options };
|
||||
}
|
||||
|
||||
const isChainArray = Array.isArray(chain);
|
||||
|
||||
Assert(!isChainArray || !options.separator, 'Separator option is not valid for array-based chain');
|
||||
|
||||
const path = isChainArray ? chain : chain.split(options.separator || '.');
|
||||
let ref = obj;
|
||||
for (let i = 0; i < path.length; ++i) {
|
||||
let key = path[i];
|
||||
const type = options.iterables && internals.iterables(ref);
|
||||
|
||||
if (Array.isArray(ref) ||
|
||||
type === 'set') {
|
||||
|
||||
const number = Number(key);
|
||||
if (Number.isInteger(number)) {
|
||||
key = number < 0 ? ref.length + number : number;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ref ||
|
||||
typeof ref === 'function' && options.functions === false || // Defaults to true
|
||||
!type && ref[key] === undefined) {
|
||||
|
||||
Assert(!options.strict || i + 1 === path.length, 'Missing segment', key, 'in reach path ', chain);
|
||||
Assert(typeof ref === 'object' || options.functions === true || typeof ref !== 'function', 'Invalid segment', key, 'in reach path ', chain);
|
||||
ref = options.default;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
ref = ref[key];
|
||||
}
|
||||
else if (type === 'set') {
|
||||
ref = [...ref][key];
|
||||
}
|
||||
else { // type === 'map'
|
||||
ref = ref.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
return ref;
|
||||
};
|
||||
|
||||
|
||||
internals.iterables = function (ref) {
|
||||
|
||||
if (ref instanceof Set) {
|
||||
return 'set';
|
||||
}
|
||||
|
||||
if (ref instanceof Map) {
|
||||
return 'map';
|
||||
}
|
||||
};
|
||||
Generated
Vendored
-16
@@ -1,16 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const Reach = require('./reach');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (obj, template, options) {
|
||||
|
||||
return template.replace(/{([^{}]+)}/g, ($0, chain) => {
|
||||
|
||||
const value = Reach(obj, chain, options);
|
||||
return (value === undefined || value === null ? '' : value);
|
||||
});
|
||||
};
|
||||
Generated
Vendored
-14
@@ -1,14 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
module.exports = function (...args) {
|
||||
|
||||
try {
|
||||
return JSON.stringify(...args);
|
||||
}
|
||||
catch (err) {
|
||||
return '[Cannot display object: ' + err.message + ']';
|
||||
}
|
||||
};
|
||||
Generated
Vendored
-55
@@ -1,55 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports = module.exports = {
|
||||
array: Array.prototype,
|
||||
buffer: Buffer && Buffer.prototype, // $lab:coverage:ignore$
|
||||
date: Date.prototype,
|
||||
error: Error.prototype,
|
||||
generic: Object.prototype,
|
||||
map: Map.prototype,
|
||||
promise: Promise.prototype,
|
||||
regex: RegExp.prototype,
|
||||
set: Set.prototype,
|
||||
weakMap: WeakMap.prototype,
|
||||
weakSet: WeakSet.prototype
|
||||
};
|
||||
|
||||
|
||||
internals.typeMap = new Map([
|
||||
['[object Error]', exports.error],
|
||||
['[object Map]', exports.map],
|
||||
['[object Promise]', exports.promise],
|
||||
['[object Set]', exports.set],
|
||||
['[object WeakMap]', exports.weakMap],
|
||||
['[object WeakSet]', exports.weakSet]
|
||||
]);
|
||||
|
||||
|
||||
exports.getInternalProto = function (obj) {
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return exports.array;
|
||||
}
|
||||
|
||||
if (Buffer && obj instanceof Buffer) { // $lab:coverage:ignore$
|
||||
return exports.buffer;
|
||||
}
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return exports.date;
|
||||
}
|
||||
|
||||
if (obj instanceof RegExp) {
|
||||
return exports.regex;
|
||||
}
|
||||
|
||||
if (obj instanceof Error) {
|
||||
return exports.error;
|
||||
}
|
||||
|
||||
const objName = Object.prototype.toString.call(obj);
|
||||
return internals.typeMap.get(objName) || exports.generic;
|
||||
};
|
||||
Generated
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports.keys = function (obj, options = {}) {
|
||||
|
||||
return options.symbols !== false ? Reflect.ownKeys(obj) : Object.getOwnPropertyNames(obj); // Defaults to true
|
||||
};
|
||||
Generated
Vendored
-37
@@ -1,37 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {
|
||||
maxTimer: 2 ** 31 - 1 // ~25 days
|
||||
};
|
||||
|
||||
|
||||
module.exports = function (timeout, returnValue, options) {
|
||||
|
||||
if (typeof timeout === 'bigint') {
|
||||
timeout = Number(timeout);
|
||||
}
|
||||
|
||||
if (timeout >= Number.MAX_SAFE_INTEGER) { // Thousands of years
|
||||
timeout = Infinity;
|
||||
}
|
||||
|
||||
if (typeof timeout !== 'number' && timeout !== undefined) {
|
||||
throw new TypeError('Timeout must be a number or bigint');
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
|
||||
const _setTimeout = options ? options.setTimeout : setTimeout;
|
||||
|
||||
const activate = () => {
|
||||
|
||||
const time = Math.min(timeout, internals.maxTimer);
|
||||
timeout -= time;
|
||||
_setTimeout(() => (timeout > 0 ? activate() : resolve(returnValue)), time);
|
||||
};
|
||||
|
||||
if (timeout !== Infinity) {
|
||||
activate();
|
||||
}
|
||||
});
|
||||
};
|
||||
Generated
Vendored
-31
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "@hapi/hoek",
|
||||
"description": "General purpose node utilities",
|
||||
"version": "9.3.0",
|
||||
"repository": "git://github.com/hapijs/hoek",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"keywords": [
|
||||
"utilities"
|
||||
],
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"plugin:@hapi/module"
|
||||
]
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@hapi/code": "8.x.x",
|
||||
"@hapi/eslint-plugin": "*",
|
||||
"@hapi/lab": "^24.0.0",
|
||||
"typescript": "~4.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "lab -a @hapi/code -t 100 -L -Y",
|
||||
"test-cov-html": "lab -a @hapi/code -t 100 -L -r html -o coverage.html"
|
||||
},
|
||||
"license": "BSD-3-Clause"
|
||||
}
|
||||
Generated
Vendored
-82
@@ -1,82 +0,0 @@
|
||||
# Licensing
|
||||
|
||||
## color
|
||||
|
||||
Copyright (c) 2012 Heather Arthur
|
||||
|
||||
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.
|
||||
|
||||
## color-convert
|
||||
|
||||
Copyright (c) 2011-2016 Heather Arthur <fayearthur@gmail.com>.
|
||||
Copyright (c) 2016-2021 Josh Junon <josh@junon.me>.
|
||||
|
||||
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.
|
||||
|
||||
## color-string
|
||||
|
||||
Copyright (c) 2011 Heather Arthur <fayearthur@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
## color-name
|
||||
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2015 Dmitry Ivanov
|
||||
|
||||
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.
|
||||
Generated
Vendored
-15
@@ -1,15 +0,0 @@
|
||||
# `@img/colour`
|
||||
|
||||
The latest version of the
|
||||
[color](https://www.npmjs.com/package/color)
|
||||
package is now ESM-only,
|
||||
however some JavaScript runtimes do not yet support this,
|
||||
which includes versions of Node.js prior to 20.19.0.
|
||||
|
||||
This package converts the `color` package and its dependencies,
|
||||
all of which are MIT-licensed, to CommonJS.
|
||||
|
||||
- [color](https://www.npmjs.com/package/color)
|
||||
- [color-convert](https://www.npmjs.com/package/color-convert)
|
||||
- [color-string](https://www.npmjs.com/package/color-string)
|
||||
- [color-name](https://www.npmjs.com/package/color-name)
|
||||
Generated
Vendored
-1596
File diff suppressed because it is too large
Load Diff
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
module.exports = require("./color.cjs").default;
|
||||
Generated
Vendored
-929
@@ -1,929 +0,0 @@
|
||||
// Generated by dts-bundle-generator v9.5.1
|
||||
|
||||
type Channels = number;
|
||||
type RGB = [
|
||||
r: number,
|
||||
g: number,
|
||||
b: number
|
||||
];
|
||||
type HSL = [
|
||||
h: number,
|
||||
s: number,
|
||||
l: number
|
||||
];
|
||||
type HSV = [
|
||||
h: number,
|
||||
s: number,
|
||||
v: number
|
||||
];
|
||||
type CMYK = [
|
||||
c: number,
|
||||
m: number,
|
||||
y: number,
|
||||
k: number
|
||||
];
|
||||
type LAB = [
|
||||
l: number,
|
||||
a: number,
|
||||
b: number
|
||||
];
|
||||
type LCH = [
|
||||
l: number,
|
||||
c: number,
|
||||
h: number
|
||||
];
|
||||
type HCG = [
|
||||
h: number,
|
||||
c: number,
|
||||
g: number
|
||||
];
|
||||
type HWB = [
|
||||
h: number,
|
||||
w: number,
|
||||
b: number
|
||||
];
|
||||
type XYZ = [
|
||||
x: number,
|
||||
y: number,
|
||||
z: number
|
||||
];
|
||||
type Apple = [
|
||||
r16: number,
|
||||
g16: number,
|
||||
b16: number
|
||||
];
|
||||
type Gray = [
|
||||
gray: number
|
||||
];
|
||||
type ANSI16 = number;
|
||||
type ANSI256 = number;
|
||||
type Keyword = string;
|
||||
type HEX = string;
|
||||
declare namespace route {
|
||||
type rgb = {
|
||||
hsl(from: RGB): HSL;
|
||||
hsl(...from: RGB): HSL;
|
||||
hsl(from: RGB): HSL;
|
||||
hsl(...from: RGB): HSL;
|
||||
hsv(from: RGB): HSV;
|
||||
hsv(...from: RGB): HSV;
|
||||
hwb(from: RGB): HWB;
|
||||
hwb(...from: RGB): HWB;
|
||||
cmyk(from: RGB): CMYK;
|
||||
cmyk(...from: RGB): CMYK;
|
||||
xyz(from: RGB): XYZ;
|
||||
xyz(...from: RGB): XYZ;
|
||||
lab(from: RGB): LAB;
|
||||
lab(...from: RGB): LAB;
|
||||
lch(from: RGB): LCH;
|
||||
lch(...from: RGB): LCH;
|
||||
hex(from: RGB): HEX;
|
||||
hex(...from: RGB): HEX;
|
||||
keyword(from: RGB): Keyword;
|
||||
keyword(...from: RGB): Keyword;
|
||||
ansi16(from: RGB): ANSI16;
|
||||
ansi16(...from: RGB): ANSI16;
|
||||
ansi256(from: RGB): ANSI256;
|
||||
ansi256(...from: RGB): ANSI256;
|
||||
hcg(from: RGB): HCG;
|
||||
hcg(...from: RGB): HCG;
|
||||
apple(from: RGB): Apple;
|
||||
apple(...from: RGB): Apple;
|
||||
gray(from: RGB): Gray;
|
||||
gray(...from: RGB): Gray;
|
||||
};
|
||||
type hsl = {
|
||||
rgb(from: HSL): RGB;
|
||||
rgb(...from: HSL): RGB;
|
||||
hsv(from: HSL): HSV;
|
||||
hsv(...from: HSL): HSV;
|
||||
hwb(from: HSL): HWB;
|
||||
hwb(...from: HSL): HWB;
|
||||
cmyk(from: HSL): CMYK;
|
||||
cmyk(...from: HSL): CMYK;
|
||||
xyz(from: HSL): XYZ;
|
||||
xyz(...from: HSL): XYZ;
|
||||
lab(from: HSL): LAB;
|
||||
lab(...from: HSL): LAB;
|
||||
lch(from: HSL): LCH;
|
||||
lch(...from: HSL): LCH;
|
||||
hex(from: HSL): HEX;
|
||||
hex(...from: HSL): HEX;
|
||||
keyword(from: HSL): Keyword;
|
||||
keyword(...from: HSL): Keyword;
|
||||
ansi16(from: HSL): ANSI16;
|
||||
ansi16(...from: HSL): ANSI16;
|
||||
ansi256(from: HSL): ANSI256;
|
||||
ansi256(...from: HSL): ANSI256;
|
||||
hcg(from: HSL): HCG;
|
||||
hcg(...from: HSL): HCG;
|
||||
apple(from: HSL): Apple;
|
||||
apple(...from: HSL): Apple;
|
||||
gray(from: HSL): Gray;
|
||||
gray(...from: HSL): Gray;
|
||||
};
|
||||
type hsv = {
|
||||
rgb(from: HSV): RGB;
|
||||
rgb(...from: HSV): RGB;
|
||||
hsl(from: HSV): HSL;
|
||||
hsl(...from: HSV): HSL;
|
||||
hwb(from: HSV): HWB;
|
||||
hwb(...from: HSV): HWB;
|
||||
cmyk(from: HSV): CMYK;
|
||||
cmyk(...from: HSV): CMYK;
|
||||
xyz(from: HSV): XYZ;
|
||||
xyz(...from: HSV): XYZ;
|
||||
lab(from: HSV): LAB;
|
||||
lab(...from: HSV): LAB;
|
||||
lch(from: HSV): LCH;
|
||||
lch(...from: HSV): LCH;
|
||||
hex(from: HSV): HEX;
|
||||
hex(...from: HSV): HEX;
|
||||
keyword(from: HSV): Keyword;
|
||||
keyword(...from: HSV): Keyword;
|
||||
ansi16(from: HSV): ANSI16;
|
||||
ansi16(...from: HSV): ANSI16;
|
||||
ansi256(from: HSV): ANSI256;
|
||||
ansi256(...from: HSV): ANSI256;
|
||||
hcg(from: HSV): HCG;
|
||||
hcg(...from: HSV): HCG;
|
||||
apple(from: HSV): Apple;
|
||||
apple(...from: HSV): Apple;
|
||||
gray(from: HSV): Gray;
|
||||
gray(...from: HSV): Gray;
|
||||
};
|
||||
type hwb = {
|
||||
rgb(from: HWB): RGB;
|
||||
rgb(...from: HWB): RGB;
|
||||
hsl(from: HWB): HSL;
|
||||
hsl(...from: HWB): HSL;
|
||||
hsv(from: HWB): HSV;
|
||||
hsv(...from: HWB): HSV;
|
||||
cmyk(from: HWB): CMYK;
|
||||
cmyk(...from: HWB): CMYK;
|
||||
xyz(from: HWB): XYZ;
|
||||
xyz(...from: HWB): XYZ;
|
||||
lab(from: HWB): LAB;
|
||||
lab(...from: HWB): LAB;
|
||||
lch(from: HWB): LCH;
|
||||
lch(...from: HWB): LCH;
|
||||
hex(from: HWB): HEX;
|
||||
hex(...from: HWB): HEX;
|
||||
keyword(from: HWB): Keyword;
|
||||
keyword(...from: HWB): Keyword;
|
||||
ansi16(from: HWB): ANSI16;
|
||||
ansi16(...from: HWB): ANSI16;
|
||||
ansi256(from: HWB): ANSI256;
|
||||
ansi256(...from: HWB): ANSI256;
|
||||
hcg(from: HWB): HCG;
|
||||
hcg(...from: HWB): HCG;
|
||||
apple(from: HWB): Apple;
|
||||
apple(...from: HWB): Apple;
|
||||
gray(from: HWB): Gray;
|
||||
gray(...from: HWB): Gray;
|
||||
};
|
||||
type cmyk = {
|
||||
rgb(from: CMYK): RGB;
|
||||
rgb(...from: CMYK): RGB;
|
||||
hsl(from: CMYK): HSL;
|
||||
hsl(...from: CMYK): HSL;
|
||||
hsv(from: CMYK): HSV;
|
||||
hsv(...from: CMYK): HSV;
|
||||
hwb(from: CMYK): HWB;
|
||||
hwb(...from: CMYK): HWB;
|
||||
xyz(from: CMYK): XYZ;
|
||||
xyz(...from: CMYK): XYZ;
|
||||
lab(from: CMYK): LAB;
|
||||
lab(...from: CMYK): LAB;
|
||||
lch(from: CMYK): LCH;
|
||||
lch(...from: CMYK): LCH;
|
||||
hex(from: CMYK): HEX;
|
||||
hex(...from: CMYK): HEX;
|
||||
keyword(from: CMYK): Keyword;
|
||||
keyword(...from: CMYK): Keyword;
|
||||
ansi16(from: CMYK): ANSI16;
|
||||
ansi16(...from: CMYK): ANSI16;
|
||||
ansi256(from: CMYK): ANSI256;
|
||||
ansi256(...from: CMYK): ANSI256;
|
||||
hcg(from: CMYK): HCG;
|
||||
hcg(...from: CMYK): HCG;
|
||||
apple(from: CMYK): Apple;
|
||||
apple(...from: CMYK): Apple;
|
||||
gray(from: CMYK): Gray;
|
||||
gray(...from: CMYK): Gray;
|
||||
};
|
||||
type xyz = {
|
||||
rgb(from: XYZ): RGB;
|
||||
rgb(...from: XYZ): RGB;
|
||||
hsl(from: XYZ): HSL;
|
||||
hsl(...from: XYZ): HSL;
|
||||
hsv(from: XYZ): HSV;
|
||||
hsv(...from: XYZ): HSV;
|
||||
hwb(from: XYZ): HWB;
|
||||
hwb(...from: XYZ): HWB;
|
||||
cmyk(from: XYZ): CMYK;
|
||||
cmyk(...from: XYZ): CMYK;
|
||||
lab(from: XYZ): LAB;
|
||||
lab(...from: XYZ): LAB;
|
||||
lch(from: XYZ): LCH;
|
||||
lch(...from: XYZ): LCH;
|
||||
hex(from: XYZ): HEX;
|
||||
hex(...from: XYZ): HEX;
|
||||
keyword(from: XYZ): Keyword;
|
||||
keyword(...from: XYZ): Keyword;
|
||||
ansi16(from: XYZ): ANSI16;
|
||||
ansi16(...from: XYZ): ANSI16;
|
||||
ansi256(from: XYZ): ANSI256;
|
||||
ansi256(...from: XYZ): ANSI256;
|
||||
hcg(from: XYZ): HCG;
|
||||
hcg(...from: XYZ): HCG;
|
||||
apple(from: XYZ): Apple;
|
||||
apple(...from: XYZ): Apple;
|
||||
gray(from: XYZ): Gray;
|
||||
gray(...from: XYZ): Gray;
|
||||
};
|
||||
type lab = {
|
||||
rgb(from: LAB): RGB;
|
||||
rgb(...from: LAB): RGB;
|
||||
hsl(from: LAB): HSL;
|
||||
hsl(...from: LAB): HSL;
|
||||
hsv(from: LAB): HSV;
|
||||
hsv(...from: LAB): HSV;
|
||||
hwb(from: LAB): HWB;
|
||||
hwb(...from: LAB): HWB;
|
||||
cmyk(from: LAB): CMYK;
|
||||
cmyk(...from: LAB): CMYK;
|
||||
xyz(from: LAB): XYZ;
|
||||
xyz(...from: LAB): XYZ;
|
||||
lch(from: LAB): LCH;
|
||||
lch(...from: LAB): LCH;
|
||||
hex(from: LAB): HEX;
|
||||
hex(...from: LAB): HEX;
|
||||
keyword(from: LAB): Keyword;
|
||||
keyword(...from: LAB): Keyword;
|
||||
ansi16(from: LAB): ANSI16;
|
||||
ansi16(...from: LAB): ANSI16;
|
||||
ansi256(from: LAB): ANSI256;
|
||||
ansi256(...from: LAB): ANSI256;
|
||||
hcg(from: LAB): HCG;
|
||||
hcg(...from: LAB): HCG;
|
||||
apple(from: LAB): Apple;
|
||||
apple(...from: LAB): Apple;
|
||||
gray(from: LAB): Gray;
|
||||
gray(...from: LAB): Gray;
|
||||
};
|
||||
type lch = {
|
||||
rgb(from: LCH): RGB;
|
||||
rgb(...from: LCH): RGB;
|
||||
hsl(from: LCH): HSL;
|
||||
hsl(...from: LCH): HSL;
|
||||
hsv(from: LCH): HSV;
|
||||
hsv(...from: LCH): HSV;
|
||||
hwb(from: LCH): HWB;
|
||||
hwb(...from: LCH): HWB;
|
||||
cmyk(from: LCH): CMYK;
|
||||
cmyk(...from: LCH): CMYK;
|
||||
xyz(from: LCH): XYZ;
|
||||
xyz(...from: LCH): XYZ;
|
||||
lab(from: LCH): LAB;
|
||||
lab(...from: LCH): LAB;
|
||||
hex(from: LCH): HEX;
|
||||
hex(...from: LCH): HEX;
|
||||
keyword(from: LCH): Keyword;
|
||||
keyword(...from: LCH): Keyword;
|
||||
ansi16(from: LCH): ANSI16;
|
||||
ansi16(...from: LCH): ANSI16;
|
||||
ansi256(from: LCH): ANSI256;
|
||||
ansi256(...from: LCH): ANSI256;
|
||||
hcg(from: LCH): HCG;
|
||||
hcg(...from: LCH): HCG;
|
||||
apple(from: LCH): Apple;
|
||||
apple(...from: LCH): Apple;
|
||||
gray(from: LCH): Gray;
|
||||
gray(...from: LCH): Gray;
|
||||
};
|
||||
type hex = {
|
||||
rgb(from: HEX): RGB;
|
||||
hsl(from: HEX): HSL;
|
||||
hsv(from: HEX): HSV;
|
||||
hwb(from: HEX): HWB;
|
||||
cmyk(from: HEX): CMYK;
|
||||
xyz(from: HEX): XYZ;
|
||||
lab(from: HEX): LAB;
|
||||
lch(from: HEX): LCH;
|
||||
keyword(from: HEX): Keyword;
|
||||
ansi16(from: HEX): ANSI16;
|
||||
ansi256(from: HEX): ANSI256;
|
||||
hcg(from: HEX): HCG;
|
||||
apple(from: HEX): Apple;
|
||||
gray(from: HEX): Gray;
|
||||
};
|
||||
type keyword = {
|
||||
rgb(from: Keyword): RGB;
|
||||
hsl(from: Keyword): HSL;
|
||||
hsv(from: Keyword): HSV;
|
||||
hwb(from: Keyword): HWB;
|
||||
cmyk(from: Keyword): CMYK;
|
||||
xyz(from: Keyword): XYZ;
|
||||
lab(from: Keyword): LAB;
|
||||
lch(from: Keyword): LCH;
|
||||
hex(from: Keyword): HEX;
|
||||
ansi16(from: Keyword): ANSI16;
|
||||
ansi256(from: Keyword): ANSI256;
|
||||
hcg(from: Keyword): HCG;
|
||||
apple(from: Keyword): Apple;
|
||||
gray(from: Keyword): Gray;
|
||||
};
|
||||
type ansi16 = {
|
||||
rgb(from: ANSI16): RGB;
|
||||
hsl(from: ANSI16): HSL;
|
||||
hsv(from: ANSI16): HSV;
|
||||
hwb(from: ANSI16): HWB;
|
||||
cmyk(from: ANSI16): CMYK;
|
||||
xyz(from: ANSI16): XYZ;
|
||||
lab(from: ANSI16): LAB;
|
||||
lch(from: ANSI16): LCH;
|
||||
hex(from: ANSI16): HEX;
|
||||
keyword(from: ANSI16): Keyword;
|
||||
ansi256(from: ANSI16): ANSI256;
|
||||
hcg(from: ANSI16): HCG;
|
||||
apple(from: ANSI16): Apple;
|
||||
gray(from: ANSI16): Gray;
|
||||
};
|
||||
type ansi256 = {
|
||||
rgb(from: ANSI256): RGB;
|
||||
hsl(from: ANSI256): HSL;
|
||||
hsv(from: ANSI256): HSV;
|
||||
hwb(from: ANSI256): HWB;
|
||||
cmyk(from: ANSI256): CMYK;
|
||||
xyz(from: ANSI256): XYZ;
|
||||
lab(from: ANSI256): LAB;
|
||||
lch(from: ANSI256): LCH;
|
||||
hex(from: ANSI256): HEX;
|
||||
keyword(from: ANSI256): Keyword;
|
||||
ansi16(from: ANSI256): ANSI16;
|
||||
hcg(from: ANSI256): HCG;
|
||||
apple(from: ANSI256): Apple;
|
||||
gray(from: ANSI256): Gray;
|
||||
};
|
||||
type hcg = {
|
||||
rgb(from: HCG): RGB;
|
||||
rgb(...from: HCG): RGB;
|
||||
hsl(from: HCG): HSL;
|
||||
hsl(...from: HCG): HSL;
|
||||
hsv(from: HCG): HSV;
|
||||
hsv(...from: HCG): HSV;
|
||||
hwb(from: HCG): HWB;
|
||||
hwb(...from: HCG): HWB;
|
||||
cmyk(from: HCG): CMYK;
|
||||
cmyk(...from: HCG): CMYK;
|
||||
xyz(from: HCG): XYZ;
|
||||
xyz(...from: HCG): XYZ;
|
||||
lab(from: HCG): LAB;
|
||||
lab(...from: HCG): LAB;
|
||||
lch(from: HCG): LCH;
|
||||
lch(...from: HCG): LCH;
|
||||
hex(from: HCG): HEX;
|
||||
hex(...from: HCG): HEX;
|
||||
keyword(from: HCG): Keyword;
|
||||
keyword(...from: HCG): Keyword;
|
||||
ansi16(from: HCG): ANSI16;
|
||||
ansi16(...from: HCG): ANSI16;
|
||||
ansi256(from: HCG): ANSI256;
|
||||
ansi256(...from: HCG): ANSI256;
|
||||
apple(from: HCG): Apple;
|
||||
apple(...from: HCG): Apple;
|
||||
gray(from: HCG): Gray;
|
||||
gray(...from: HCG): Gray;
|
||||
};
|
||||
type apple = {
|
||||
rgb(from: Apple): RGB;
|
||||
rgb(...from: Apple): RGB;
|
||||
hsl(from: Apple): HSL;
|
||||
hsl(...from: Apple): HSL;
|
||||
hsv(from: Apple): HSV;
|
||||
hsv(...from: Apple): HSV;
|
||||
hwb(from: Apple): HWB;
|
||||
hwb(...from: Apple): HWB;
|
||||
cmyk(from: Apple): CMYK;
|
||||
cmyk(...from: Apple): CMYK;
|
||||
xyz(from: Apple): XYZ;
|
||||
xyz(...from: Apple): XYZ;
|
||||
lab(from: Apple): LAB;
|
||||
lab(...from: Apple): LAB;
|
||||
lch(from: Apple): LCH;
|
||||
lch(...from: Apple): LCH;
|
||||
hex(from: Apple): HEX;
|
||||
hex(...from: Apple): HEX;
|
||||
keyword(from: Apple): Keyword;
|
||||
keyword(...from: Apple): Keyword;
|
||||
ansi16(from: Apple): ANSI16;
|
||||
ansi16(...from: Apple): ANSI16;
|
||||
ansi256(from: Apple): ANSI256;
|
||||
ansi256(...from: Apple): ANSI256;
|
||||
hcg(from: Apple): HCG;
|
||||
hcg(...from: Apple): HCG;
|
||||
gray(from: Apple): Gray;
|
||||
gray(...from: Apple): Gray;
|
||||
};
|
||||
type gray = {
|
||||
rgb(from: Gray): RGB;
|
||||
rgb(...from: Gray): RGB;
|
||||
hsl(from: Gray): HSL;
|
||||
hsl(...from: Gray): HSL;
|
||||
hsv(from: Gray): HSV;
|
||||
hsv(...from: Gray): HSV;
|
||||
hwb(from: Gray): HWB;
|
||||
hwb(...from: Gray): HWB;
|
||||
cmyk(from: Gray): CMYK;
|
||||
cmyk(...from: Gray): CMYK;
|
||||
xyz(from: Gray): XYZ;
|
||||
xyz(...from: Gray): XYZ;
|
||||
lab(from: Gray): LAB;
|
||||
lab(...from: Gray): LAB;
|
||||
lch(from: Gray): LCH;
|
||||
lch(...from: Gray): LCH;
|
||||
hex(from: Gray): HEX;
|
||||
hex(...from: Gray): HEX;
|
||||
keyword(from: Gray): Keyword;
|
||||
keyword(...from: Gray): Keyword;
|
||||
ansi16(from: Gray): ANSI16;
|
||||
ansi16(...from: Gray): ANSI16;
|
||||
ansi256(from: Gray): ANSI256;
|
||||
ansi256(...from: Gray): ANSI256;
|
||||
hcg(from: Gray): HCG;
|
||||
hcg(...from: Gray): HCG;
|
||||
apple(from: Gray): Apple;
|
||||
apple(...from: Gray): Apple;
|
||||
};
|
||||
}
|
||||
declare function route(fromModel: "rgb"): route.rgb;
|
||||
declare function route(fromModel: "hsl"): route.hsl;
|
||||
declare function route(fromModel: "hsv"): route.hsv;
|
||||
declare function route(fromModel: "hwb"): route.hwb;
|
||||
declare function route(fromModel: "cmyk"): route.cmyk;
|
||||
declare function route(fromModel: "xyz"): route.xyz;
|
||||
declare function route(fromModel: "lab"): route.lab;
|
||||
declare function route(fromModel: "lch"): route.lch;
|
||||
declare function route(fromModel: "hex"): route.hex;
|
||||
declare function route(fromModel: "keyword"): route.keyword;
|
||||
declare function route(fromModel: "ansi16"): route.ansi16;
|
||||
declare function route(fromModel: "ansi256"): route.ansi256;
|
||||
declare function route(fromModel: "hcg"): route.hcg;
|
||||
declare function route(fromModel: "apple"): route.apple;
|
||||
declare function route(fromModel: "gray"): route.gray;
|
||||
type Convert = {
|
||||
rgb: {
|
||||
channels: Channels;
|
||||
labels: "rgb";
|
||||
hsl: {
|
||||
(...rgb: RGB): HSL;
|
||||
raw: (...rgb: RGB) => HSL;
|
||||
};
|
||||
hsv: {
|
||||
(...rgb: RGB): HSV;
|
||||
raw: (...rgb: RGB) => HSV;
|
||||
};
|
||||
hwb: {
|
||||
(...rgb: RGB): HWB;
|
||||
raw: (...rgb: RGB) => HWB;
|
||||
};
|
||||
hcg: {
|
||||
(...rgb: RGB): HCG;
|
||||
raw: (...rgb: RGB) => HCG;
|
||||
};
|
||||
cmyk: {
|
||||
(...rgb: RGB): CMYK;
|
||||
raw: (...rgb: RGB) => CMYK;
|
||||
};
|
||||
keyword: {
|
||||
(...rgb: RGB): Keyword;
|
||||
raw: (...rgb: RGB) => Keyword;
|
||||
};
|
||||
ansi16: {
|
||||
(...rgb: RGB): ANSI16;
|
||||
raw: (...rgb: RGB) => ANSI16;
|
||||
};
|
||||
ansi256: {
|
||||
(...rgb: RGB): ANSI256;
|
||||
raw: (...rgb: RGB) => ANSI256;
|
||||
};
|
||||
apple: {
|
||||
(...rgb: RGB): Apple;
|
||||
raw: (...rgb: RGB) => Apple;
|
||||
};
|
||||
hex: {
|
||||
(...rgb: RGB): HEX;
|
||||
raw: (...rgb: RGB) => HEX;
|
||||
};
|
||||
gray: {
|
||||
(...rgb: RGB): Gray;
|
||||
raw: (...rgb: RGB) => Gray;
|
||||
};
|
||||
} & route.rgb & {
|
||||
[F in keyof route.rgb]: {
|
||||
raw: route.rgb[F];
|
||||
};
|
||||
};
|
||||
keyword: {
|
||||
channels: Channels;
|
||||
rgb: {
|
||||
(keyword: Keyword): RGB;
|
||||
raw: (keyword: Keyword) => RGB;
|
||||
};
|
||||
} & route.keyword & {
|
||||
[F in keyof route.keyword]: {
|
||||
raw: route.keyword[F];
|
||||
};
|
||||
};
|
||||
hsl: {
|
||||
channels: Channels;
|
||||
labels: "hsl";
|
||||
rgb: {
|
||||
(...hsl: HSL): RGB;
|
||||
raw: (...hsl: HSL) => RGB;
|
||||
};
|
||||
hsv: {
|
||||
(...hsl: HSL): HSV;
|
||||
raw: (...hsl: HSL) => HSV;
|
||||
};
|
||||
hcg: {
|
||||
(...hsl: HSL): HCG;
|
||||
raw: (...hsl: HSL) => HCG;
|
||||
};
|
||||
} & route.hsl & {
|
||||
[F in keyof route.hsl]: {
|
||||
raw: route.hsl[F];
|
||||
};
|
||||
};
|
||||
hsv: {
|
||||
channels: Channels;
|
||||
labels: "hsv";
|
||||
hcg: {
|
||||
(...hsv: HSV): HCG;
|
||||
raw: (...hsv: HSV) => HCG;
|
||||
};
|
||||
rgb: {
|
||||
(...hsv: HSV): RGB;
|
||||
raw: (...hsv: HSV) => RGB;
|
||||
};
|
||||
hsv: {
|
||||
(...hsv: HSV): HSV;
|
||||
raw: (...hsv: HSV) => HSV;
|
||||
};
|
||||
hsl: {
|
||||
(...hsv: HSV): HSL;
|
||||
raw: (...hsv: HSV) => HSL;
|
||||
};
|
||||
hwb: {
|
||||
(...hsv: HSV): HWB;
|
||||
raw: (...hsv: HSV) => HWB;
|
||||
};
|
||||
ansi16: {
|
||||
(...hsv: HSV): ANSI16;
|
||||
raw: (...hsv: HSV) => ANSI16;
|
||||
};
|
||||
} & route.hsv & {
|
||||
[F in keyof route.hsv]: {
|
||||
raw: route.hsv[F];
|
||||
};
|
||||
};
|
||||
hwb: {
|
||||
channels: Channels;
|
||||
labels: "hwb";
|
||||
hcg: {
|
||||
(...hwb: HWB): HCG;
|
||||
raw: (...hwb: HWB) => HCG;
|
||||
};
|
||||
rgb: {
|
||||
(...hwb: HWB): RGB;
|
||||
raw: (...hwb: HWB) => RGB;
|
||||
};
|
||||
} & route.hwb & {
|
||||
[F in keyof route.hwb]: {
|
||||
raw: route.hwb[F];
|
||||
};
|
||||
};
|
||||
cmyk: {
|
||||
channels: Channels;
|
||||
labels: "cmyk";
|
||||
rgb: {
|
||||
(...cmyk: CMYK): RGB;
|
||||
raw: (...cmyk: CMYK) => RGB;
|
||||
};
|
||||
} & route.cmyk & {
|
||||
[F in keyof route.cmyk]: {
|
||||
raw: route.cmyk[F];
|
||||
};
|
||||
};
|
||||
xyz: {
|
||||
channels: Channels;
|
||||
labels: "xyz";
|
||||
rgb: {
|
||||
(...xyz: XYZ): RGB;
|
||||
raw: (...xyz: XYZ) => RGB;
|
||||
};
|
||||
lab: {
|
||||
(...xyz: XYZ): LAB;
|
||||
raw: (...xyz: XYZ) => LAB;
|
||||
};
|
||||
} & route.xyz & {
|
||||
[F in keyof route.xyz]: {
|
||||
raw: route.xyz[F];
|
||||
};
|
||||
};
|
||||
lab: {
|
||||
channels: Channels;
|
||||
labels: "lab";
|
||||
xyz: {
|
||||
(...lab: LAB): XYZ;
|
||||
raw: (...lab: LAB) => XYZ;
|
||||
};
|
||||
lch: {
|
||||
(...lab: LAB): LCH;
|
||||
raw: (...lab: LAB) => LCH;
|
||||
};
|
||||
} & route.lab & {
|
||||
[F in keyof route.lab]: {
|
||||
raw: route.lab[F];
|
||||
};
|
||||
};
|
||||
lch: {
|
||||
channels: Channels;
|
||||
labels: "lch";
|
||||
lab: {
|
||||
(...lch: LCH): LAB;
|
||||
raw: (...lch: LCH) => LAB;
|
||||
};
|
||||
} & route.lch & {
|
||||
[F in keyof route.lch]: {
|
||||
raw: route.lch[F];
|
||||
};
|
||||
};
|
||||
hex: {
|
||||
channels: Channels;
|
||||
labels: [
|
||||
"hex"
|
||||
];
|
||||
rgb: {
|
||||
(hex: HEX): RGB;
|
||||
raw: (hex: HEX) => RGB;
|
||||
};
|
||||
} & route.hex & {
|
||||
[F in keyof route.hex]: {
|
||||
raw: route.hex[F];
|
||||
};
|
||||
};
|
||||
ansi16: {
|
||||
channels: Channels;
|
||||
labels: [
|
||||
"ansi16"
|
||||
];
|
||||
rgb: {
|
||||
(ansi16: ANSI16): RGB;
|
||||
raw: (ansi16: ANSI16) => RGB;
|
||||
};
|
||||
} & route.ansi16 & {
|
||||
[F in keyof route.ansi16]: {
|
||||
raw: route.ansi16[F];
|
||||
};
|
||||
};
|
||||
ansi256: {
|
||||
channels: Channels;
|
||||
labels: [
|
||||
"ansi256"
|
||||
];
|
||||
rgb: {
|
||||
(ansi256: ANSI256): RGB;
|
||||
raw: (ansi256: ANSI256) => RGB;
|
||||
};
|
||||
} & route.ansi256 & {
|
||||
[F in keyof route.ansi256]: {
|
||||
raw: route.ansi256[F];
|
||||
};
|
||||
};
|
||||
hcg: {
|
||||
channels: Channels;
|
||||
labels: [
|
||||
"h",
|
||||
"c",
|
||||
"g"
|
||||
];
|
||||
rgb: {
|
||||
(...hcg: HCG): RGB;
|
||||
raw: (...hcg: HCG) => RGB;
|
||||
};
|
||||
hsv: {
|
||||
(...hcg: HCG): HSV;
|
||||
raw: (...hcg: HCG) => HSV;
|
||||
};
|
||||
hwb: {
|
||||
(...hcg: HCG): HWB;
|
||||
raw: (...hcg: HCG) => HWB;
|
||||
};
|
||||
} & route.hcg & {
|
||||
[F in keyof route.hcg]: {
|
||||
raw: route.hcg[F];
|
||||
};
|
||||
};
|
||||
apple: {
|
||||
channels: Channels;
|
||||
labels: [
|
||||
"r16",
|
||||
"g16",
|
||||
"b16"
|
||||
];
|
||||
rgb: {
|
||||
(...apple: Apple): RGB;
|
||||
raw: (...apple: Apple) => RGB;
|
||||
};
|
||||
} & route.apple & {
|
||||
[F in keyof route.apple]: {
|
||||
raw: route.apple[F];
|
||||
};
|
||||
};
|
||||
gray: {
|
||||
channels: Channels;
|
||||
labels: [
|
||||
"gray"
|
||||
];
|
||||
rgb: {
|
||||
(...gray: Gray): RGB;
|
||||
raw: (...gray: Gray) => RGB;
|
||||
};
|
||||
hsl: {
|
||||
(...gray: Gray): HSL;
|
||||
raw: (...gray: Gray) => HSL;
|
||||
};
|
||||
hsv: {
|
||||
(...gray: Gray): HSV;
|
||||
raw: (...gray: Gray) => HSV;
|
||||
};
|
||||
hwb: {
|
||||
(...gray: Gray): HWB;
|
||||
raw: (...gray: Gray) => HWB;
|
||||
};
|
||||
cmyk: {
|
||||
(...gray: Gray): CMYK;
|
||||
raw: (...gray: Gray) => CMYK;
|
||||
};
|
||||
lab: {
|
||||
(...gray: Gray): LAB;
|
||||
raw: (...gray: Gray) => LAB;
|
||||
};
|
||||
hex: {
|
||||
(...gray: Gray): HEX;
|
||||
raw: (...gray: Gray) => HEX;
|
||||
};
|
||||
} & route.gray & {
|
||||
[F in keyof route.gray]: {
|
||||
raw: route.gray[F];
|
||||
};
|
||||
};
|
||||
};
|
||||
declare const convert: Convert;
|
||||
export type ColorLike = ColorInstance | string | ArrayLike<number> | number | Record<string, any>;
|
||||
export type ColorJson = {
|
||||
model: string;
|
||||
color: number[];
|
||||
valpha: number;
|
||||
};
|
||||
export type ColorObject = {
|
||||
alpha?: number | undefined;
|
||||
} & Record<string, number>;
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
export interface ColorInstance {
|
||||
toString(): string;
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
toJSON(): ColorJson;
|
||||
string(places?: number): string;
|
||||
percentString(places?: number): string;
|
||||
array(): number[];
|
||||
object(): ColorObject;
|
||||
unitArray(): number[];
|
||||
unitObject(): {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
alpha?: number | undefined;
|
||||
};
|
||||
round(places?: number): ColorInstance;
|
||||
alpha(): number;
|
||||
alpha(value: number): ColorInstance;
|
||||
red(): number;
|
||||
red(value: number): ColorInstance;
|
||||
green(): number;
|
||||
green(value: number): ColorInstance;
|
||||
blue(): number;
|
||||
blue(value: number): ColorInstance;
|
||||
hue(): number;
|
||||
hue(value: number): ColorInstance;
|
||||
saturationl(): number;
|
||||
saturationl(value: number): ColorInstance;
|
||||
lightness(): number;
|
||||
lightness(value: number): ColorInstance;
|
||||
saturationv(): number;
|
||||
saturationv(value: number): ColorInstance;
|
||||
value(): number;
|
||||
value(value: number): ColorInstance;
|
||||
chroma(): number;
|
||||
chroma(value: number): ColorInstance;
|
||||
gray(): number;
|
||||
gray(value: number): ColorInstance;
|
||||
white(): number;
|
||||
white(value: number): ColorInstance;
|
||||
wblack(): number;
|
||||
wblack(value: number): ColorInstance;
|
||||
cyan(): number;
|
||||
cyan(value: number): ColorInstance;
|
||||
magenta(): number;
|
||||
magenta(value: number): ColorInstance;
|
||||
yellow(): number;
|
||||
yellow(value: number): ColorInstance;
|
||||
black(): number;
|
||||
black(value: number): ColorInstance;
|
||||
x(): number;
|
||||
x(value: number): ColorInstance;
|
||||
y(): number;
|
||||
y(value: number): ColorInstance;
|
||||
z(): number;
|
||||
z(value: number): ColorInstance;
|
||||
l(): number;
|
||||
l(value: number): ColorInstance;
|
||||
a(): number;
|
||||
a(value: number): ColorInstance;
|
||||
b(): number;
|
||||
b(value: number): ColorInstance;
|
||||
keyword(): string;
|
||||
keyword<V extends string>(value: V): ColorInstance;
|
||||
hex(): string;
|
||||
hex<V extends string>(value: V): ColorInstance;
|
||||
hexa(): string;
|
||||
hexa<V extends string>(value: V): ColorInstance;
|
||||
rgbNumber(): number;
|
||||
luminosity(): number;
|
||||
contrast(color2: ColorInstance): number;
|
||||
level(color2: ColorInstance): "AAA" | "AA" | "";
|
||||
isDark(): boolean;
|
||||
isLight(): boolean;
|
||||
negate(): ColorInstance;
|
||||
lighten(ratio: number): ColorInstance;
|
||||
darken(ratio: number): ColorInstance;
|
||||
saturate(ratio: number): ColorInstance;
|
||||
desaturate(ratio: number): ColorInstance;
|
||||
whiten(ratio: number): ColorInstance;
|
||||
blacken(ratio: number): ColorInstance;
|
||||
grayscale(): ColorInstance;
|
||||
fade(ratio: number): ColorInstance;
|
||||
opaquer(ratio: number): ColorInstance;
|
||||
rotate(degrees: number): ColorInstance;
|
||||
mix(mixinColor: ColorInstance, weight?: number): ColorInstance;
|
||||
rgb(...arguments_: number[]): ColorInstance;
|
||||
hsl(...arguments_: number[]): ColorInstance;
|
||||
hsv(...arguments_: number[]): ColorInstance;
|
||||
hwb(...arguments_: number[]): ColorInstance;
|
||||
cmyk(...arguments_: number[]): ColorInstance;
|
||||
xyz(...arguments_: number[]): ColorInstance;
|
||||
lab(...arguments_: number[]): ColorInstance;
|
||||
lch(...arguments_: number[]): ColorInstance;
|
||||
ansi16(...arguments_: number[]): ColorInstance;
|
||||
ansi256(...arguments_: number[]): ColorInstance;
|
||||
hcg(...arguments_: number[]): ColorInstance;
|
||||
apple(...arguments_: number[]): ColorInstance;
|
||||
}
|
||||
export type ColorConstructor = {
|
||||
(object?: ColorLike, model?: keyof (typeof convert)): ColorInstance;
|
||||
new (object?: ColorLike, model?: keyof (typeof convert)): ColorInstance;
|
||||
rgb(...value: number[]): ColorInstance;
|
||||
rgb(color: ColorLike): ColorInstance;
|
||||
hsl(...value: number[]): ColorInstance;
|
||||
hsl(color: ColorLike): ColorInstance;
|
||||
hsv(...value: number[]): ColorInstance;
|
||||
hsv(color: ColorLike): ColorInstance;
|
||||
hwb(...value: number[]): ColorInstance;
|
||||
hwb(color: ColorLike): ColorInstance;
|
||||
cmyk(...value: number[]): ColorInstance;
|
||||
cmyk(color: ColorLike): ColorInstance;
|
||||
xyz(...value: number[]): ColorInstance;
|
||||
xyz(color: ColorLike): ColorInstance;
|
||||
lab(...value: number[]): ColorInstance;
|
||||
lab(color: ColorLike): ColorInstance;
|
||||
lch(...value: number[]): ColorInstance;
|
||||
lch(color: ColorLike): ColorInstance;
|
||||
ansi16(...value: number[]): ColorInstance;
|
||||
ansi16(color: ColorLike): ColorInstance;
|
||||
ansi256(...value: number[]): ColorInstance;
|
||||
ansi256(color: ColorLike): ColorInstance;
|
||||
hcg(...value: number[]): ColorInstance;
|
||||
hcg(color: ColorLike): ColorInstance;
|
||||
apple(...value: number[]): ColorInstance;
|
||||
apple(color: ColorLike): ColorInstance;
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
declare const Color: ColorConstructor;
|
||||
|
||||
export {
|
||||
Color as default,
|
||||
};
|
||||
|
||||
export {};
|
||||
Generated
Vendored
-58
@@ -1,58 +0,0 @@
|
||||
{
|
||||
"name": "@img/colour",
|
||||
"version": "1.1.0",
|
||||
"description": "The ESM-only 'color' package made compatible for use with CommonJS runtimes",
|
||||
"license": "MIT",
|
||||
"main": "index.cjs",
|
||||
"types": "index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./index.d.ts",
|
||||
"require": "./index.cjs",
|
||||
"default": "./index.cjs"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"authors": [
|
||||
"Heather Arthur <fayearthur@gmail.com>",
|
||||
"Josh Junon <josh@junon.me>",
|
||||
"Maxime Thirouin",
|
||||
"Dyma Ywanov <dfcreative@gmail.com>",
|
||||
"LitoMore (https://github.com/LitoMore)"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"files": [
|
||||
"color.cjs",
|
||||
"index.d.ts"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lovell/colour.git"
|
||||
},
|
||||
"type": "commonjs",
|
||||
"keywords": [
|
||||
"color",
|
||||
"colour",
|
||||
"cjs",
|
||||
"commonjs"
|
||||
],
|
||||
"scripts": {
|
||||
"build:cjs": "esbuild node_modules/color/index.js --bundle --platform=node --outfile=color.cjs",
|
||||
"build:dts": "dts-bundle-generator ./dts-src.ts -o index.d.ts --project tsconfig.build.json --external-inlines color --external-inlines color-convert --export-referenced-types=false",
|
||||
"build": "npm run build:cjs && npm run build:dts",
|
||||
"test": "node --test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"color": "5.0.3",
|
||||
"color-convert": "3.1.3",
|
||||
"color-name": "2.1.0",
|
||||
"color-string": "2.1.4",
|
||||
"dts-bundle-generator": "^9.5.1",
|
||||
"esbuild": "^0.27.3"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
-191
@@ -1,191 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same "printed page" as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Generated
Vendored
-18
@@ -1,18 +0,0 @@
|
||||
# `@img/sharp-darwin-arm64`
|
||||
|
||||
Prebuilt sharp for use with macOS 64-bit ARM.
|
||||
|
||||
## Licensing
|
||||
|
||||
Copyright 2013 Lovell Fuller and others.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
-40
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"name": "@img/sharp-darwin-arm64",
|
||||
"version": "0.34.5",
|
||||
"description": "Prebuilt sharp for use with macOS 64-bit ARM",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lovell/sharp.git",
|
||||
"directory": "npm/darwin-arm64"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./sharp.node": "./lib/sharp-darwin-arm64.node",
|
||||
"./package": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"cpu": [
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
Generated
Vendored
-46
@@ -1,46 +0,0 @@
|
||||
# `@img/sharp-libvips-darwin-arm64`
|
||||
|
||||
Prebuilt libvips and dependencies for use with sharp on macOS 64-bit ARM.
|
||||
|
||||
## Licensing
|
||||
|
||||
This software contains third-party libraries
|
||||
used under the terms of the following licences:
|
||||
|
||||
| Library | Used under the terms of |
|
||||
|---------------|-----------------------------------------------------------------------------------------------------------|
|
||||
| aom | BSD 2-Clause + [Alliance for Open Media Patent License 1.0](https://aomedia.org/license/patent-license/) |
|
||||
| cairo | Mozilla Public License 2.0 |
|
||||
| cgif | MIT Licence |
|
||||
| expat | MIT Licence |
|
||||
| fontconfig | [fontconfig Licence](https://gitlab.freedesktop.org/fontconfig/fontconfig/blob/main/COPYING) (BSD-like) |
|
||||
| freetype | [freetype Licence](https://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT) (BSD-like) |
|
||||
| fribidi | LGPLv3 |
|
||||
| glib | LGPLv3 |
|
||||
| harfbuzz | MIT Licence |
|
||||
| highway | Apache-2.0 License, BSD 3-Clause |
|
||||
| lcms | MIT Licence |
|
||||
| libarchive | BSD 2-Clause |
|
||||
| libexif | LGPLv3 |
|
||||
| libffi | MIT Licence |
|
||||
| libheif | LGPLv3 |
|
||||
| libimagequant | [BSD 2-Clause](https://github.com/lovell/libimagequant/blob/main/COPYRIGHT) |
|
||||
| libnsgif | MIT Licence |
|
||||
| libpng | [libpng License](https://github.com/pnggroup/libpng/blob/master/LICENSE) |
|
||||
| librsvg | LGPLv3 |
|
||||
| libspng | [BSD 2-Clause, libpng License](https://github.com/randy408/libspng/blob/master/LICENSE) |
|
||||
| libtiff | [libtiff License](https://gitlab.com/libtiff/libtiff/blob/master/LICENSE.md) (BSD-like) |
|
||||
| libvips | LGPLv3 |
|
||||
| libwebp | New BSD License |
|
||||
| libxml2 | MIT Licence |
|
||||
| mozjpeg | [zlib License, IJG License, BSD-3-Clause](https://github.com/mozilla/mozjpeg/blob/master/LICENSE.md) |
|
||||
| pango | LGPLv3 |
|
||||
| pixman | MIT Licence |
|
||||
| proxy-libintl | LGPLv3 |
|
||||
| zlib-ng | [zlib Licence](https://github.com/zlib-ng/zlib-ng/blob/develop/LICENSE.md) |
|
||||
|
||||
Use of libraries under the terms of the LGPLv3 is via the
|
||||
"any later version" clause of the LGPLv2 or LGPLv2.1.
|
||||
|
||||
Please report any errors or omissions via
|
||||
https://github.com/lovell/sharp-libvips/issues/new
|
||||
Generated
Vendored
-220
@@ -1,220 +0,0 @@
|
||||
/* glibconfig.h
|
||||
*
|
||||
* This is a generated file. Please modify 'glibconfig.h.in'
|
||||
*/
|
||||
|
||||
#ifndef __GLIBCONFIG_H__
|
||||
#define __GLIBCONFIG_H__
|
||||
|
||||
#include <glib/gmacros.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <float.h>
|
||||
#define GLIB_HAVE_ALLOCA_H
|
||||
|
||||
#define GLIB_STATIC_COMPILATION 1
|
||||
#define GOBJECT_STATIC_COMPILATION 1
|
||||
#define GIO_STATIC_COMPILATION 1
|
||||
#define GMODULE_STATIC_COMPILATION 1
|
||||
#define GI_STATIC_COMPILATION 1
|
||||
#define G_INTL_STATIC_COMPILATION 1
|
||||
#define FFI_STATIC_BUILD 1
|
||||
|
||||
/* Specifies that GLib's g_print*() functions wrap the
|
||||
* system printf functions. This is useful to know, for example,
|
||||
* when using glibc's register_printf_function().
|
||||
*/
|
||||
#define GLIB_USING_SYSTEM_PRINTF
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_MINFLOAT FLT_MIN
|
||||
#define G_MAXFLOAT FLT_MAX
|
||||
#define G_MINDOUBLE DBL_MIN
|
||||
#define G_MAXDOUBLE DBL_MAX
|
||||
#define G_MINSHORT SHRT_MIN
|
||||
#define G_MAXSHORT SHRT_MAX
|
||||
#define G_MAXUSHORT USHRT_MAX
|
||||
#define G_MININT INT_MIN
|
||||
#define G_MAXINT INT_MAX
|
||||
#define G_MAXUINT UINT_MAX
|
||||
#define G_MINLONG LONG_MIN
|
||||
#define G_MAXLONG LONG_MAX
|
||||
#define G_MAXULONG ULONG_MAX
|
||||
|
||||
typedef signed char gint8;
|
||||
typedef unsigned char guint8;
|
||||
|
||||
typedef signed short gint16;
|
||||
typedef unsigned short guint16;
|
||||
|
||||
#define G_GINT16_MODIFIER "h"
|
||||
#define G_GINT16_FORMAT "hi"
|
||||
#define G_GUINT16_FORMAT "hu"
|
||||
|
||||
|
||||
typedef signed int gint32;
|
||||
typedef unsigned int guint32;
|
||||
|
||||
#define G_GINT32_MODIFIER ""
|
||||
#define G_GINT32_FORMAT "i"
|
||||
#define G_GUINT32_FORMAT "u"
|
||||
|
||||
|
||||
#define G_HAVE_GINT64 1 /* deprecated, always true */
|
||||
|
||||
G_GNUC_EXTENSION typedef signed long long gint64;
|
||||
G_GNUC_EXTENSION typedef unsigned long long guint64;
|
||||
|
||||
#define G_GINT64_CONSTANT(val) (G_GNUC_EXTENSION (val##LL))
|
||||
#define G_GUINT64_CONSTANT(val) (G_GNUC_EXTENSION (val##ULL))
|
||||
|
||||
#define G_GINT64_MODIFIER "ll"
|
||||
#define G_GINT64_FORMAT "lli"
|
||||
#define G_GUINT64_FORMAT "llu"
|
||||
|
||||
|
||||
#define GLIB_SIZEOF_VOID_P 8
|
||||
#define GLIB_SIZEOF_LONG 8
|
||||
#define GLIB_SIZEOF_SIZE_T 8
|
||||
#define GLIB_SIZEOF_SSIZE_T 8
|
||||
|
||||
typedef signed long gssize;
|
||||
typedef unsigned long gsize;
|
||||
#define G_GSIZE_MODIFIER "l"
|
||||
#define G_GSSIZE_MODIFIER "l"
|
||||
#define G_GSIZE_FORMAT "lu"
|
||||
#define G_GSSIZE_FORMAT "li"
|
||||
|
||||
#define G_MAXSIZE G_MAXULONG
|
||||
#define G_MINSSIZE G_MINLONG
|
||||
#define G_MAXSSIZE G_MAXLONG
|
||||
|
||||
typedef gint64 goffset;
|
||||
#define G_MINOFFSET G_MININT64
|
||||
#define G_MAXOFFSET G_MAXINT64
|
||||
|
||||
#define G_GOFFSET_MODIFIER G_GINT64_MODIFIER
|
||||
#define G_GOFFSET_FORMAT G_GINT64_FORMAT
|
||||
#define G_GOFFSET_CONSTANT(val) G_GINT64_CONSTANT(val)
|
||||
|
||||
#define G_POLLFD_FORMAT "%d"
|
||||
|
||||
#define GPOINTER_TO_INT(p) ((gint) (glong) (p))
|
||||
#define GPOINTER_TO_UINT(p) ((guint) (gulong) (p))
|
||||
|
||||
#define GINT_TO_POINTER(i) ((gpointer) (glong) (i))
|
||||
#define GUINT_TO_POINTER(u) ((gpointer) (gulong) (u))
|
||||
|
||||
typedef signed long gintptr;
|
||||
typedef unsigned long guintptr;
|
||||
|
||||
#define G_GINTPTR_MODIFIER "l"
|
||||
#define G_GINTPTR_FORMAT "li"
|
||||
#define G_GUINTPTR_FORMAT "lu"
|
||||
|
||||
#define GLIB_MAJOR_VERSION 2
|
||||
#define GLIB_MINOR_VERSION 86
|
||||
#define GLIB_MICRO_VERSION 1
|
||||
|
||||
#define G_OS_UNIX
|
||||
|
||||
#define G_VA_COPY va_copy
|
||||
|
||||
|
||||
#define G_HAVE_ISO_VARARGS 1
|
||||
|
||||
/* gcc-2.95.x supports both gnu style and ISO varargs, but if -ansi
|
||||
* is passed ISO vararg support is turned off, and there is no work
|
||||
* around to turn it on, so we unconditionally turn it off.
|
||||
*/
|
||||
#if __GNUC__ == 2 && __GNUC_MINOR__ == 95
|
||||
# undef G_HAVE_ISO_VARARGS
|
||||
#endif
|
||||
|
||||
#define G_HAVE_GROWING_STACK 0
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# define G_HAVE_GNUC_VARARGS 1
|
||||
#endif
|
||||
|
||||
#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)
|
||||
#define G_GNUC_INTERNAL __attribute__((visibility("hidden")))
|
||||
#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
|
||||
#define G_GNUC_INTERNAL __hidden
|
||||
#elif defined (__GNUC__) && defined (G_HAVE_GNUC_VISIBILITY)
|
||||
#define G_GNUC_INTERNAL __attribute__((visibility("hidden")))
|
||||
#else
|
||||
#define G_GNUC_INTERNAL
|
||||
#endif
|
||||
|
||||
#define G_THREADS_ENABLED
|
||||
#define G_THREADS_IMPL_POSIX
|
||||
|
||||
#define G_ATOMIC_LOCK_FREE
|
||||
|
||||
#define GINT16_TO_LE(val) ((gint16) (val))
|
||||
#define GUINT16_TO_LE(val) ((guint16) (val))
|
||||
#define GINT16_TO_BE(val) ((gint16) GUINT16_SWAP_LE_BE (val))
|
||||
#define GUINT16_TO_BE(val) (GUINT16_SWAP_LE_BE (val))
|
||||
|
||||
#define GINT32_TO_LE(val) ((gint32) (val))
|
||||
#define GUINT32_TO_LE(val) ((guint32) (val))
|
||||
#define GINT32_TO_BE(val) ((gint32) GUINT32_SWAP_LE_BE (val))
|
||||
#define GUINT32_TO_BE(val) (GUINT32_SWAP_LE_BE (val))
|
||||
|
||||
#define GINT64_TO_LE(val) ((gint64) (val))
|
||||
#define GUINT64_TO_LE(val) ((guint64) (val))
|
||||
#define GINT64_TO_BE(val) ((gint64) GUINT64_SWAP_LE_BE (val))
|
||||
#define GUINT64_TO_BE(val) (GUINT64_SWAP_LE_BE (val))
|
||||
|
||||
#define GLONG_TO_LE(val) ((glong) GINT64_TO_LE (val))
|
||||
#define GULONG_TO_LE(val) ((gulong) GUINT64_TO_LE (val))
|
||||
#define GLONG_TO_BE(val) ((glong) GINT64_TO_BE (val))
|
||||
#define GULONG_TO_BE(val) ((gulong) GUINT64_TO_BE (val))
|
||||
#define GINT_TO_LE(val) ((gint) GINT32_TO_LE (val))
|
||||
#define GUINT_TO_LE(val) ((guint) GUINT32_TO_LE (val))
|
||||
#define GINT_TO_BE(val) ((gint) GINT32_TO_BE (val))
|
||||
#define GUINT_TO_BE(val) ((guint) GUINT32_TO_BE (val))
|
||||
#define GSIZE_TO_LE(val) ((gsize) GUINT64_TO_LE (val))
|
||||
#define GSSIZE_TO_LE(val) ((gssize) GINT64_TO_LE (val))
|
||||
#define GSIZE_TO_BE(val) ((gsize) GUINT64_TO_BE (val))
|
||||
#define GSSIZE_TO_BE(val) ((gssize) GINT64_TO_BE (val))
|
||||
#define G_BYTE_ORDER G_LITTLE_ENDIAN
|
||||
|
||||
#define GLIB_SYSDEF_POLLIN =1
|
||||
#define GLIB_SYSDEF_POLLOUT =4
|
||||
#define GLIB_SYSDEF_POLLPRI =2
|
||||
#define GLIB_SYSDEF_POLLHUP =16
|
||||
#define GLIB_SYSDEF_POLLERR =8
|
||||
#define GLIB_SYSDEF_POLLNVAL =32
|
||||
|
||||
/* No way to disable deprecation warnings for macros, so only emit deprecation
|
||||
* warnings on platforms where usage of this macro is broken */
|
||||
#if defined(__APPLE__) || defined(_MSC_VER) || defined(__CYGWIN__)
|
||||
#define G_MODULE_SUFFIX "so" GLIB_DEPRECATED_MACRO_IN_2_76
|
||||
#else
|
||||
#define G_MODULE_SUFFIX "so"
|
||||
#endif
|
||||
|
||||
typedef int GPid;
|
||||
#define G_PID_FORMAT "i"
|
||||
|
||||
#define GLIB_SYSDEF_AF_UNIX 1
|
||||
#define GLIB_SYSDEF_AF_INET 2
|
||||
#define GLIB_SYSDEF_AF_INET6 30
|
||||
|
||||
#define GLIB_SYSDEF_MSG_OOB 1
|
||||
#define GLIB_SYSDEF_MSG_PEEK 2
|
||||
#define GLIB_SYSDEF_MSG_DONTROUTE 4
|
||||
|
||||
#define G_DIR_SEPARATOR '/'
|
||||
#define G_DIR_SEPARATOR_S "/"
|
||||
#define G_SEARCHPATH_SEPARATOR ':'
|
||||
#define G_SEARCHPATH_SEPARATOR_S ":"
|
||||
|
||||
#undef G_HAVE_FREE_SIZED
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GLIBCONFIG_H__ */
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
module.exports = __dirname;
|
||||
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
-36
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "@img/sharp-libvips-darwin-arm64",
|
||||
"version": "1.2.4",
|
||||
"description": "Prebuilt libvips and dependencies for use with sharp on macOS 64-bit ARM",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lovell/sharp-libvips.git",
|
||||
"directory": "npm/darwin-arm64"
|
||||
},
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"versions.json"
|
||||
],
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./lib": "./lib/index.js",
|
||||
"./package": "./package.json",
|
||||
"./versions": "./versions.json"
|
||||
},
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"cpu": [
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
Generated
Vendored
-30
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"aom": "3.13.1",
|
||||
"archive": "3.8.2",
|
||||
"cairo": "1.18.4",
|
||||
"cgif": "0.5.0",
|
||||
"exif": "0.6.25",
|
||||
"expat": "2.7.3",
|
||||
"ffi": "3.5.2",
|
||||
"fontconfig": "2.17.1",
|
||||
"freetype": "2.14.1",
|
||||
"fribidi": "1.0.16",
|
||||
"glib": "2.86.1",
|
||||
"harfbuzz": "12.1.0",
|
||||
"heif": "1.20.2",
|
||||
"highway": "1.3.0",
|
||||
"imagequant": "2.4.1",
|
||||
"lcms": "2.17",
|
||||
"mozjpeg": "0826579",
|
||||
"pango": "1.57.0",
|
||||
"pixman": "0.46.4",
|
||||
"png": "1.6.50",
|
||||
"proxy-libintl": "0.5",
|
||||
"rsvg": "2.61.2",
|
||||
"spng": "0.7.4",
|
||||
"tiff": "4.7.1",
|
||||
"vips": "8.17.3",
|
||||
"webp": "1.6.0",
|
||||
"xml2": "2.15.1",
|
||||
"zlib-ng": "2.2.5"
|
||||
}
|
||||
Generated
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Jared Wray
|
||||
|
||||
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.
|
||||
Generated
Vendored
-624
@@ -1,624 +0,0 @@
|
||||
# @keyv/bigmap [<img width="100" align="right" src="https://jaredwray.com/images/keyv-symbol.svg" alt="keyv">](https://github.com/jaredwra/keyv)
|
||||
|
||||
> Bigmap for Keyv
|
||||
|
||||
[](https://github.com/jaredwray/keyv/actions/workflows/tests.yaml)
|
||||
[](https://codecov.io/gh/jaredwray/keyv)
|
||||
[](https://www.npmjs.com/package/@keyv/bigmap)
|
||||
[](https://npmjs.com/package/@keyv/bigmap)
|
||||
|
||||
# Features
|
||||
* Based on the Map interface and uses the same API.
|
||||
* Lightweight with no dependencies.
|
||||
* Scales to past the 17 million key limit of a regular Map.
|
||||
* Uses a hash `djb2Hash` for fast key lookups.
|
||||
* Ability to use your own hash function.
|
||||
* Built in Typescript and Generics for type safety.
|
||||
* Used in `@cacheable/memory` for scalable in-memory caching.
|
||||
* Maintained regularly with a focus on performance and reliability.
|
||||
|
||||
# Table of Contents
|
||||
- [Features](#features)
|
||||
- [Installation](#installation)
|
||||
- [Overview](#overview)
|
||||
- [Basic Usage](#basic-usage)
|
||||
- [Custom Store Size](#custom-store-size)
|
||||
- [Custom Hash Function](#custom-hash-function)
|
||||
- [Iteration](#iteration)
|
||||
- [For...of Loop](#forof-loop)
|
||||
- [forEach](#foreach)
|
||||
- [Keys, Values, and Entries](#keys-values-and-entries)
|
||||
- [Advanced Features](#advanced-features)
|
||||
- [Type Safety with Generics](#type-safety-with-generics)
|
||||
- [Large-Scale Data](#large-scale-data)
|
||||
- [Using with Keyv](#using-with-keyv)
|
||||
- [createKeyv](#createkeyv)
|
||||
- [With Custom Options](#with-custom-options)
|
||||
- [Type Safety](#type-safety)
|
||||
- [Integration with Keyv Ecosystem](#integration-with-keyv-ecosystem)
|
||||
- [API](#api)
|
||||
- [Constructor](#constructor)
|
||||
- [Properties](#properties)
|
||||
- [Methods](#methods)
|
||||
- [set](#set)
|
||||
- [get](#get)
|
||||
- [has](#has)
|
||||
- [delete](#delete)
|
||||
- [clear](#clear)
|
||||
- [forEach](#foreach)
|
||||
- [keys](#keys)
|
||||
- [values](#values)
|
||||
- [entries](#entries)
|
||||
- [Symbol.iterator](#symboliterator)
|
||||
- [getStore](#getstorekey)
|
||||
- [getStoreMap](#getstoremapindex)
|
||||
- [initStore](#initstore)
|
||||
- [Types](#types)
|
||||
- [StoreHashFunction](#storehashfunction)
|
||||
- [defaultHashFunction(key, storeSize)](#defaulthashfunctionkey-storesize)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
|
||||
# Installation
|
||||
|
||||
```bash
|
||||
npm install --save keyv @keyv/bigmap
|
||||
```
|
||||
|
||||
# Overview
|
||||
|
||||
BigMap is a scalable Map implementation that overcomes JavaScript's built-in Map limit of approximately 17 million entries. It uses a distributed hash approach with multiple internal Map instances.
|
||||
|
||||
# Basic Usage
|
||||
|
||||
```typescript
|
||||
import { BigMap } from '@keyv/bigmap';
|
||||
|
||||
// Create a new BigMap
|
||||
const bigMap = new BigMap<string, number>();
|
||||
|
||||
// Set values
|
||||
bigMap.set('key1', 100);
|
||||
bigMap.set('key2', 200);
|
||||
|
||||
// Get values
|
||||
const value = bigMap.get('key1'); // 100
|
||||
|
||||
// Check if key exists
|
||||
bigMap.has('key1'); // true
|
||||
|
||||
// Delete a key
|
||||
bigMap.delete('key1'); // true
|
||||
|
||||
// Get size
|
||||
console.log(bigMap.size); // 1
|
||||
|
||||
// Clear all entries
|
||||
bigMap.clear();
|
||||
```
|
||||
|
||||
# Custom Store Size
|
||||
|
||||
By default, BigMap uses 4 internal Map instances. You can configure this:
|
||||
|
||||
```typescript
|
||||
const bigMap = new BigMap<string, number>({ storeSize: 10 });
|
||||
```
|
||||
|
||||
**Note:** Changing the `storeSize` after initialization will clear all entries.
|
||||
|
||||
# Custom Hash Function
|
||||
|
||||
Provide your own hash function for key distribution:
|
||||
|
||||
```typescript
|
||||
const customHashFunction = (key: string, storeSize: number) => {
|
||||
return key.length % storeSize;
|
||||
};
|
||||
|
||||
const bigMap = new BigMap<string, string>({
|
||||
storeHashFunction: customHashFunction
|
||||
});
|
||||
```
|
||||
|
||||
## Using Hashery for Hash Functions
|
||||
|
||||
[Hashery](https://github.com/jaredwray/hashery) is a powerful hashing library that provides multiple hash algorithms. You can use it for better key distribution and it is available as an export:
|
||||
|
||||
```typescript
|
||||
import { BigMap, Hashery } from '@keyv/bigmap';
|
||||
|
||||
const hashery = new Hashery();
|
||||
|
||||
// Using Hashery's toNumberSync for deterministic key distribution
|
||||
const bigMap = new BigMap<string, string>({
|
||||
storeHashFunction: (key: string, storeSize: number) => {
|
||||
return hashery.toNumberSync(key, { min: 0, max: storeSize - 1 });
|
||||
}
|
||||
});
|
||||
|
||||
// You can also use different algorithms
|
||||
const hasheryFnv1 = new Hashery({ defaultAlgorithmSync: 'fnv1' });
|
||||
|
||||
const bigMapWithFnv1 = new BigMap<string, string>({
|
||||
storeHashFunction: (key: string, storeSize: number) => {
|
||||
return hasheryFnv1.toNumberSync(key, { min: 0, max: storeSize - 1 });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Hashery supports multiple synchronous hash algorithms:
|
||||
- **djb2** - Fast hash function (default)
|
||||
- **fnv1** - Excellent distribution for hash tables
|
||||
- **murmer** - MurmurHash algorithm
|
||||
- **crc32** - Cyclic Redundancy Check
|
||||
|
||||
# Iteration
|
||||
|
||||
BigMap supports all standard Map iteration methods:
|
||||
|
||||
## For...of Loop
|
||||
|
||||
```typescript
|
||||
const bigMap = new BigMap<string, number>();
|
||||
bigMap.set('a', 1);
|
||||
bigMap.set('b', 2);
|
||||
|
||||
for (const [key, value] of bigMap) {
|
||||
console.log(key, value);
|
||||
}
|
||||
```
|
||||
|
||||
## forEach
|
||||
|
||||
```typescript
|
||||
bigMap.forEach((value, key) => {
|
||||
console.log(key, value);
|
||||
});
|
||||
|
||||
// With custom context
|
||||
const context = { sum: 0 };
|
||||
bigMap.forEach(function(value) {
|
||||
this.sum += value;
|
||||
}, context);
|
||||
```
|
||||
|
||||
## Keys, Values, and Entries
|
||||
|
||||
```typescript
|
||||
// Iterate over keys
|
||||
for (const key of bigMap.keys()) {
|
||||
console.log(key);
|
||||
}
|
||||
|
||||
// Iterate over values
|
||||
for (const value of bigMap.values()) {
|
||||
console.log(value);
|
||||
}
|
||||
|
||||
// Iterate over entries
|
||||
for (const [key, value] of bigMap.entries()) {
|
||||
console.log(key, value);
|
||||
}
|
||||
```
|
||||
|
||||
# Advanced Features
|
||||
|
||||
## Type Safety with Generics
|
||||
|
||||
```typescript
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const userMap = new BigMap<string, User>();
|
||||
userMap.set('user1', { id: 1, name: 'Alice' });
|
||||
```
|
||||
|
||||
## Large-Scale Data
|
||||
|
||||
BigMap is designed to handle millions of entries:
|
||||
|
||||
```typescript
|
||||
const bigMap = new BigMap<string, number>({ storeSize: 16 });
|
||||
|
||||
// Add 20+ million entries without hitting Map limits
|
||||
for (let i = 0; i < 20000000; i++) {
|
||||
bigMap.set(`key${i}`, i);
|
||||
}
|
||||
|
||||
console.log(bigMap.size); // 20000000
|
||||
```
|
||||
|
||||
# Using with Keyv
|
||||
|
||||
BigMap can be used as a storage adapter for [Keyv](https://github.com/jaredwray/keyv), providing a scalable in-memory store with TTL support.
|
||||
|
||||
## createKeyv
|
||||
|
||||
The `createKeyv` function creates a Keyv instance with BigMap as the storage adapter.
|
||||
|
||||
**Parameters:**
|
||||
- `options` (optional): BigMap configuration options
|
||||
- `storeSize` (number): Number of internal Map instances. Default: `4`
|
||||
- `storeHashFunction` (StoreHashFunction): Custom hash function for key distribution
|
||||
|
||||
**Returns:** `Keyv` instance with BigMap adapter
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
import { createKeyv } from '@keyv/bigmap';
|
||||
|
||||
// Basic usage
|
||||
const keyv = createKeyv();
|
||||
|
||||
// Set with TTL (in milliseconds)
|
||||
await keyv.set('user:123', { name: 'Alice', age: 30 }, 60000); // Expires in 60 seconds
|
||||
|
||||
// Get value
|
||||
const user = await keyv.get('user:123');
|
||||
console.log(user); // { name: 'Alice', age: 30 }
|
||||
|
||||
// Check if key exists
|
||||
const exists = await keyv.has('user:123');
|
||||
|
||||
// Delete key
|
||||
await keyv.delete('user:123');
|
||||
|
||||
// Clear all keys
|
||||
await keyv.clear();
|
||||
```
|
||||
|
||||
## With Custom Options
|
||||
|
||||
```typescript
|
||||
import { createKeyv } from '@keyv/bigmap';
|
||||
|
||||
// Create with custom store size for better performance with millions of keys
|
||||
const keyv = createKeyv({ storeSize: 16 });
|
||||
|
||||
// With custom hash function
|
||||
const keyv = createKeyv({
|
||||
storeSize: 8,
|
||||
storeHashFunction: (key, storeSize) => {
|
||||
// Custom distribution logic
|
||||
return key.length % storeSize;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Type Safety
|
||||
|
||||
```typescript
|
||||
import { createKeyv } from '@keyv/bigmap';
|
||||
|
||||
interface Product {
|
||||
id: string;
|
||||
name: string;
|
||||
price: number;
|
||||
}
|
||||
|
||||
const keyv = createKeyv<string, Product>();
|
||||
|
||||
await keyv.set('product:1', {
|
||||
id: '1',
|
||||
name: 'Laptop',
|
||||
price: 999
|
||||
});
|
||||
|
||||
const product = await keyv.get<Product>('product:1');
|
||||
```
|
||||
|
||||
# Integration with Keyv Ecosystem
|
||||
|
||||
BigMap works seamlessly with the Keyv ecosystem:
|
||||
|
||||
```typescript
|
||||
import { createKeyv } from '@keyv/bigmap';
|
||||
|
||||
const cache = createKeyv({ storeSize: 16 });
|
||||
|
||||
// Use with namespaces
|
||||
const users = cache.namespace('users');
|
||||
const products = cache.namespace('products');
|
||||
|
||||
await users.set('123', { name: 'Alice' });
|
||||
await products.set('456', { name: 'Laptop' });
|
||||
|
||||
// Iterate over keys
|
||||
for await (const [key, value] of cache.iterator()) {
|
||||
console.log(key, value);
|
||||
}
|
||||
```
|
||||
|
||||
# API
|
||||
|
||||
## Constructor
|
||||
|
||||
`new BigMap<K, V>(options?)`
|
||||
|
||||
Creates a new BigMap instance.
|
||||
|
||||
**Parameters:**
|
||||
- `options` (optional): Configuration options
|
||||
- `storeSize` (number): Number of internal Map instances to use. Default: `4`. Must be at least 1.
|
||||
- `storeHashFunction` (StoreHashFunction): Custom hash function for key distribution. Default: `defaultHashFunction`
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
const bigMap = new BigMap<string, number>();
|
||||
const customBigMap = new BigMap<string, number>({
|
||||
storeSize: 10,
|
||||
storeHashFunction: (key, storeSize) => key.length % storeSize
|
||||
});
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Access | Description |
|
||||
|----------|------|--------|-------------|
|
||||
| `size` | `number` | Read-only | Gets the total number of entries in the BigMap. |
|
||||
| `storeSize` | `number` | Read/Write | Gets or sets the number of internal Map instances. **Note:** Setting this will clear all entries. Default: `4` |
|
||||
| `storeHashFunction` | `StoreHashFunction \| undefined` | Read/Write | Gets or sets the hash function used for key distribution. |
|
||||
| `store` | `Array<Map<K, V>>` | Read-only | Gets the internal array of Map instances. |
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
const bigMap = new BigMap<string, number>();
|
||||
|
||||
// size property
|
||||
bigMap.set('key1', 100);
|
||||
console.log(bigMap.size); // 1
|
||||
|
||||
// storeSize property
|
||||
console.log(bigMap.storeSize); // 4 (default)
|
||||
bigMap.storeSize = 8; // Changes size and clears all entries
|
||||
|
||||
// storeHashFunction property
|
||||
bigMap.storeHashFunction = (key, storeSize) => key.length % storeSize;
|
||||
|
||||
// store property
|
||||
console.log(bigMap.store.length); // 8
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
### set
|
||||
|
||||
Sets the value for a key in the map.
|
||||
|
||||
**Parameters:**
|
||||
- `key` (K): The key to set
|
||||
- `value` (V): The value to associate with the key
|
||||
|
||||
**Returns:** `Map<K, V>` - The internal Map instance where the key was stored
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
bigMap.set('user123', { name: 'Alice' });
|
||||
```
|
||||
|
||||
### get
|
||||
|
||||
Gets the value associated with a key.
|
||||
|
||||
**Parameters:**
|
||||
- `key` (K): The key to retrieve
|
||||
|
||||
**Returns:** `V | undefined` - The value, or undefined if not found
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
const value = bigMap.get('user123');
|
||||
```
|
||||
|
||||
### has
|
||||
|
||||
Checks if a key exists in the map.
|
||||
|
||||
**Parameters:**
|
||||
- `key` (K): The key to check
|
||||
|
||||
**Returns:** `boolean` - True if the key exists, false otherwise
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
if (bigMap.has('user123')) {
|
||||
console.log('User exists');
|
||||
}
|
||||
```
|
||||
|
||||
### delete
|
||||
|
||||
Deletes a key-value pair from the map.
|
||||
|
||||
**Parameters:**
|
||||
- `key` (K): The key to delete
|
||||
|
||||
**Returns:** `boolean` - True if the entry was deleted, false if the key was not found
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
const deleted = bigMap.delete('user123');
|
||||
```
|
||||
|
||||
### clear
|
||||
|
||||
Removes all entries from the map.
|
||||
|
||||
**Returns:** `void`
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
bigMap.clear();
|
||||
console.log(bigMap.size); // 0
|
||||
```
|
||||
|
||||
### forEach
|
||||
|
||||
Executes a provided function once for each key-value pair.
|
||||
|
||||
**Parameters:**
|
||||
- `callbackfn` (function): Function to execute for each entry
|
||||
- `value` (V): The value of the current entry
|
||||
- `key` (K): The key of the current entry
|
||||
- `map` (`Map<K, V>`): The BigMap instance
|
||||
- `thisArg` (optional): Value to use as `this` when executing the callback
|
||||
|
||||
**Returns:** `void`
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
bigMap.forEach((value, key) => {
|
||||
console.log(`${key}: ${value}`);
|
||||
});
|
||||
|
||||
// With custom context
|
||||
const context = { total: 0 };
|
||||
bigMap.forEach(function(value) {
|
||||
this.total += value;
|
||||
}, context);
|
||||
```
|
||||
|
||||
### keys
|
||||
|
||||
Returns an iterator of all keys in the map.
|
||||
|
||||
**Returns:** `IterableIterator<K>`
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
for (const key of bigMap.keys()) {
|
||||
console.log(key);
|
||||
}
|
||||
```
|
||||
|
||||
### values
|
||||
|
||||
Returns an iterator of all values in the map.
|
||||
|
||||
**Returns:** `IterableIterator<V>`
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
for (const value of bigMap.values()) {
|
||||
console.log(value);
|
||||
}
|
||||
```
|
||||
|
||||
### entries
|
||||
|
||||
Returns an iterator of all key-value pairs in the map.
|
||||
|
||||
**Returns:** `IterableIterator<[K, V]>`
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
for (const [key, value] of bigMap.entries()) {
|
||||
console.log(key, value);
|
||||
}
|
||||
```
|
||||
|
||||
### Symbol.iterator
|
||||
|
||||
Returns an iterator for the map (same as `entries()`). Enables `for...of` loops.
|
||||
|
||||
**Returns:** `IterableIterator<[K, V]>`
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
for (const [key, value] of bigMap) {
|
||||
console.log(key, value);
|
||||
}
|
||||
```
|
||||
|
||||
### getStore
|
||||
|
||||
Gets the internal Map instance for a specific key.
|
||||
|
||||
**Parameters:**
|
||||
- `key` (K): The key to find the store for
|
||||
|
||||
**Returns:** `Map<K, V>` - The internal Map instance
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
const store = bigMap.getStore('user123');
|
||||
```
|
||||
|
||||
### getStoreMap
|
||||
|
||||
Gets the internal Map instance at a specific index.
|
||||
|
||||
**Parameters:**
|
||||
- `index` (number): The index of the Map to retrieve (0 to storeSize - 1)
|
||||
|
||||
**Returns:** `Map<K, V>` - The Map at the specified index
|
||||
|
||||
**Throws:** Error if index is out of bounds
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
const firstMap = bigMap.getStoreMap(0);
|
||||
```
|
||||
|
||||
### initStore
|
||||
|
||||
Initializes the internal store with empty Map instances. Called automatically during construction.
|
||||
|
||||
**Returns:** `void`
|
||||
|
||||
## Types
|
||||
|
||||
### StoreHashFunction
|
||||
|
||||
Type definition for custom hash functions.
|
||||
|
||||
```typescript
|
||||
type StoreHashFunction = (key: string, storeSize: number) => number;
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `key` (string): The key to hash (converted to string)
|
||||
- `storeSize` (number): The number of stores (adjusted for zero-based index)
|
||||
|
||||
**Returns:** `number` - The index of the store to use (0 to storeSize - 1)
|
||||
|
||||
### defaultHashFunction
|
||||
|
||||
The default hash function using DJB2 algorithm from [Hashery](https://npmjs.com/package/hashery):
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { defaultHashFunction } from '@keyv/bigmap';
|
||||
|
||||
const index = defaultHashFunction('myKey', 4);
|
||||
```
|
||||
|
||||
### djb2Hash
|
||||
|
||||
DJB2 hash algorithm implementation.
|
||||
|
||||
**Parameters:**
|
||||
- `string` (string): The string to hash
|
||||
- `min` (number): Minimum value. Default: `0`
|
||||
- `max` (number): Maximum value. Default: `10`
|
||||
|
||||
**Returns:** `number` - Hash value within the specified range
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
import { djb2Hash } from '@keyv/bigmap';
|
||||
|
||||
const hash = djb2Hash('myKey', 0, 10);
|
||||
```
|
||||
|
||||
# Contributing
|
||||
|
||||
Please see our [contributing](https://github.com/jaredwray/keyv/blob/main/CONTRIBUTING.md) guide.
|
||||
|
||||
# License
|
||||
|
||||
[MIT © Jared Wray](LICENSE)
|
||||
Generated
Vendored
-73
@@ -1,73 +0,0 @@
|
||||
{
|
||||
"name": "@keyv/bigmap",
|
||||
"version": "1.3.1",
|
||||
"description": "Bigmap for Keyv",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": {
|
||||
"types": "./dist/index.d.cts",
|
||||
"default": "./dist/index.cjs"
|
||||
},
|
||||
"import": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jaredwray/keyv.git"
|
||||
},
|
||||
"keywords": [
|
||||
"bigmap",
|
||||
"keyv",
|
||||
"key",
|
||||
"value",
|
||||
"store",
|
||||
"cache",
|
||||
"ttl"
|
||||
],
|
||||
"author": "Jared Wray <me@jaredwray.com> (http://jaredwray.com)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jaredwray/keyv/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jaredwray/keyv",
|
||||
"dependencies": {
|
||||
"hashery": "^1.4.0",
|
||||
"hookified": "^1.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.3.11",
|
||||
"@faker-js/faker": "^10.2.0",
|
||||
"@vitest/coverage-v8": "^4.0.17",
|
||||
"rimraf": "^6.1.2",
|
||||
"tsd": "^0.33.0",
|
||||
"vitest": "^4.0.17"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"keyv": "^5.6.0"
|
||||
},
|
||||
"tsd": {
|
||||
"directory": "test"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"LICENSE"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean",
|
||||
"lint": "biome check --write --error-on-warnings",
|
||||
"lint:ci": "biome check --error-on-warnings",
|
||||
"test": "pnpm lint && vitest run --coverage",
|
||||
"test:ci": "pnpm lint:ci && vitest --run --sequence.setupFiles=list --coverage",
|
||||
"clean": "rimraf ./node_modules ./coverage ./dist"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
-22
@@ -1,22 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2021 Luke Childs
|
||||
Copyright (c) 2021-2022 Jared Wray
|
||||
|
||||
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.
|
||||
Generated
Vendored
-15
@@ -1,15 +0,0 @@
|
||||
# @keyv/serialize [<img width="100" align="right" src="https://jaredwray.com/images/keyv-symbol.svg" alt="keyv">](https://github.com/jaredwra/keyv)
|
||||
|
||||
> Serialization functionality for [Keyv](https://github.com/jaredwray/keyv)
|
||||
|
||||
|
||||
[](https://github.com/jaredwray/keyv/actions/workflows/tests.yaml)
|
||||
[](https://codecov.io/gh/jaredwray/keyv)
|
||||
[](https://github.com/jaredwray/keyv/blob/main/LICENSE)
|
||||
[](https://npmjs.com/package/@keyv/serialize)
|
||||
|
||||
This is the serialization functionality for [Keyv](https://github.com/jaredwray/keyv/tree/main/packages/keyv). It is used to serialize and deserialize data for storage and retrieval. You can also create your own [custom serialization functions](https://github.com/jaredwray/keyv/tree/main/packages/keyv#custom-serializers).
|
||||
|
||||
## License
|
||||
|
||||
[MIT © Jared Wray](LISCENCE)
|
||||
Generated
Vendored
-55
@@ -1,55 +0,0 @@
|
||||
{
|
||||
"name": "@keyv/serialize",
|
||||
"version": "1.1.1",
|
||||
"description": "Serialization for Keyv",
|
||||
"type": "module",
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./dist/index.cjs",
|
||||
"import": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jaredwray/keyv.git"
|
||||
},
|
||||
"keywords": [
|
||||
"keyv",
|
||||
"serialize",
|
||||
"key",
|
||||
"value",
|
||||
"store"
|
||||
],
|
||||
"author": "Jared Wray <me@jaredwray.com> (https://jaredwray.com)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jaredwray/keyv/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jaredwray/keyv",
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.2.3",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"rimraf": "^6.0.1",
|
||||
"tsd": "^0.33.0",
|
||||
"typescript": "^5.9.2",
|
||||
"vitest": "^3.2.4",
|
||||
"@keyv/test-suite": "^2.1.1",
|
||||
"keyv": "^5.5.0"
|
||||
},
|
||||
"tsd": {
|
||||
"directory": "test"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"LICENSE"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean",
|
||||
"test": "biome check --write && vitest run --coverage",
|
||||
"test:ci": "biome check && vitest --run --sequence.setupFiles=list --coverage",
|
||||
"clean": "rimraf ./node_modules ./coverage ./dist"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 10
|
||||
src/openjarvis/channels/whatsapp_baileys_bridge/node_modules/@pinojs/redact/.github/workflows/ci.yml
Generated
Vendored
-48
@@ -1,48 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'v*'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
|
||||
# This allows a subsequently queued workflow run to interrupt previous runs
|
||||
concurrency:
|
||||
group: "${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: ${{ matrix.node-version }} ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macOS-latest, windows-latest, ubuntu-latest]
|
||||
node-version: [18, 20, 22, 24]
|
||||
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm i --ignore-scripts
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test
|
||||
Generated
Vendored
-43
@@ -1,43 +0,0 @@
|
||||
name: Publish release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'The version number to tag and release'
|
||||
required: true
|
||||
type: string
|
||||
prerelease:
|
||||
description: 'Release as pre-release'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
release-npm:
|
||||
runs-on: ubuntu-latest
|
||||
environment: main
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4
|
||||
- uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: '22'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- run: npm install npm -g
|
||||
- run: npm install
|
||||
- name: Change version number and sync
|
||||
run: |
|
||||
node scripts/sync-version.mjs ${{ inputs.version }}
|
||||
- name: GIT commit and push all changed files
|
||||
run: |
|
||||
git config --global user.name "mcollina"
|
||||
git config --global user.email "hello@matteocollina.com"
|
||||
git commit -n -a -m "Bumped v${{ inputs.version }}"
|
||||
git push origin HEAD:${{ github.ref }}
|
||||
- run: npm publish --access public --tag ${{ inputs.prerelease == true && 'next' || 'latest' }}
|
||||
- name: 'Create release notes'
|
||||
run: |
|
||||
npx @matteo.collina/release-notes -a ${{ secrets.GITHUB_TOKEN }} -t v${{ inputs.version }} -r redact -o pinojs ${{ github.event.inputs.prerelease == 'true' && '-p' || '' }} -c ${{ github.ref }}
|
||||
Generated
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 pinojs 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.
|
||||
Generated
Vendored
-350
@@ -1,350 +0,0 @@
|
||||
# @pinojs/redact
|
||||
|
||||
> Smart object redaction for JavaScript applications - safe AND fast!
|
||||
|
||||
Redact JS objects with the same API as [fast-redact](https://github.com/davidmarkclements/fast-redact), but uses innovative **selective cloning** instead of mutating the original. This provides immutability guarantees with **performance competitive** to fast-redact for real-world usage patterns.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
npm install @pinojs/redact
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
const slowRedact = require('@pinojs/redact')
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['headers.cookie', 'headers.authorization', 'user.password']
|
||||
})
|
||||
|
||||
const obj = {
|
||||
headers: {
|
||||
cookie: 'secret-session-token',
|
||||
authorization: 'Bearer abc123',
|
||||
'x-forwarded-for': '192.168.1.1'
|
||||
},
|
||||
user: {
|
||||
name: 'john',
|
||||
password: 'secret123'
|
||||
}
|
||||
}
|
||||
|
||||
console.log(redact(obj))
|
||||
// Output: {"headers":{"cookie":"[REDACTED]","authorization":"[REDACTED]","x-forwarded-for":"192.168.1.1"},"user":{"name":"john","password":"[REDACTED]"}}
|
||||
|
||||
// Original object is completely unchanged:
|
||||
console.log(obj.headers.cookie) // 'secret-session-token'
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### slowRedact(options) → Function
|
||||
|
||||
Creates a redaction function with the specified options.
|
||||
|
||||
#### Options
|
||||
|
||||
- **paths** `string[]` (required): An array of strings describing the nested location of a key in an object
|
||||
- **censor** `any` (optional, default: `'[REDACTED]'`): The value to replace sensitive data with. Can be a static value or function.
|
||||
- **serialize** `Function|boolean` (optional, default: `JSON.stringify`): Serialization function. Set to `false` to return the redacted object.
|
||||
- **remove** `boolean` (optional, default: `false`): Remove redacted keys from serialized output
|
||||
- **strict** `boolean` (optional, default: `true`): Throw on non-object values or pass through primitives
|
||||
|
||||
#### Path Syntax
|
||||
|
||||
Supports the same path syntax as fast-redact:
|
||||
|
||||
- **Dot notation**: `'user.name'`, `'headers.cookie'`
|
||||
- **Bracket notation**: `'user["password"]'`, `'headers["X-Forwarded-For"]'`
|
||||
- **Array indices**: `'users[0].password'`, `'items[1].secret'`
|
||||
- **Wildcards**:
|
||||
- Terminal: `'users.*.password'` (redacts password for all users)
|
||||
- Intermediate: `'*.password'` (redacts password at any level)
|
||||
- Array wildcard: `'items.*'` (redacts all array elements)
|
||||
|
||||
#### Examples
|
||||
|
||||
**Custom censor value:**
|
||||
```js
|
||||
const redact = slowRedact({
|
||||
paths: ['password'],
|
||||
censor: '***HIDDEN***'
|
||||
})
|
||||
```
|
||||
|
||||
**Dynamic censor function:**
|
||||
```js
|
||||
const redact = slowRedact({
|
||||
paths: ['password'],
|
||||
censor: (value, path) => `REDACTED:${path}`
|
||||
})
|
||||
```
|
||||
|
||||
**Return object instead of JSON string:**
|
||||
```js
|
||||
const redact = slowRedact({
|
||||
paths: ['secret'],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact({ secret: 'hidden', public: 'data' })
|
||||
console.log(result.secret) // '[REDACTED]'
|
||||
console.log(result.public) // 'data'
|
||||
|
||||
// Restore original values
|
||||
const restored = result.restore()
|
||||
console.log(restored.secret) // 'hidden'
|
||||
```
|
||||
|
||||
**Custom serialization:**
|
||||
```js
|
||||
const redact = slowRedact({
|
||||
paths: ['password'],
|
||||
serialize: obj => JSON.stringify(obj, null, 2)
|
||||
})
|
||||
```
|
||||
|
||||
**Remove keys instead of redacting:**
|
||||
```js
|
||||
const redact = slowRedact({
|
||||
paths: ['password', 'user.secret'],
|
||||
remove: true
|
||||
})
|
||||
|
||||
const obj = { username: 'john', password: 'secret123', user: { name: 'Jane', secret: 'hidden' } }
|
||||
console.log(redact(obj))
|
||||
// Output: {"username":"john","user":{"name":"Jane"}}
|
||||
// Note: 'password' and 'user.secret' are completely absent, not redacted
|
||||
```
|
||||
|
||||
**Wildcard patterns:**
|
||||
```js
|
||||
// Redact all properties in secrets object
|
||||
const redact1 = slowRedact({ paths: ['secrets.*'] })
|
||||
|
||||
// Redact password for any user
|
||||
const redact2 = slowRedact({ paths: ['users.*.password'] })
|
||||
|
||||
// Redact all items in an array
|
||||
const redact3 = slowRedact({ paths: ['items.*'] })
|
||||
|
||||
// Remove all secrets instead of redacting them
|
||||
const redact4 = slowRedact({ paths: ['secrets.*'], remove: true })
|
||||
```
|
||||
|
||||
## Key Differences from fast-redact
|
||||
|
||||
### Safety First
|
||||
- **No mutation**: Original objects are never modified
|
||||
- **Selective cloning**: Only clones paths that need redaction, shares references for everything else
|
||||
- **Restore capability**: Can restore original values when `serialize: false`
|
||||
|
||||
### Feature Compatibility
|
||||
- **Remove option**: Full compatibility with fast-redact's `remove: true` option to completely omit keys from output
|
||||
- **All path patterns**: Supports same syntax including wildcards, bracket notation, and array indices
|
||||
- **Censor functions**: Dynamic censoring with path information passed as arrays
|
||||
- **Serialization**: Custom serializers and `serialize: false` mode
|
||||
|
||||
### Smart Performance Approach
|
||||
- **Selective cloning**: Analyzes redaction paths and only clones necessary object branches
|
||||
- **Reference sharing**: Non-redacted properties maintain original object references
|
||||
- **Memory efficiency**: Dramatically reduced memory usage for large objects with minimal redaction
|
||||
- **Setup-time optimization**: Path analysis happens once during setup, not per redaction
|
||||
|
||||
### When to Use @pinojs/redact
|
||||
- When immutability is critical
|
||||
- When you need to preserve original objects
|
||||
- When objects are shared across multiple contexts
|
||||
- In functional programming environments
|
||||
- When debugging and you need to compare before/after
|
||||
- **Large objects with selective redaction** (now performance-competitive!)
|
||||
- When memory efficiency with reference sharing is important
|
||||
|
||||
### When to Use fast-redact
|
||||
- When absolute maximum performance is critical
|
||||
- In extremely high-throughput scenarios (>100,000 ops/sec)
|
||||
- When you control the object lifecycle and mutation is acceptable
|
||||
- Very small objects where setup overhead matters
|
||||
|
||||
## Performance Benchmarks
|
||||
|
||||
@pinojs/redact uses **selective cloning** that provides good performance while maintaining immutability guarantees:
|
||||
|
||||
### Performance Results
|
||||
|
||||
| Operation Type | @pinojs/redact | fast-redact | Performance Ratio |
|
||||
|---------------|-------------|-------------|-------------------|
|
||||
| **Small objects** | ~690ns | ~200ns | ~3.5x slower |
|
||||
| **Large objects (minimal redaction)** | **~18μs** | ~17μs | **~same performance** |
|
||||
| **Large objects (wildcards)** | **~48μs** | ~37μs | **~1.3x slower** |
|
||||
| **No redaction (large objects)** | **~18μs** | ~17μs | **~same performance** |
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@pinojs/redact is performance-competitive with fast-redact for large objects.
|
||||
|
||||
1. **Selective cloning approach**: Only clones object paths that need redaction
|
||||
2. **Reference sharing**: Non-redacted properties share original object references
|
||||
3. **Setup-time optimization**: Path analysis happens once, not per redaction
|
||||
4. **Memory efficiency**: Dramatically reduced memory usage for typical use cases
|
||||
|
||||
### Benchmark Details
|
||||
|
||||
**Small Objects (~180 bytes)**:
|
||||
- @pinojs/redact: **690ns** per operation
|
||||
- fast-redact: **200ns** per operation
|
||||
- **Slight setup overhead for small objects**
|
||||
|
||||
**Large Objects (~18KB, minimal redaction)**:
|
||||
- @pinojs/redact: **18μs** per operation
|
||||
- fast-redact: **17μs** per operation
|
||||
- Near-identical performance
|
||||
|
||||
**Large Objects (~18KB, wildcard patterns)**:
|
||||
- @pinojs/redact: **48μs** per operation
|
||||
- fast-redact: **37μs** per operation
|
||||
- Competitive performance for complex patterns
|
||||
|
||||
**Memory Considerations**:
|
||||
- @pinojs/redact: **Selective reference sharing** (much lower memory usage than before)
|
||||
- fast-redact: Mutates in-place (lowest memory usage)
|
||||
- Large objects with few redacted paths now share most references
|
||||
|
||||
### When Performance Matters
|
||||
|
||||
Choose **fast-redact** when:
|
||||
- Absolute maximum performance is critical (>100,000 ops/sec)
|
||||
- Working with very small objects frequently
|
||||
- Mutation is acceptable and controlled
|
||||
- Every microsecond counts
|
||||
|
||||
Choose **@pinojs/redact** when:
|
||||
- Immutability is required (with competitive performance)
|
||||
- Objects are shared across contexts
|
||||
- Large objects with selective redaction
|
||||
- Memory efficiency through reference sharing is important
|
||||
- Safety and functionality are priorities
|
||||
- Most production applications (performance gap is minimal)
|
||||
|
||||
Run benchmarks yourself:
|
||||
```bash
|
||||
npm run bench
|
||||
```
|
||||
|
||||
## How Selective Cloning Works
|
||||
|
||||
@pinojs/redact uses an innovative **selective cloning** approach that provides immutability guarantees while dramatically improving performance:
|
||||
|
||||
### Traditional Approach (before optimization)
|
||||
```js
|
||||
// Old approach: Deep clone entire object, then redact
|
||||
const fullClone = deepClone(originalObject) // Clone everything
|
||||
redact(fullClone, paths) // Then redact specific paths
|
||||
```
|
||||
|
||||
### Selective Cloning Approach (current)
|
||||
```js
|
||||
// New approach: Analyze paths, clone only what's needed
|
||||
const pathStructure = buildPathStructure(paths) // One-time setup
|
||||
const selectiveClone = cloneOnlyNeededPaths(obj, pathStructure) // Smart cloning
|
||||
redact(selectiveClone, paths) // Redact pre-identified paths
|
||||
```
|
||||
|
||||
### Key Innovations
|
||||
|
||||
1. **Path Analysis**: Pre-processes redaction paths into an efficient tree structure
|
||||
2. **Selective Cloning**: Only creates new objects for branches that contain redaction targets
|
||||
3. **Reference Sharing**: Non-redacted properties maintain exact same object references
|
||||
4. **Setup Optimization**: Path parsing happens once during redactor creation, not per redaction
|
||||
|
||||
### Example: Reference Sharing in Action
|
||||
|
||||
```js
|
||||
const largeConfig = {
|
||||
database: { /* large config object */ },
|
||||
api: { /* another large config */ },
|
||||
secrets: { password: 'hidden', apiKey: 'secret' }
|
||||
}
|
||||
|
||||
const redact = slowRedact({ paths: ['secrets.password'] })
|
||||
const result = redact(largeConfig)
|
||||
|
||||
// Only secrets object is cloned, database and api share original references
|
||||
console.log(result.database === largeConfig.database) // true - shared reference!
|
||||
console.log(result.api === largeConfig.api) // true - shared reference!
|
||||
console.log(result.secrets === largeConfig.secrets) // false - cloned for redaction
|
||||
```
|
||||
|
||||
This approach provides **immutability where it matters** while **sharing references where it's safe**.
|
||||
|
||||
## Remove Option
|
||||
|
||||
The `remove: true` option provides full compatibility with fast-redact's key removal functionality:
|
||||
|
||||
```js
|
||||
const redact = slowRedact({
|
||||
paths: ['password', 'secrets.*', 'users.*.credentials'],
|
||||
remove: true
|
||||
})
|
||||
|
||||
const data = {
|
||||
username: 'john',
|
||||
password: 'secret123',
|
||||
secrets: { apiKey: 'abc', token: 'xyz' },
|
||||
users: [
|
||||
{ name: 'Alice', credentials: { password: 'pass1' } },
|
||||
{ name: 'Bob', credentials: { password: 'pass2' } }
|
||||
]
|
||||
}
|
||||
|
||||
console.log(redact(data))
|
||||
// Output: {"username":"john","secrets":{},"users":[{"name":"Alice"},{"name":"Bob"}]}
|
||||
```
|
||||
|
||||
### Remove vs Redact Behavior
|
||||
|
||||
| Option | Behavior | Output Example |
|
||||
|--------|----------|----------------|
|
||||
| Default (redact) | Replaces values with censor | `{"password":"[REDACTED]"}` |
|
||||
| `remove: true` | Completely omits keys | `{}` |
|
||||
|
||||
### Compatibility Notes
|
||||
|
||||
- **Same output as fast-redact**: Identical JSON output when using `remove: true`
|
||||
- **Wildcard support**: Works with all wildcard patterns (`*`, `users.*`, `items.*.secret`)
|
||||
- **Array handling**: Array items are set to `undefined` (omitted in JSON output)
|
||||
- **Nested paths**: Supports deep removal (`users.*.credentials.password`)
|
||||
- **Serialize compatibility**: Only works with `JSON.stringify` serializer (like fast-redact)
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Run unit tests
|
||||
npm test
|
||||
|
||||
# Run integration tests comparing with fast-redact
|
||||
npm run test:integration
|
||||
|
||||
# Run all tests (unit + integration)
|
||||
npm run test:all
|
||||
|
||||
# Run benchmarks
|
||||
npm run bench
|
||||
```
|
||||
|
||||
### Test Coverage
|
||||
|
||||
- **16 unit tests**: Core functionality and edge cases
|
||||
- **16 integration tests**: Output compatibility with fast-redact
|
||||
- **All major features**: Paths, wildcards, serialization, custom censors
|
||||
- **Performance benchmarks**: Direct comparison with fast-redact
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Contributing
|
||||
|
||||
Pull requests welcome! Please ensure all tests pass and add tests for new features.
|
||||
Generated
Vendored
-184
@@ -1,184 +0,0 @@
|
||||
const { bench, group, run } = require('mitata')
|
||||
const slowRedact = require('../index.js')
|
||||
const fastRedact = require('fast-redact')
|
||||
|
||||
// Test objects
|
||||
const smallObj = {
|
||||
user: { name: 'john', password: 'secret123' },
|
||||
headers: { cookie: 'session-token', authorization: 'Bearer abc123' }
|
||||
}
|
||||
|
||||
const largeObj = {
|
||||
users: [],
|
||||
metadata: {
|
||||
version: '1.0.0',
|
||||
secret: 'app-secret-key',
|
||||
database: {
|
||||
host: 'localhost',
|
||||
password: 'db-password'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate users array with for loop instead of Array.from
|
||||
for (let i = 0; i < 100; i++) {
|
||||
largeObj.users.push({
|
||||
id: i,
|
||||
name: `user${i}`,
|
||||
email: `user${i}@example.com`,
|
||||
password: `secret${i}`,
|
||||
profile: {
|
||||
age: 20 + (i % 50),
|
||||
preferences: {
|
||||
theme: 'dark',
|
||||
notifications: true,
|
||||
apiKey: `key-${i}-secret`
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Redaction configurations
|
||||
const basicSlowRedact = slowRedact({
|
||||
paths: ['user.password', 'headers.cookie']
|
||||
})
|
||||
|
||||
const basicFastRedact = fastRedact({
|
||||
paths: ['user.password', 'headers.cookie']
|
||||
})
|
||||
|
||||
const wildcardSlowRedact = slowRedact({
|
||||
paths: ['users.*.password', 'users.*.profile.preferences.apiKey']
|
||||
})
|
||||
|
||||
const wildcardFastRedact = fastRedact({
|
||||
paths: ['users.*.password', 'users.*.profile.preferences.apiKey']
|
||||
})
|
||||
|
||||
const deepSlowRedact = slowRedact({
|
||||
paths: ['metadata.secret', 'metadata.database.password']
|
||||
})
|
||||
|
||||
const deepFastRedact = fastRedact({
|
||||
paths: ['metadata.secret', 'metadata.database.password']
|
||||
})
|
||||
|
||||
group('Small Object Redaction - @pinojs/redact', () => {
|
||||
bench('basic paths', () => {
|
||||
basicSlowRedact(smallObj)
|
||||
})
|
||||
|
||||
bench('serialize: false', () => {
|
||||
const redact = slowRedact({
|
||||
paths: ['user.password'],
|
||||
serialize: false
|
||||
})
|
||||
redact(smallObj)
|
||||
})
|
||||
|
||||
bench('custom censor function', () => {
|
||||
const redact = slowRedact({
|
||||
paths: ['user.password'],
|
||||
censor: (value, path) => `HIDDEN:${path}`
|
||||
})
|
||||
redact(smallObj)
|
||||
})
|
||||
})
|
||||
|
||||
group('Small Object Redaction - fast-redact', () => {
|
||||
bench('basic paths', () => {
|
||||
basicFastRedact(smallObj)
|
||||
})
|
||||
|
||||
bench('serialize: false', () => {
|
||||
const redact = fastRedact({
|
||||
paths: ['user.password'],
|
||||
serialize: false
|
||||
})
|
||||
redact(smallObj)
|
||||
})
|
||||
|
||||
bench('custom censor function', () => {
|
||||
const redact = fastRedact({
|
||||
paths: ['user.password'],
|
||||
censor: (value, path) => `HIDDEN:${path}`
|
||||
})
|
||||
redact(smallObj)
|
||||
})
|
||||
})
|
||||
|
||||
group('Large Object Redaction - @pinojs/redact', () => {
|
||||
bench('wildcard patterns', () => {
|
||||
wildcardSlowRedact(largeObj)
|
||||
})
|
||||
|
||||
bench('deep nested paths', () => {
|
||||
deepSlowRedact(largeObj)
|
||||
})
|
||||
|
||||
bench('multiple wildcards', () => {
|
||||
const redact = slowRedact({
|
||||
paths: ['users.*.password', 'users.*.profile.preferences.*']
|
||||
})
|
||||
redact(largeObj)
|
||||
})
|
||||
})
|
||||
|
||||
group('Large Object Redaction - fast-redact', () => {
|
||||
bench('wildcard patterns', () => {
|
||||
wildcardFastRedact(largeObj)
|
||||
})
|
||||
|
||||
bench('deep nested paths', () => {
|
||||
deepFastRedact(largeObj)
|
||||
})
|
||||
|
||||
bench('multiple wildcards', () => {
|
||||
const redact = fastRedact({
|
||||
paths: ['users.*.password', 'users.*.profile.preferences.*']
|
||||
})
|
||||
redact(largeObj)
|
||||
})
|
||||
})
|
||||
|
||||
group('Direct Performance Comparison', () => {
|
||||
bench('@pinojs/redact - basic paths', () => {
|
||||
basicSlowRedact(smallObj)
|
||||
})
|
||||
|
||||
bench('fast-redact - basic paths', () => {
|
||||
basicFastRedact(smallObj)
|
||||
})
|
||||
|
||||
bench('@pinojs/redact - wildcards', () => {
|
||||
wildcardSlowRedact(largeObj)
|
||||
})
|
||||
|
||||
bench('fast-redact - wildcards', () => {
|
||||
wildcardFastRedact(largeObj)
|
||||
})
|
||||
})
|
||||
|
||||
group('Object Cloning Overhead', () => {
|
||||
bench('@pinojs/redact - no redaction (clone only)', () => {
|
||||
const redact = slowRedact({ paths: [] })
|
||||
redact(smallObj)
|
||||
})
|
||||
|
||||
bench('fast-redact - no redaction', () => {
|
||||
const redact = fastRedact({ paths: [] })
|
||||
redact(smallObj)
|
||||
})
|
||||
|
||||
bench('@pinojs/redact - large object clone', () => {
|
||||
const redact = slowRedact({ paths: [] })
|
||||
redact(largeObj)
|
||||
})
|
||||
|
||||
bench('fast-redact - large object', () => {
|
||||
const redact = fastRedact({ paths: [] })
|
||||
redact(largeObj)
|
||||
})
|
||||
})
|
||||
|
||||
run()
|
||||
Generated
Vendored
-1
@@ -1 +0,0 @@
|
||||
module.exports = require('neostandard')()
|
||||
Generated
Vendored
-52
@@ -1,52 +0,0 @@
|
||||
export = F;
|
||||
|
||||
/**
|
||||
* When called without any options, or with a zero length paths array, @pinojs/redact will return JSON.stringify or the serialize option, if set.
|
||||
* @param redactOptions
|
||||
* @param redactOptions.paths An array of strings describing the nested location of a key in an object.
|
||||
* @param redactOptions.censor This is the value which overwrites redacted properties.
|
||||
* @param redactOptions.remove The remove option, when set to true will cause keys to be removed from the serialized output.
|
||||
* @param redactOptions.serialize The serialize option may either be a function or a boolean. If a function is supplied, this will be used to serialize the redacted object.
|
||||
* @param redactOptions.strict The strict option, when set to true, will cause the redactor function to throw if instead of an object it finds a primitive.
|
||||
* @returns Redacted value from input
|
||||
*/
|
||||
declare function F(
|
||||
redactOptions: F.RedactOptionsNoSerialize
|
||||
): F.redactFnNoSerialize;
|
||||
declare function F(redactOptions?: F.RedactOptions): F.redactFn;
|
||||
|
||||
declare namespace F {
|
||||
/** Redacts input */
|
||||
type redactFn = <T>(input: T) => string | T;
|
||||
|
||||
/** Redacts input without serialization */
|
||||
type redactFnNoSerialize = redactFn & {
|
||||
/** Method that allowing the redacted keys to be restored with the original data. Supplied only when serialize option set to false. */
|
||||
restore<T>(input: T): T;
|
||||
};
|
||||
|
||||
interface RedactOptions {
|
||||
/** An array of strings describing the nested location of a key in an object. */
|
||||
paths?: string[] | undefined;
|
||||
|
||||
/** This is the value which overwrites redacted properties. */
|
||||
censor?: string | ((v: any) => any) | undefined;
|
||||
|
||||
/** The remove option, when set to true will cause keys to be removed from the serialized output. */
|
||||
remove?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* The serialize option may either be a function or a boolean. If a function is supplied, this will be used to serialize the redacted object.
|
||||
* The default serialize is the function JSON.stringify
|
||||
*/
|
||||
serialize?: boolean | ((v: any) => any) | undefined;
|
||||
|
||||
/** The strict option, when set to true, will cause the redactor function to throw if instead of an object it finds a primitive. */
|
||||
strict?: boolean | undefined;
|
||||
}
|
||||
|
||||
/** RedactOptions without serialization. Instead of the serialized object, the output of the redactor function will be the mutated object itself. */
|
||||
interface RedactOptionsNoSerialize extends RedactOptions {
|
||||
serialize: false;
|
||||
}
|
||||
}
|
||||
Generated
Vendored
-529
@@ -1,529 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
function deepClone (obj) {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
return obj
|
||||
}
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return new Date(obj.getTime())
|
||||
}
|
||||
|
||||
if (obj instanceof Array) {
|
||||
const cloned = []
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
cloned[i] = deepClone(obj[i])
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
const cloned = Object.create(Object.getPrototypeOf(obj))
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
cloned[key] = deepClone(obj[key])
|
||||
}
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
function parsePath (path) {
|
||||
const parts = []
|
||||
let current = ''
|
||||
let inBrackets = false
|
||||
let inQuotes = false
|
||||
let quoteChar = ''
|
||||
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
const char = path[i]
|
||||
|
||||
if (!inBrackets && char === '.') {
|
||||
if (current) {
|
||||
parts.push(current)
|
||||
current = ''
|
||||
}
|
||||
} else if (char === '[') {
|
||||
if (current) {
|
||||
parts.push(current)
|
||||
current = ''
|
||||
}
|
||||
inBrackets = true
|
||||
} else if (char === ']' && inBrackets) {
|
||||
// Always push the current value when closing brackets, even if it's an empty string
|
||||
parts.push(current)
|
||||
current = ''
|
||||
inBrackets = false
|
||||
inQuotes = false
|
||||
} else if ((char === '"' || char === "'") && inBrackets) {
|
||||
if (!inQuotes) {
|
||||
inQuotes = true
|
||||
quoteChar = char
|
||||
} else if (char === quoteChar) {
|
||||
inQuotes = false
|
||||
quoteChar = ''
|
||||
} else {
|
||||
current += char
|
||||
}
|
||||
} else {
|
||||
current += char
|
||||
}
|
||||
}
|
||||
|
||||
if (current) {
|
||||
parts.push(current)
|
||||
}
|
||||
|
||||
return parts
|
||||
}
|
||||
|
||||
function setValue (obj, parts, value) {
|
||||
let current = obj
|
||||
|
||||
for (let i = 0; i < parts.length - 1; i++) {
|
||||
const key = parts[i]
|
||||
// Type safety: Check if current is an object before using 'in' operator
|
||||
if (typeof current !== 'object' || current === null || !(key in current)) {
|
||||
return false // Path doesn't exist, don't create it
|
||||
}
|
||||
if (typeof current[key] !== 'object' || current[key] === null) {
|
||||
return false // Path doesn't exist properly
|
||||
}
|
||||
current = current[key]
|
||||
}
|
||||
|
||||
const lastKey = parts[parts.length - 1]
|
||||
if (lastKey === '*') {
|
||||
if (Array.isArray(current)) {
|
||||
for (let i = 0; i < current.length; i++) {
|
||||
current[i] = value
|
||||
}
|
||||
} else if (typeof current === 'object' && current !== null) {
|
||||
for (const key in current) {
|
||||
if (Object.prototype.hasOwnProperty.call(current, key)) {
|
||||
current[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Type safety: Check if current is an object before using 'in' operator
|
||||
if (typeof current === 'object' && current !== null && lastKey in current && Object.prototype.hasOwnProperty.call(current, lastKey)) {
|
||||
current[lastKey] = value
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function removeKey (obj, parts) {
|
||||
let current = obj
|
||||
|
||||
for (let i = 0; i < parts.length - 1; i++) {
|
||||
const key = parts[i]
|
||||
// Type safety: Check if current is an object before using 'in' operator
|
||||
if (typeof current !== 'object' || current === null || !(key in current)) {
|
||||
return false // Path doesn't exist, don't create it
|
||||
}
|
||||
if (typeof current[key] !== 'object' || current[key] === null) {
|
||||
return false // Path doesn't exist properly
|
||||
}
|
||||
current = current[key]
|
||||
}
|
||||
|
||||
const lastKey = parts[parts.length - 1]
|
||||
if (lastKey === '*') {
|
||||
if (Array.isArray(current)) {
|
||||
// For arrays, we can't really "remove" all items as that would change indices
|
||||
// Instead, we set them to undefined which will be omitted by JSON.stringify
|
||||
for (let i = 0; i < current.length; i++) {
|
||||
current[i] = undefined
|
||||
}
|
||||
} else if (typeof current === 'object' && current !== null) {
|
||||
for (const key in current) {
|
||||
if (Object.prototype.hasOwnProperty.call(current, key)) {
|
||||
delete current[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Type safety: Check if current is an object before using 'in' operator
|
||||
if (typeof current === 'object' && current !== null && lastKey in current && Object.prototype.hasOwnProperty.call(current, lastKey)) {
|
||||
delete current[lastKey]
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Sentinel object to distinguish between undefined value and non-existent path
|
||||
const PATH_NOT_FOUND = Symbol('PATH_NOT_FOUND')
|
||||
|
||||
function getValueIfExists (obj, parts) {
|
||||
let current = obj
|
||||
|
||||
for (const part of parts) {
|
||||
if (current === null || current === undefined) {
|
||||
return PATH_NOT_FOUND
|
||||
}
|
||||
// Type safety: Check if current is an object before property access
|
||||
if (typeof current !== 'object' || current === null) {
|
||||
return PATH_NOT_FOUND
|
||||
}
|
||||
// Check if the property exists before accessing it
|
||||
if (!(part in current)) {
|
||||
return PATH_NOT_FOUND
|
||||
}
|
||||
current = current[part]
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
function getValue (obj, parts) {
|
||||
let current = obj
|
||||
|
||||
for (const part of parts) {
|
||||
if (current === null || current === undefined) {
|
||||
return undefined
|
||||
}
|
||||
// Type safety: Check if current is an object before property access
|
||||
if (typeof current !== 'object' || current === null) {
|
||||
return undefined
|
||||
}
|
||||
current = current[part]
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
function redactPaths (obj, paths, censor, remove = false) {
|
||||
for (const path of paths) {
|
||||
const parts = parsePath(path)
|
||||
|
||||
if (parts.includes('*')) {
|
||||
redactWildcardPath(obj, parts, censor, path, remove)
|
||||
} else {
|
||||
if (remove) {
|
||||
removeKey(obj, parts)
|
||||
} else {
|
||||
// Get value only if path exists - single traversal
|
||||
const value = getValueIfExists(obj, parts)
|
||||
if (value === PATH_NOT_FOUND) {
|
||||
continue
|
||||
}
|
||||
|
||||
const actualCensor = typeof censor === 'function'
|
||||
? censor(value, parts)
|
||||
: censor
|
||||
setValue(obj, parts, actualCensor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function redactWildcardPath (obj, parts, censor, originalPath, remove = false) {
|
||||
const wildcardIndex = parts.indexOf('*')
|
||||
|
||||
if (wildcardIndex === parts.length - 1) {
|
||||
const parentParts = parts.slice(0, -1)
|
||||
let current = obj
|
||||
|
||||
for (const part of parentParts) {
|
||||
if (current === null || current === undefined) return
|
||||
// Type safety: Check if current is an object before property access
|
||||
if (typeof current !== 'object' || current === null) return
|
||||
current = current[part]
|
||||
}
|
||||
|
||||
if (Array.isArray(current)) {
|
||||
if (remove) {
|
||||
// For arrays, set all items to undefined which will be omitted by JSON.stringify
|
||||
for (let i = 0; i < current.length; i++) {
|
||||
current[i] = undefined
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < current.length; i++) {
|
||||
const indexPath = [...parentParts, i.toString()]
|
||||
const actualCensor = typeof censor === 'function'
|
||||
? censor(current[i], indexPath)
|
||||
: censor
|
||||
current[i] = actualCensor
|
||||
}
|
||||
}
|
||||
} else if (typeof current === 'object' && current !== null) {
|
||||
if (remove) {
|
||||
// Collect keys to delete to avoid issues with deleting during iteration
|
||||
const keysToDelete = []
|
||||
for (const key in current) {
|
||||
if (Object.prototype.hasOwnProperty.call(current, key)) {
|
||||
keysToDelete.push(key)
|
||||
}
|
||||
}
|
||||
for (const key of keysToDelete) {
|
||||
delete current[key]
|
||||
}
|
||||
} else {
|
||||
for (const key in current) {
|
||||
const keyPath = [...parentParts, key]
|
||||
const actualCensor = typeof censor === 'function'
|
||||
? censor(current[key], keyPath)
|
||||
: censor
|
||||
current[key] = actualCensor
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
redactIntermediateWildcard(obj, parts, censor, wildcardIndex, originalPath, remove)
|
||||
}
|
||||
}
|
||||
|
||||
function redactIntermediateWildcard (obj, parts, censor, wildcardIndex, originalPath, remove = false) {
|
||||
const beforeWildcard = parts.slice(0, wildcardIndex)
|
||||
const afterWildcard = parts.slice(wildcardIndex + 1)
|
||||
const pathArray = [] // Cached array to avoid allocations
|
||||
|
||||
function traverse (current, pathLength) {
|
||||
if (pathLength === beforeWildcard.length) {
|
||||
if (Array.isArray(current)) {
|
||||
for (let i = 0; i < current.length; i++) {
|
||||
pathArray[pathLength] = i.toString()
|
||||
traverse(current[i], pathLength + 1)
|
||||
}
|
||||
} else if (typeof current === 'object' && current !== null) {
|
||||
for (const key in current) {
|
||||
pathArray[pathLength] = key
|
||||
traverse(current[key], pathLength + 1)
|
||||
}
|
||||
}
|
||||
} else if (pathLength < beforeWildcard.length) {
|
||||
const nextKey = beforeWildcard[pathLength]
|
||||
// Type safety: Check if current is an object before using 'in' operator
|
||||
if (current && typeof current === 'object' && current !== null && nextKey in current) {
|
||||
pathArray[pathLength] = nextKey
|
||||
traverse(current[nextKey], pathLength + 1)
|
||||
}
|
||||
} else {
|
||||
// Check if afterWildcard contains more wildcards
|
||||
if (afterWildcard.includes('*')) {
|
||||
// Recursively handle remaining wildcards
|
||||
// Wrap censor to prepend current path context
|
||||
const wrappedCensor = typeof censor === 'function'
|
||||
? (value, path) => {
|
||||
const fullPath = [...pathArray.slice(0, pathLength), ...path]
|
||||
return censor(value, fullPath)
|
||||
}
|
||||
: censor
|
||||
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove)
|
||||
} else {
|
||||
// No more wildcards, apply the redaction directly
|
||||
if (remove) {
|
||||
removeKey(current, afterWildcard)
|
||||
} else {
|
||||
const actualCensor = typeof censor === 'function'
|
||||
? censor(getValue(current, afterWildcard), [...pathArray.slice(0, pathLength), ...afterWildcard])
|
||||
: censor
|
||||
setValue(current, afterWildcard, actualCensor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (beforeWildcard.length === 0) {
|
||||
traverse(obj, 0)
|
||||
} else {
|
||||
let current = obj
|
||||
for (let i = 0; i < beforeWildcard.length; i++) {
|
||||
const part = beforeWildcard[i]
|
||||
if (current === null || current === undefined) return
|
||||
// Type safety: Check if current is an object before property access
|
||||
if (typeof current !== 'object' || current === null) return
|
||||
current = current[part]
|
||||
pathArray[i] = part
|
||||
}
|
||||
if (current !== null && current !== undefined) {
|
||||
traverse(current, beforeWildcard.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildPathStructure (pathsToClone) {
|
||||
if (pathsToClone.length === 0) {
|
||||
return null // No paths to redact
|
||||
}
|
||||
|
||||
// Parse all paths and organize by depth
|
||||
const pathStructure = new Map()
|
||||
for (const path of pathsToClone) {
|
||||
const parts = parsePath(path)
|
||||
let current = pathStructure
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const part = parts[i]
|
||||
if (!current.has(part)) {
|
||||
current.set(part, new Map())
|
||||
}
|
||||
current = current.get(part)
|
||||
}
|
||||
}
|
||||
return pathStructure
|
||||
}
|
||||
|
||||
function selectiveClone (obj, pathStructure) {
|
||||
if (!pathStructure) {
|
||||
return obj // No paths to redact, return original
|
||||
}
|
||||
|
||||
function cloneSelectively (source, pathMap, depth = 0) {
|
||||
if (!pathMap || pathMap.size === 0) {
|
||||
return source // No more paths to clone, return reference
|
||||
}
|
||||
|
||||
if (source === null || typeof source !== 'object') {
|
||||
return source
|
||||
}
|
||||
|
||||
if (source instanceof Date) {
|
||||
return new Date(source.getTime())
|
||||
}
|
||||
|
||||
if (Array.isArray(source)) {
|
||||
const cloned = []
|
||||
for (let i = 0; i < source.length; i++) {
|
||||
const indexStr = i.toString()
|
||||
if (pathMap.has(indexStr) || pathMap.has('*')) {
|
||||
cloned[i] = cloneSelectively(source[i], pathMap.get(indexStr) || pathMap.get('*'))
|
||||
} else {
|
||||
cloned[i] = source[i] // Share reference for non-redacted items
|
||||
}
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
// Handle objects
|
||||
const cloned = Object.create(Object.getPrototypeOf(source))
|
||||
for (const key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
if (pathMap.has(key) || pathMap.has('*')) {
|
||||
cloned[key] = cloneSelectively(source[key], pathMap.get(key) || pathMap.get('*'))
|
||||
} else {
|
||||
cloned[key] = source[key] // Share reference for non-redacted properties
|
||||
}
|
||||
}
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
return cloneSelectively(obj, pathStructure)
|
||||
}
|
||||
|
||||
function validatePath (path) {
|
||||
if (typeof path !== 'string') {
|
||||
throw new Error('Paths must be (non-empty) strings')
|
||||
}
|
||||
|
||||
if (path === '') {
|
||||
throw new Error('Invalid redaction path ()')
|
||||
}
|
||||
|
||||
// Check for double dots
|
||||
if (path.includes('..')) {
|
||||
throw new Error(`Invalid redaction path (${path})`)
|
||||
}
|
||||
|
||||
// Check for comma-separated paths (invalid syntax)
|
||||
if (path.includes(',')) {
|
||||
throw new Error(`Invalid redaction path (${path})`)
|
||||
}
|
||||
|
||||
// Check for unmatched brackets
|
||||
let bracketCount = 0
|
||||
let inQuotes = false
|
||||
let quoteChar = ''
|
||||
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
const char = path[i]
|
||||
|
||||
if ((char === '"' || char === "'") && bracketCount > 0) {
|
||||
if (!inQuotes) {
|
||||
inQuotes = true
|
||||
quoteChar = char
|
||||
} else if (char === quoteChar) {
|
||||
inQuotes = false
|
||||
quoteChar = ''
|
||||
}
|
||||
} else if (char === '[' && !inQuotes) {
|
||||
bracketCount++
|
||||
} else if (char === ']' && !inQuotes) {
|
||||
bracketCount--
|
||||
if (bracketCount < 0) {
|
||||
throw new Error(`Invalid redaction path (${path})`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bracketCount !== 0) {
|
||||
throw new Error(`Invalid redaction path (${path})`)
|
||||
}
|
||||
}
|
||||
|
||||
function validatePaths (paths) {
|
||||
if (!Array.isArray(paths)) {
|
||||
throw new TypeError('paths must be an array')
|
||||
}
|
||||
|
||||
for (const path of paths) {
|
||||
validatePath(path)
|
||||
}
|
||||
}
|
||||
|
||||
function slowRedact (options = {}) {
|
||||
const {
|
||||
paths = [],
|
||||
censor = '[REDACTED]',
|
||||
serialize = JSON.stringify,
|
||||
strict = true,
|
||||
remove = false
|
||||
} = options
|
||||
|
||||
// Validate paths upfront to match fast-redact behavior
|
||||
validatePaths(paths)
|
||||
|
||||
// Build path structure once during setup, not on every call
|
||||
const pathStructure = buildPathStructure(paths)
|
||||
|
||||
return function redact (obj) {
|
||||
if (strict && (obj === null || typeof obj !== 'object')) {
|
||||
if (obj === null || obj === undefined) {
|
||||
return serialize ? serialize(obj) : obj
|
||||
}
|
||||
if (typeof obj !== 'object') {
|
||||
return serialize ? serialize(obj) : obj
|
||||
}
|
||||
}
|
||||
|
||||
// Only clone paths that need redaction
|
||||
const cloned = selectiveClone(obj, pathStructure)
|
||||
const original = obj // Keep reference to original for restore
|
||||
|
||||
let actualCensor = censor
|
||||
if (typeof censor === 'function') {
|
||||
actualCensor = censor
|
||||
}
|
||||
|
||||
redactPaths(cloned, paths, actualCensor, remove)
|
||||
|
||||
if (serialize === false) {
|
||||
cloned.restore = function () {
|
||||
return deepClone(original) // Full clone only when restore is called
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
if (typeof serialize === 'function') {
|
||||
return serialize(cloned)
|
||||
}
|
||||
|
||||
return JSON.stringify(cloned)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = slowRedact
|
||||
Generated
Vendored
-22
@@ -1,22 +0,0 @@
|
||||
import { expectType, expectAssignable } from "tsd";
|
||||
import slowRedact from ".";
|
||||
import type { redactFn, redactFnNoSerialize } from ".";
|
||||
|
||||
// should return redactFn
|
||||
expectType<redactFn>(slowRedact());
|
||||
expectType<redactFn>(slowRedact({ paths: [] }));
|
||||
expectType<redactFn>(slowRedact({ paths: ["some.path"] }));
|
||||
expectType<redactFn>(slowRedact({ paths: [], censor: "[REDACTED]" }));
|
||||
expectType<redactFn>(slowRedact({ paths: [], strict: true }));
|
||||
expectType<redactFn>(slowRedact({ paths: [], serialize: JSON.stringify }));
|
||||
expectType<redactFn>(slowRedact({ paths: [], serialize: true }));
|
||||
expectType<redactFnNoSerialize>(slowRedact({ paths: [], serialize: false }));
|
||||
expectType<redactFn>(slowRedact({ paths: [], remove: true }));
|
||||
|
||||
// should return string
|
||||
expectType<string>(slowRedact()(""));
|
||||
|
||||
// should return string or T
|
||||
expectAssignable<string | { someField: string }>(
|
||||
slowRedact()({ someField: "someValue" })
|
||||
);
|
||||
Generated
Vendored
-37
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"name": "@pinojs/redact",
|
||||
"version": "0.4.0",
|
||||
"description": "Redact JS objects",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"scripts": {
|
||||
"test": "node --test && npm run test:types",
|
||||
"test:integration": "node --test test/integration.test.js",
|
||||
"test:types": "tsd",
|
||||
"test:all": "node --test test/*.test.js",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"bench": "node benchmarks/basic.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/pinojs/redact.git"
|
||||
},
|
||||
"keywords": [
|
||||
"redact"
|
||||
],
|
||||
"author": "Matteo Collina <hello@matteocollina.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pinojs/redact/issues"
|
||||
},
|
||||
"homepage": "https://github.com/pinojs/redact#readme",
|
||||
"devDependencies": {
|
||||
"eslint": "^9.36.0",
|
||||
"fast-redact": "^3.5.0",
|
||||
"mitata": "^1.0.34",
|
||||
"neostandard": "^0.12.2",
|
||||
"tsd": "^0.33.0",
|
||||
"typescript": "^5.9.2"
|
||||
}
|
||||
}
|
||||
src/openjarvis/channels/whatsapp_baileys_bridge/node_modules/@pinojs/redact/scripts/sync-version.mjs
Generated
Vendored
-20
@@ -1,20 +0,0 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
const packageJsonPath = path.resolve(import.meta.dirname, '../package.json')
|
||||
let { version } = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
|
||||
|
||||
let passedVersion = process.argv[2]
|
||||
|
||||
if (passedVersion) {
|
||||
passedVersion = passedVersion.trim().replace(/^v/, '')
|
||||
if (version !== passedVersion) {
|
||||
console.log(`Syncing version from ${version} to ${passedVersion}`)
|
||||
version = passedVersion
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
|
||||
packageJson.version = version
|
||||
fs.writeFileSync(path.resolve('./package.json'), JSON.stringify(packageJson, null, 2) + '\n', { encoding: 'utf-8' })
|
||||
}
|
||||
} else {
|
||||
throw new Error('Version argument is required')
|
||||
}
|
||||
Generated
Vendored
-211
@@ -1,211 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
// Node.js test comparing @pinojs/redact vs fast-redact for multiple wildcard patterns
|
||||
// This test validates that @pinojs/redact correctly handles 3+ consecutive wildcards
|
||||
// matching the behavior of fast-redact
|
||||
|
||||
const { test } = require('node:test')
|
||||
const { strict: assert } = require('node:assert')
|
||||
const fastRedact = require('fast-redact')
|
||||
const slowRedact = require('../index.js')
|
||||
|
||||
// Helper function to test redaction and track which values were censored
|
||||
function testRedactDirect (library, pattern, testData = {}) {
|
||||
const matches = []
|
||||
const redactor = library === '@pinojs/redact' ? slowRedact : fastRedact
|
||||
|
||||
try {
|
||||
const redact = redactor({
|
||||
paths: [pattern],
|
||||
censor: (value, path) => {
|
||||
if (
|
||||
value !== undefined &&
|
||||
value !== null &&
|
||||
typeof value === 'string' &&
|
||||
value.includes('secret')
|
||||
) {
|
||||
matches.push({
|
||||
value,
|
||||
path: path ? path.join('.') : 'unknown'
|
||||
})
|
||||
}
|
||||
return '[REDACTED]'
|
||||
}
|
||||
})
|
||||
|
||||
redact(JSON.parse(JSON.stringify(testData)))
|
||||
|
||||
return {
|
||||
library,
|
||||
pattern,
|
||||
matches,
|
||||
success: true,
|
||||
testData
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
library,
|
||||
pattern,
|
||||
matches: [],
|
||||
success: false,
|
||||
error: error.message,
|
||||
testData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function testSlowRedactDirect (pattern, testData) {
|
||||
return testRedactDirect('@pinojs/redact', pattern, testData)
|
||||
}
|
||||
|
||||
function testFastRedactDirect (pattern, testData) {
|
||||
return testRedactDirect('fast-redact', pattern, testData)
|
||||
}
|
||||
|
||||
test('@pinojs/redact: *.password (2 levels)', () => {
|
||||
const result = testSlowRedactDirect('*.password', {
|
||||
simple: { password: 'secret-2-levels' }
|
||||
})
|
||||
|
||||
assert.strictEqual(result.success, true)
|
||||
assert.strictEqual(result.matches.length, 1)
|
||||
assert.strictEqual(result.matches[0].value, 'secret-2-levels')
|
||||
})
|
||||
|
||||
test('@pinojs/redact: *.*.password (3 levels)', () => {
|
||||
const result = testSlowRedactDirect('*.*.password', {
|
||||
simple: { password: 'secret-2-levels' },
|
||||
user: { auth: { password: 'secret-3-levels' } }
|
||||
})
|
||||
|
||||
assert.strictEqual(result.success, true)
|
||||
assert.strictEqual(result.matches.length, 1)
|
||||
assert.strictEqual(result.matches[0].value, 'secret-3-levels')
|
||||
})
|
||||
|
||||
test('@pinojs/redact: *.*.*.password (4 levels)', () => {
|
||||
const result = testSlowRedactDirect('*.*.*.password', {
|
||||
simple: { password: 'secret-2-levels' },
|
||||
user: { auth: { password: 'secret-3-levels' } },
|
||||
nested: { deep: { auth: { password: 'secret-4-levels' } } }
|
||||
})
|
||||
|
||||
assert.strictEqual(result.success, true)
|
||||
assert.strictEqual(result.matches.length, 1)
|
||||
assert.strictEqual(result.matches[0].value, 'secret-4-levels')
|
||||
})
|
||||
|
||||
test('@pinojs/redact: *.*.*.*.password (5 levels)', () => {
|
||||
const result = testSlowRedactDirect('*.*.*.*.password', {
|
||||
simple: { password: 'secret-2-levels' },
|
||||
user: { auth: { password: 'secret-3-levels' } },
|
||||
nested: { deep: { auth: { password: 'secret-4-levels' } } },
|
||||
config: {
|
||||
user: { auth: { settings: { password: 'secret-5-levels' } } }
|
||||
}
|
||||
})
|
||||
|
||||
assert.strictEqual(result.success, true)
|
||||
assert.strictEqual(result.matches.length, 1)
|
||||
assert.strictEqual(result.matches[0].value, 'secret-5-levels')
|
||||
})
|
||||
|
||||
test('@pinojs/redact: *.*.*.*.*.password (6 levels)', () => {
|
||||
const result = testSlowRedactDirect('*.*.*.*.*.password', {
|
||||
simple: { password: 'secret-2-levels' },
|
||||
user: { auth: { password: 'secret-3-levels' } },
|
||||
nested: { deep: { auth: { password: 'secret-4-levels' } } },
|
||||
config: {
|
||||
user: { auth: { settings: { password: 'secret-5-levels' } } }
|
||||
},
|
||||
data: {
|
||||
reqConfig: {
|
||||
data: {
|
||||
credentials: {
|
||||
settings: {
|
||||
password: 'real-secret-6-levels'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
assert.strictEqual(result.success, true)
|
||||
assert.strictEqual(result.matches.length, 1)
|
||||
assert.strictEqual(result.matches[0].value, 'real-secret-6-levels')
|
||||
})
|
||||
|
||||
test('fast-redact: *.password (2 levels)', () => {
|
||||
const result = testFastRedactDirect('*.password', {
|
||||
simple: { password: 'secret-2-levels' }
|
||||
})
|
||||
|
||||
assert.strictEqual(result.success, true)
|
||||
assert.strictEqual(result.matches.length, 1)
|
||||
assert.strictEqual(result.matches[0].value, 'secret-2-levels')
|
||||
})
|
||||
|
||||
test('fast-redact: *.*.password (3 levels)', () => {
|
||||
const result = testFastRedactDirect('*.*.password', {
|
||||
simple: { password: 'secret-2-levels' },
|
||||
user: { auth: { password: 'secret-3-levels' } }
|
||||
})
|
||||
|
||||
assert.strictEqual(result.success, true)
|
||||
assert.strictEqual(result.matches.length, 1)
|
||||
assert.strictEqual(result.matches[0].value, 'secret-3-levels')
|
||||
})
|
||||
|
||||
test('fast-redact: *.*.*.password (4 levels)', () => {
|
||||
const result = testFastRedactDirect('*.*.*.password', {
|
||||
simple: { password: 'secret-2-levels' },
|
||||
user: { auth: { password: 'secret-3-levels' } },
|
||||
nested: { deep: { auth: { password: 'secret-4-levels' } } }
|
||||
})
|
||||
|
||||
assert.strictEqual(result.success, true)
|
||||
assert.strictEqual(result.matches.length, 1)
|
||||
assert.strictEqual(result.matches[0].value, 'secret-4-levels')
|
||||
})
|
||||
|
||||
test('fast-redact: *.*.*.*.password (5 levels)', () => {
|
||||
const result = testFastRedactDirect('*.*.*.*.password', {
|
||||
simple: { password: 'secret-2-levels' },
|
||||
user: { auth: { password: 'secret-3-levels' } },
|
||||
nested: { deep: { auth: { password: 'secret-4-levels' } } },
|
||||
config: {
|
||||
user: { auth: { settings: { password: 'secret-5-levels' } } }
|
||||
}
|
||||
})
|
||||
|
||||
assert.strictEqual(result.success, true)
|
||||
assert.strictEqual(result.matches.length, 1)
|
||||
assert.strictEqual(result.matches[0].value, 'secret-5-levels')
|
||||
})
|
||||
|
||||
test('fast-redact: *.*.*.*.*.password (6 levels)', () => {
|
||||
const result = testFastRedactDirect('*.*.*.*.*.password', {
|
||||
simple: { password: 'secret-2-levels' },
|
||||
user: { auth: { password: 'secret-3-levels' } },
|
||||
nested: { deep: { auth: { password: 'secret-4-levels' } } },
|
||||
config: {
|
||||
user: { auth: { settings: { password: 'secret-5-levels' } } }
|
||||
},
|
||||
data: {
|
||||
reqConfig: {
|
||||
data: {
|
||||
credentials: {
|
||||
settings: {
|
||||
password: 'real-secret-6-levels'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
assert.strictEqual(result.success, true)
|
||||
assert.strictEqual(result.matches.length, 1)
|
||||
assert.strictEqual(result.matches[0].value, 'real-secret-6-levels')
|
||||
})
|
||||
Generated
Vendored
-824
@@ -1,824 +0,0 @@
|
||||
const { test } = require('node:test')
|
||||
const { strict: assert } = require('node:assert')
|
||||
const slowRedact = require('../index.js')
|
||||
|
||||
test('basic path redaction', () => {
|
||||
const obj = {
|
||||
headers: {
|
||||
cookie: 'secret-cookie',
|
||||
authorization: 'Bearer token'
|
||||
},
|
||||
body: { message: 'hello' }
|
||||
}
|
||||
|
||||
const redact = slowRedact({ paths: ['headers.cookie'] })
|
||||
const result = redact(obj)
|
||||
|
||||
// Original object should remain unchanged
|
||||
assert.strictEqual(obj.headers.cookie, 'secret-cookie')
|
||||
|
||||
// Result should have redacted path
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.headers.cookie, '[REDACTED]')
|
||||
assert.strictEqual(parsed.headers.authorization, 'Bearer token')
|
||||
assert.strictEqual(parsed.body.message, 'hello')
|
||||
})
|
||||
|
||||
test('multiple paths redaction', () => {
|
||||
const obj = {
|
||||
user: { name: 'john', password: 'secret' },
|
||||
session: { token: 'abc123' }
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['user.password', 'session.token']
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
// Original unchanged
|
||||
assert.strictEqual(obj.user.password, 'secret')
|
||||
assert.strictEqual(obj.session.token, 'abc123')
|
||||
|
||||
// Result redacted
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.user.password, '[REDACTED]')
|
||||
assert.strictEqual(parsed.session.token, '[REDACTED]')
|
||||
assert.strictEqual(parsed.user.name, 'john')
|
||||
})
|
||||
|
||||
test('custom censor value', () => {
|
||||
const obj = { secret: 'hidden' }
|
||||
const redact = slowRedact({
|
||||
paths: ['secret'],
|
||||
censor: '***'
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.secret, '***')
|
||||
})
|
||||
|
||||
test('serialize: false returns object with restore method', () => {
|
||||
const obj = { secret: 'hidden' }
|
||||
const redact = slowRedact({
|
||||
paths: ['secret'],
|
||||
serialize: false
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
// Should be object, not string
|
||||
assert.strictEqual(typeof result, 'object')
|
||||
assert.strictEqual(result.secret, '[REDACTED]')
|
||||
|
||||
// Should have restore method
|
||||
assert.strictEqual(typeof result.restore, 'function')
|
||||
|
||||
const restored = result.restore()
|
||||
assert.strictEqual(restored.secret, 'hidden')
|
||||
})
|
||||
|
||||
test('bracket notation paths', () => {
|
||||
const obj = {
|
||||
'weird-key': { 'another-weird': 'secret' },
|
||||
normal: 'public'
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['["weird-key"]["another-weird"]']
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed['weird-key']['another-weird'], '[REDACTED]')
|
||||
assert.strictEqual(parsed.normal, 'public')
|
||||
})
|
||||
|
||||
test('array paths', () => {
|
||||
const obj = {
|
||||
users: [
|
||||
{ name: 'john', password: 'secret1' },
|
||||
{ name: 'jane', password: 'secret2' }
|
||||
]
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['users[0].password', 'users[1].password']
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.users[0].password, '[REDACTED]')
|
||||
assert.strictEqual(parsed.users[1].password, '[REDACTED]')
|
||||
assert.strictEqual(parsed.users[0].name, 'john')
|
||||
assert.strictEqual(parsed.users[1].name, 'jane')
|
||||
})
|
||||
|
||||
test('wildcard at end of path', () => {
|
||||
const obj = {
|
||||
secrets: {
|
||||
key1: 'secret1',
|
||||
key2: 'secret2'
|
||||
},
|
||||
public: 'data'
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['secrets.*']
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.secrets.key1, '[REDACTED]')
|
||||
assert.strictEqual(parsed.secrets.key2, '[REDACTED]')
|
||||
assert.strictEqual(parsed.public, 'data')
|
||||
})
|
||||
|
||||
test('wildcard with arrays', () => {
|
||||
const obj = {
|
||||
items: ['secret1', 'secret2', 'secret3']
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['items.*']
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.items[0], '[REDACTED]')
|
||||
assert.strictEqual(parsed.items[1], '[REDACTED]')
|
||||
assert.strictEqual(parsed.items[2], '[REDACTED]')
|
||||
})
|
||||
|
||||
test('intermediate wildcard', () => {
|
||||
const obj = {
|
||||
users: {
|
||||
user1: { password: 'secret1' },
|
||||
user2: { password: 'secret2' }
|
||||
}
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['users.*.password']
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.users.user1.password, '[REDACTED]')
|
||||
assert.strictEqual(parsed.users.user2.password, '[REDACTED]')
|
||||
})
|
||||
|
||||
test('censor function', () => {
|
||||
const obj = { secret: 'hidden' }
|
||||
const redact = slowRedact({
|
||||
paths: ['secret'],
|
||||
censor: (value, path) => `REDACTED:${path.join('.')}`
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.secret, 'REDACTED:secret')
|
||||
})
|
||||
|
||||
test('custom serialize function', () => {
|
||||
const obj = { secret: 'hidden', public: 'data' }
|
||||
const redact = slowRedact({
|
||||
paths: ['secret'],
|
||||
serialize: (obj) => `custom:${JSON.stringify(obj)}`
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
assert(result.startsWith('custom:'))
|
||||
const parsed = JSON.parse(result.slice(7))
|
||||
assert.strictEqual(parsed.secret, '[REDACTED]')
|
||||
assert.strictEqual(parsed.public, 'data')
|
||||
})
|
||||
|
||||
test('nested paths', () => {
|
||||
const obj = {
|
||||
level1: {
|
||||
level2: {
|
||||
level3: {
|
||||
secret: 'hidden'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['level1.level2.level3.secret']
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.level1.level2.level3.secret, '[REDACTED]')
|
||||
})
|
||||
|
||||
test('non-existent paths are ignored', () => {
|
||||
const obj = { existing: 'value' }
|
||||
const redact = slowRedact({
|
||||
paths: ['nonexistent.path']
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.existing, 'value')
|
||||
assert.strictEqual(parsed.nonexistent, undefined)
|
||||
})
|
||||
|
||||
test('null and undefined handling', () => {
|
||||
const obj = {
|
||||
nullValue: null,
|
||||
undefinedValue: undefined,
|
||||
nested: {
|
||||
nullValue: null
|
||||
}
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['nullValue', 'nested.nullValue']
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.nullValue, '[REDACTED]')
|
||||
assert.strictEqual(parsed.nested.nullValue, '[REDACTED]')
|
||||
})
|
||||
|
||||
test('original object remains unchanged', () => {
|
||||
const original = {
|
||||
secret: 'hidden',
|
||||
nested: { secret: 'hidden2' }
|
||||
}
|
||||
const copy = JSON.parse(JSON.stringify(original))
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['secret', 'nested.secret']
|
||||
})
|
||||
redact(original)
|
||||
|
||||
// Original should be completely unchanged
|
||||
assert.deepStrictEqual(original, copy)
|
||||
})
|
||||
|
||||
test('strict mode with primitives', () => {
|
||||
const redact = slowRedact({
|
||||
paths: ['test'],
|
||||
strict: true
|
||||
})
|
||||
|
||||
const stringResult = redact('primitive')
|
||||
assert.strictEqual(stringResult, '"primitive"')
|
||||
|
||||
const numberResult = redact(42)
|
||||
assert.strictEqual(numberResult, '42')
|
||||
})
|
||||
|
||||
// Path validation tests to match fast-redact behavior
|
||||
test('path validation - non-string paths should throw', () => {
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: [123] })
|
||||
}, {
|
||||
message: 'Paths must be (non-empty) strings'
|
||||
})
|
||||
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: [null] })
|
||||
}, {
|
||||
message: 'Paths must be (non-empty) strings'
|
||||
})
|
||||
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: [undefined] })
|
||||
}, {
|
||||
message: 'Paths must be (non-empty) strings'
|
||||
})
|
||||
})
|
||||
|
||||
test('path validation - empty string should throw', () => {
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: [''] })
|
||||
}, {
|
||||
message: 'Invalid redaction path ()'
|
||||
})
|
||||
})
|
||||
|
||||
test('path validation - double dots should throw', () => {
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: ['invalid..path'] })
|
||||
}, {
|
||||
message: 'Invalid redaction path (invalid..path)'
|
||||
})
|
||||
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: ['a..b..c'] })
|
||||
}, {
|
||||
message: 'Invalid redaction path (a..b..c)'
|
||||
})
|
||||
})
|
||||
|
||||
test('path validation - unmatched brackets should throw', () => {
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: ['invalid[unclosed'] })
|
||||
}, {
|
||||
message: 'Invalid redaction path (invalid[unclosed)'
|
||||
})
|
||||
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: ['invalid]unopened'] })
|
||||
}, {
|
||||
message: 'Invalid redaction path (invalid]unopened)'
|
||||
})
|
||||
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: ['nested[a[b]'] })
|
||||
}, {
|
||||
message: 'Invalid redaction path (nested[a[b])'
|
||||
})
|
||||
})
|
||||
|
||||
test('path validation - comma-separated paths should throw', () => {
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: ['req,headers.cookie'] })
|
||||
}, {
|
||||
message: 'Invalid redaction path (req,headers.cookie)'
|
||||
})
|
||||
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: ['user,profile,name'] })
|
||||
}, {
|
||||
message: 'Invalid redaction path (user,profile,name)'
|
||||
})
|
||||
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: ['a,b'] })
|
||||
}, {
|
||||
message: 'Invalid redaction path (a,b)'
|
||||
})
|
||||
})
|
||||
|
||||
test('path validation - mixed valid and invalid should throw', () => {
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: ['valid.path', 123, 'another.valid'] })
|
||||
}, {
|
||||
message: 'Paths must be (non-empty) strings'
|
||||
})
|
||||
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: ['valid.path', 'invalid..path'] })
|
||||
}, {
|
||||
message: 'Invalid redaction path (invalid..path)'
|
||||
})
|
||||
|
||||
assert.throws(() => {
|
||||
slowRedact({ paths: ['valid.path', 'req,headers.cookie'] })
|
||||
}, {
|
||||
message: 'Invalid redaction path (req,headers.cookie)'
|
||||
})
|
||||
})
|
||||
|
||||
test('path validation - valid paths should work', () => {
|
||||
// These should not throw
|
||||
assert.doesNotThrow(() => {
|
||||
slowRedact({ paths: [] })
|
||||
})
|
||||
|
||||
assert.doesNotThrow(() => {
|
||||
slowRedact({ paths: ['valid.path'] })
|
||||
})
|
||||
|
||||
assert.doesNotThrow(() => {
|
||||
slowRedact({ paths: ['user.password', 'data[0].secret'] })
|
||||
})
|
||||
|
||||
assert.doesNotThrow(() => {
|
||||
slowRedact({ paths: ['["quoted-key"].value'] })
|
||||
})
|
||||
|
||||
assert.doesNotThrow(() => {
|
||||
slowRedact({ paths: ["['single-quoted'].value"] })
|
||||
})
|
||||
|
||||
assert.doesNotThrow(() => {
|
||||
slowRedact({ paths: ['array[0]', 'object.property', 'wildcard.*'] })
|
||||
})
|
||||
})
|
||||
|
||||
// fast-redact compatibility tests
|
||||
test('censor function receives path as array (fast-redact compatibility)', () => {
|
||||
const obj = {
|
||||
headers: {
|
||||
authorization: 'Bearer token',
|
||||
'x-api-key': 'secret-key'
|
||||
}
|
||||
}
|
||||
|
||||
const pathsReceived = []
|
||||
const redact = slowRedact({
|
||||
paths: ['headers.authorization', 'headers["x-api-key"]'],
|
||||
censor: (value, path) => {
|
||||
pathsReceived.push(path)
|
||||
assert(Array.isArray(path), 'Path should be an array')
|
||||
return '[REDACTED]'
|
||||
}
|
||||
})
|
||||
|
||||
redact(obj)
|
||||
|
||||
// Verify paths are arrays
|
||||
assert.strictEqual(pathsReceived.length, 2)
|
||||
assert.deepStrictEqual(pathsReceived[0], ['headers', 'authorization'])
|
||||
assert.deepStrictEqual(pathsReceived[1], ['headers', 'x-api-key'])
|
||||
})
|
||||
|
||||
test('censor function with nested paths receives correct array', () => {
|
||||
const obj = {
|
||||
user: {
|
||||
profile: {
|
||||
credentials: {
|
||||
password: 'secret123'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let receivedPath
|
||||
const redact = slowRedact({
|
||||
paths: ['user.profile.credentials.password'],
|
||||
censor: (value, path) => {
|
||||
receivedPath = path
|
||||
assert.strictEqual(value, 'secret123')
|
||||
assert(Array.isArray(path))
|
||||
return '[REDACTED]'
|
||||
}
|
||||
})
|
||||
|
||||
redact(obj)
|
||||
|
||||
assert.deepStrictEqual(receivedPath, ['user', 'profile', 'credentials', 'password'])
|
||||
})
|
||||
|
||||
test('censor function with wildcards receives correct array paths', () => {
|
||||
const obj = {
|
||||
users: {
|
||||
user1: { password: 'secret1' },
|
||||
user2: { password: 'secret2' }
|
||||
}
|
||||
}
|
||||
|
||||
const pathsReceived = []
|
||||
const redact = slowRedact({
|
||||
paths: ['users.*.password'],
|
||||
censor: (value, path) => {
|
||||
pathsReceived.push([...path]) // copy the array
|
||||
assert(Array.isArray(path))
|
||||
return '[REDACTED]'
|
||||
}
|
||||
})
|
||||
|
||||
redact(obj)
|
||||
|
||||
assert.strictEqual(pathsReceived.length, 2)
|
||||
assert.deepStrictEqual(pathsReceived[0], ['users', 'user1', 'password'])
|
||||
assert.deepStrictEqual(pathsReceived[1], ['users', 'user2', 'password'])
|
||||
})
|
||||
|
||||
test('censor function with array wildcard receives correct array paths', () => {
|
||||
const obj = {
|
||||
items: [
|
||||
{ secret: 'value1' },
|
||||
{ secret: 'value2' }
|
||||
]
|
||||
}
|
||||
|
||||
const pathsReceived = []
|
||||
const redact = slowRedact({
|
||||
paths: ['items.*.secret'],
|
||||
censor: (value, path) => {
|
||||
pathsReceived.push([...path])
|
||||
assert(Array.isArray(path))
|
||||
return '[REDACTED]'
|
||||
}
|
||||
})
|
||||
|
||||
redact(obj)
|
||||
|
||||
assert.strictEqual(pathsReceived.length, 2)
|
||||
assert.deepStrictEqual(pathsReceived[0], ['items', '0', 'secret'])
|
||||
assert.deepStrictEqual(pathsReceived[1], ['items', '1', 'secret'])
|
||||
})
|
||||
|
||||
test('censor function with end wildcard receives correct array paths', () => {
|
||||
const obj = {
|
||||
secrets: {
|
||||
key1: 'secret1',
|
||||
key2: 'secret2'
|
||||
}
|
||||
}
|
||||
|
||||
const pathsReceived = []
|
||||
const redact = slowRedact({
|
||||
paths: ['secrets.*'],
|
||||
censor: (value, path) => {
|
||||
pathsReceived.push([...path])
|
||||
assert(Array.isArray(path))
|
||||
return '[REDACTED]'
|
||||
}
|
||||
})
|
||||
|
||||
redact(obj)
|
||||
|
||||
assert.strictEqual(pathsReceived.length, 2)
|
||||
// Sort paths for consistent testing since object iteration order isn't guaranteed
|
||||
pathsReceived.sort((a, b) => a[1].localeCompare(b[1]))
|
||||
assert.deepStrictEqual(pathsReceived[0], ['secrets', 'key1'])
|
||||
assert.deepStrictEqual(pathsReceived[1], ['secrets', 'key2'])
|
||||
})
|
||||
|
||||
test('type safety: accessing properties on primitive values should not throw', () => {
|
||||
// Test case from GitHub issue #5
|
||||
const redactor = slowRedact({ paths: ['headers.authorization'] })
|
||||
const data = {
|
||||
headers: 123 // primitive value
|
||||
}
|
||||
|
||||
assert.doesNotThrow(() => {
|
||||
const result = redactor(data)
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.headers, 123) // Should remain unchanged
|
||||
})
|
||||
|
||||
// Test wildcards with primitives
|
||||
const redactor2 = slowRedact({ paths: ['data.*.nested'] })
|
||||
const data2 = {
|
||||
data: {
|
||||
item1: 123, // primitive, trying to access .nested on it
|
||||
item2: { nested: 'secret' }
|
||||
}
|
||||
}
|
||||
|
||||
assert.doesNotThrow(() => {
|
||||
const result2 = redactor2(data2)
|
||||
const parsed2 = JSON.parse(result2)
|
||||
assert.strictEqual(parsed2.data.item1, 123) // Primitive unchanged
|
||||
assert.strictEqual(parsed2.data.item2.nested, '[REDACTED]') // Object property redacted
|
||||
})
|
||||
|
||||
// Test deep nested access on primitives
|
||||
const redactor3 = slowRedact({ paths: ['user.name.first.charAt'] })
|
||||
const data3 = {
|
||||
user: {
|
||||
name: 'John' // string primitive
|
||||
}
|
||||
}
|
||||
|
||||
assert.doesNotThrow(() => {
|
||||
const result3 = redactor3(data3)
|
||||
const parsed3 = JSON.parse(result3)
|
||||
assert.strictEqual(parsed3.user.name, 'John') // Should remain unchanged
|
||||
})
|
||||
})
|
||||
|
||||
// Remove option tests
|
||||
test('remove option: basic key removal', () => {
|
||||
const obj = { username: 'john', password: 'secret123' }
|
||||
const redact = slowRedact({ paths: ['password'], remove: true })
|
||||
const result = redact(obj)
|
||||
|
||||
// Original object should remain unchanged
|
||||
assert.strictEqual(obj.password, 'secret123')
|
||||
|
||||
// Result should have password completely removed
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.username, 'john')
|
||||
assert.strictEqual('password' in parsed, false)
|
||||
assert.strictEqual(parsed.password, undefined)
|
||||
})
|
||||
|
||||
test('remove option: multiple paths removal', () => {
|
||||
const obj = {
|
||||
user: { name: 'john', password: 'secret' },
|
||||
session: { token: 'abc123', id: 'session1' }
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['user.password', 'session.token'],
|
||||
remove: true
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
// Original unchanged
|
||||
assert.strictEqual(obj.user.password, 'secret')
|
||||
assert.strictEqual(obj.session.token, 'abc123')
|
||||
|
||||
// Result has keys completely removed
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.user.name, 'john')
|
||||
assert.strictEqual(parsed.session.id, 'session1')
|
||||
assert.strictEqual('password' in parsed.user, false)
|
||||
assert.strictEqual('token' in parsed.session, false)
|
||||
})
|
||||
|
||||
test('remove option: wildcard removal', () => {
|
||||
const obj = {
|
||||
secrets: {
|
||||
key1: 'secret1',
|
||||
key2: 'secret2'
|
||||
},
|
||||
public: 'data'
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['secrets.*'],
|
||||
remove: true
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.public, 'data')
|
||||
assert.deepStrictEqual(parsed.secrets, {}) // All keys removed
|
||||
})
|
||||
|
||||
test('remove option: array wildcard removal', () => {
|
||||
const obj = {
|
||||
items: ['secret1', 'secret2', 'secret3'],
|
||||
meta: 'data'
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['items.*'],
|
||||
remove: true
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.meta, 'data')
|
||||
// Array items set to undefined are omitted by JSON.stringify
|
||||
assert.deepStrictEqual(parsed.items, [null, null, null])
|
||||
})
|
||||
|
||||
test('remove option: intermediate wildcard removal', () => {
|
||||
const obj = {
|
||||
users: {
|
||||
user1: { password: 'secret1', name: 'john' },
|
||||
user2: { password: 'secret2', name: 'jane' }
|
||||
}
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['users.*.password'],
|
||||
remove: true
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.users.user1.name, 'john')
|
||||
assert.strictEqual(parsed.users.user2.name, 'jane')
|
||||
assert.strictEqual('password' in parsed.users.user1, false)
|
||||
assert.strictEqual('password' in parsed.users.user2, false)
|
||||
})
|
||||
|
||||
test('remove option: serialize false returns object with removed keys', () => {
|
||||
const obj = { secret: 'hidden', public: 'data' }
|
||||
const redact = slowRedact({
|
||||
paths: ['secret'],
|
||||
remove: true,
|
||||
serialize: false
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
// Should be object, not string
|
||||
assert.strictEqual(typeof result, 'object')
|
||||
assert.strictEqual(result.public, 'data')
|
||||
assert.strictEqual('secret' in result, false)
|
||||
|
||||
// Should have restore method
|
||||
assert.strictEqual(typeof result.restore, 'function')
|
||||
|
||||
const restored = result.restore()
|
||||
assert.strictEqual(restored.secret, 'hidden')
|
||||
})
|
||||
|
||||
test('remove option: non-existent paths are ignored', () => {
|
||||
const obj = { existing: 'value' }
|
||||
const redact = slowRedact({
|
||||
paths: ['nonexistent.path'],
|
||||
remove: true
|
||||
})
|
||||
const result = redact(obj)
|
||||
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed.existing, 'value')
|
||||
assert.strictEqual(parsed.nonexistent, undefined)
|
||||
})
|
||||
|
||||
// Test for Issue #13: Empty string bracket notation paths not being redacted correctly
|
||||
test('empty string bracket notation path', () => {
|
||||
const obj = { '': { c: 'sensitive-data' } }
|
||||
const redact = slowRedact({ paths: ["[''].c"] })
|
||||
const result = redact(obj)
|
||||
|
||||
// Original object should remain unchanged
|
||||
assert.strictEqual(obj[''].c, 'sensitive-data')
|
||||
|
||||
// Result should have redacted path
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed[''].c, '[REDACTED]')
|
||||
})
|
||||
|
||||
test('empty string bracket notation with double quotes', () => {
|
||||
const obj = { '': { c: 'sensitive-data' } }
|
||||
const redact = slowRedact({ paths: ['[""].c'] })
|
||||
const result = redact(obj)
|
||||
|
||||
// Original object should remain unchanged
|
||||
assert.strictEqual(obj[''].c, 'sensitive-data')
|
||||
|
||||
// Result should have redacted path
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed[''].c, '[REDACTED]')
|
||||
})
|
||||
|
||||
test('empty string key with nested bracket notation', () => {
|
||||
const obj = { '': { '': { secret: 'value' } } }
|
||||
const redact = slowRedact({ paths: ["[''][''].secret"] })
|
||||
const result = redact(obj)
|
||||
|
||||
// Original object should remain unchanged
|
||||
assert.strictEqual(obj[''][''].secret, 'value')
|
||||
|
||||
// Result should have redacted path
|
||||
const parsed = JSON.parse(result)
|
||||
assert.strictEqual(parsed[''][''].secret, '[REDACTED]')
|
||||
})
|
||||
|
||||
// Test for Pino issue #2313: censor should only be called when path exists
|
||||
test('censor function not called for non-existent paths', () => {
|
||||
let censorCallCount = 0
|
||||
const censorCalls = []
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['a.b.c', 'req.authorization', 'url'],
|
||||
serialize: false,
|
||||
censor (value, path) {
|
||||
censorCallCount++
|
||||
censorCalls.push({ value, path: path.slice() })
|
||||
return '***'
|
||||
}
|
||||
})
|
||||
|
||||
// Test case 1: { req: { id: 'test' } }
|
||||
// req.authorization doesn't exist, censor should not be called for it
|
||||
censorCallCount = 0
|
||||
censorCalls.length = 0
|
||||
redact({ req: { id: 'test' } })
|
||||
|
||||
// Should not have been called for any path since none exist
|
||||
assert.strictEqual(censorCallCount, 0, 'censor should not be called when paths do not exist')
|
||||
|
||||
// Test case 2: { a: { d: 'test' } }
|
||||
// a.b.c doesn't exist (a.d exists, but not a.b.c)
|
||||
censorCallCount = 0
|
||||
redact({ a: { d: 'test' } })
|
||||
assert.strictEqual(censorCallCount, 0)
|
||||
|
||||
// Test case 3: paths that do exist should still call censor
|
||||
censorCallCount = 0
|
||||
censorCalls.length = 0
|
||||
const result = redact({ req: { authorization: 'bearer token' } })
|
||||
assert.strictEqual(censorCallCount, 1, 'censor should be called when path exists')
|
||||
assert.deepStrictEqual(censorCalls[0].path, ['req', 'authorization'])
|
||||
assert.strictEqual(censorCalls[0].value, 'bearer token')
|
||||
assert.strictEqual(result.req.authorization, '***')
|
||||
})
|
||||
|
||||
test('censor function not called for non-existent nested paths', () => {
|
||||
let censorCallCount = 0
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['headers.authorization'],
|
||||
serialize: false,
|
||||
censor (value, path) {
|
||||
censorCallCount++
|
||||
return '[REDACTED]'
|
||||
}
|
||||
})
|
||||
|
||||
// headers exists but authorization doesn't
|
||||
censorCallCount = 0
|
||||
const result1 = redact({ headers: { 'content-type': 'application/json' } })
|
||||
assert.strictEqual(censorCallCount, 0)
|
||||
assert.deepStrictEqual(result1.headers, { 'content-type': 'application/json' })
|
||||
|
||||
// headers doesn't exist at all
|
||||
censorCallCount = 0
|
||||
const result2 = redact({ body: 'data' })
|
||||
assert.strictEqual(censorCallCount, 0)
|
||||
assert.strictEqual(result2.body, 'data')
|
||||
assert.strictEqual(typeof result2.restore, 'function')
|
||||
|
||||
// headers.authorization exists - should call censor
|
||||
censorCallCount = 0
|
||||
const result3 = redact({ headers: { authorization: 'Bearer token' } })
|
||||
assert.strictEqual(censorCallCount, 1)
|
||||
assert.strictEqual(result3.headers.authorization, '[REDACTED]')
|
||||
})
|
||||
src/openjarvis/channels/whatsapp_baileys_bridge/node_modules/@pinojs/redact/test/integration.test.js
Generated
Vendored
-390
@@ -1,390 +0,0 @@
|
||||
const { test } = require('node:test')
|
||||
const { strict: assert } = require('node:assert')
|
||||
const slowRedact = require('../index.js')
|
||||
const fastRedact = require('fast-redact')
|
||||
|
||||
test('integration: basic path redaction matches fast-redact', () => {
|
||||
const obj = {
|
||||
headers: {
|
||||
cookie: 'secret-cookie',
|
||||
authorization: 'Bearer token'
|
||||
},
|
||||
body: { message: 'hello' }
|
||||
}
|
||||
|
||||
const slowResult = slowRedact({ paths: ['headers.cookie'] })(obj)
|
||||
const fastResult = fastRedact({ paths: ['headers.cookie'] })(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: multiple paths match fast-redact', () => {
|
||||
const obj = {
|
||||
user: { name: 'john', password: 'secret' },
|
||||
session: { token: 'abc123' }
|
||||
}
|
||||
|
||||
const paths = ['user.password', 'session.token']
|
||||
const slowResult = slowRedact({ paths })(obj)
|
||||
const fastResult = fastRedact({ paths })(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: custom censor value matches fast-redact', () => {
|
||||
const obj = { secret: 'hidden' }
|
||||
const options = { paths: ['secret'], censor: '***' }
|
||||
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: bracket notation matches fast-redact', () => {
|
||||
const obj = {
|
||||
'weird-key': { 'another-weird': 'secret' },
|
||||
normal: 'public'
|
||||
}
|
||||
|
||||
const options = { paths: ['["weird-key"]["another-weird"]'] }
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: array paths match fast-redact', () => {
|
||||
const obj = {
|
||||
users: [
|
||||
{ name: 'john', password: 'secret1' },
|
||||
{ name: 'jane', password: 'secret2' }
|
||||
]
|
||||
}
|
||||
|
||||
const options = { paths: ['users[0].password', 'users[1].password'] }
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: wildcard at end matches fast-redact', () => {
|
||||
const obj = {
|
||||
secrets: {
|
||||
key1: 'secret1',
|
||||
key2: 'secret2'
|
||||
},
|
||||
public: 'data'
|
||||
}
|
||||
|
||||
const options = { paths: ['secrets.*'] }
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: wildcard with arrays matches fast-redact', () => {
|
||||
const obj = {
|
||||
items: ['secret1', 'secret2', 'secret3']
|
||||
}
|
||||
|
||||
const options = { paths: ['items.*'] }
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: intermediate wildcard matches fast-redact', () => {
|
||||
const obj = {
|
||||
users: {
|
||||
user1: { password: 'secret1' },
|
||||
user2: { password: 'secret2' }
|
||||
}
|
||||
}
|
||||
|
||||
const options = { paths: ['users.*.password'] }
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: custom serialize function matches fast-redact', () => {
|
||||
const obj = { secret: 'hidden', public: 'data' }
|
||||
const options = {
|
||||
paths: ['secret'],
|
||||
serialize: (obj) => `custom:${JSON.stringify(obj)}`
|
||||
}
|
||||
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: nested paths match fast-redact', () => {
|
||||
const obj = {
|
||||
level1: {
|
||||
level2: {
|
||||
level3: {
|
||||
secret: 'hidden'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const options = { paths: ['level1.level2.level3.secret'] }
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: non-existent paths match fast-redact', () => {
|
||||
const obj = { existing: 'value' }
|
||||
const options = { paths: ['nonexistent.path'] }
|
||||
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: null and undefined handling - legitimate difference', () => {
|
||||
const obj = {
|
||||
nullValue: null,
|
||||
undefinedValue: undefined,
|
||||
nested: {
|
||||
nullValue: null
|
||||
}
|
||||
}
|
||||
|
||||
const options = { paths: ['nullValue', 'nested.nullValue'] }
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
// This is a legitimate behavioral difference:
|
||||
// @pinojs/redact redacts null values, fast-redact doesn't
|
||||
const slowParsed = JSON.parse(slowResult)
|
||||
const fastParsed = JSON.parse(fastResult)
|
||||
|
||||
// @pinojs/redact redacts nulls
|
||||
assert.strictEqual(slowParsed.nullValue, '[REDACTED]')
|
||||
assert.strictEqual(slowParsed.nested.nullValue, '[REDACTED]')
|
||||
|
||||
// fast-redact preserves nulls
|
||||
assert.strictEqual(fastParsed.nullValue, null)
|
||||
assert.strictEqual(fastParsed.nested.nullValue, null)
|
||||
})
|
||||
|
||||
test('integration: strict mode with primitives - different error handling', () => {
|
||||
const options = { paths: ['test'], strict: true }
|
||||
|
||||
const slowRedactFn = slowRedact(options)
|
||||
const fastRedactFn = fastRedact(options)
|
||||
|
||||
// @pinojs/redact handles primitives gracefully
|
||||
const stringSlowResult = slowRedactFn('primitive')
|
||||
assert.strictEqual(stringSlowResult, '"primitive"')
|
||||
|
||||
const numberSlowResult = slowRedactFn(42)
|
||||
assert.strictEqual(numberSlowResult, '42')
|
||||
|
||||
// fast-redact throws an error for primitives in strict mode
|
||||
assert.throws(() => {
|
||||
fastRedactFn('primitive')
|
||||
}, /primitives cannot be redacted/)
|
||||
|
||||
assert.throws(() => {
|
||||
fastRedactFn(42)
|
||||
}, /primitives cannot be redacted/)
|
||||
})
|
||||
|
||||
test('integration: serialize false behavior difference', () => {
|
||||
const slowObj = { secret: 'hidden' }
|
||||
const fastObj = { secret: 'hidden' }
|
||||
const options = { paths: ['secret'], serialize: false }
|
||||
|
||||
const slowResult = slowRedact(options)(slowObj)
|
||||
const fastResult = fastRedact(options)(fastObj)
|
||||
|
||||
// Both should redact the secret
|
||||
assert.strictEqual(slowResult.secret, '[REDACTED]')
|
||||
assert.strictEqual(fastResult.secret, '[REDACTED]')
|
||||
|
||||
// @pinojs/redact always has restore method
|
||||
assert.strictEqual(typeof slowResult.restore, 'function')
|
||||
|
||||
// @pinojs/redact should restore to original value
|
||||
assert.strictEqual(slowResult.restore().secret, 'hidden')
|
||||
|
||||
// Key difference: original object state
|
||||
// fast-redact mutates the original, @pinojs/redact doesn't
|
||||
assert.strictEqual(slowObj.secret, 'hidden') // @pinojs/redact preserves original
|
||||
assert.strictEqual(fastObj.secret, '[REDACTED]') // fast-redact mutates original
|
||||
})
|
||||
|
||||
test('integration: censor function behavior', () => {
|
||||
const obj = { secret: 'hidden' }
|
||||
const options = {
|
||||
paths: ['secret'],
|
||||
censor: (value, path) => `REDACTED:${path}`
|
||||
}
|
||||
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: complex object with mixed patterns', () => {
|
||||
const obj = {
|
||||
users: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'john',
|
||||
credentials: { password: 'secret1', apiKey: 'key1' }
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'jane',
|
||||
credentials: { password: 'secret2', apiKey: 'key2' }
|
||||
}
|
||||
],
|
||||
config: {
|
||||
database: { password: 'db-secret' },
|
||||
api: { keys: ['key1', 'key2', 'key3'] }
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
paths: [
|
||||
'users.*.credentials.password',
|
||||
'users.*.credentials.apiKey',
|
||||
'config.database.password',
|
||||
'config.api.keys.*'
|
||||
]
|
||||
}
|
||||
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
// Remove option integration tests - comparing with fast-redact
|
||||
test('integration: remove option basic comparison with fast-redact', () => {
|
||||
const obj = { username: 'john', password: 'secret123' }
|
||||
const options = { paths: ['password'], remove: true }
|
||||
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
|
||||
// Verify the key is actually removed
|
||||
const parsed = JSON.parse(slowResult)
|
||||
assert.strictEqual(parsed.username, 'john')
|
||||
assert.strictEqual('password' in parsed, false)
|
||||
})
|
||||
|
||||
test('integration: remove option multiple paths comparison with fast-redact', () => {
|
||||
const obj = {
|
||||
user: { name: 'john', password: 'secret' },
|
||||
session: { token: 'abc123', id: 'session1' }
|
||||
}
|
||||
|
||||
const options = {
|
||||
paths: ['user.password', 'session.token'],
|
||||
remove: true
|
||||
}
|
||||
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: remove option wildcard comparison with fast-redact', () => {
|
||||
const obj = {
|
||||
secrets: {
|
||||
key1: 'secret1',
|
||||
key2: 'secret2'
|
||||
},
|
||||
public: 'data'
|
||||
}
|
||||
|
||||
const options = {
|
||||
paths: ['secrets.*'],
|
||||
remove: true
|
||||
}
|
||||
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: remove option intermediate wildcard comparison with fast-redact', () => {
|
||||
const obj = {
|
||||
users: {
|
||||
user1: { password: 'secret1', name: 'john' },
|
||||
user2: { password: 'secret2', name: 'jane' }
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
paths: ['users.*.password'],
|
||||
remove: true
|
||||
}
|
||||
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
})
|
||||
|
||||
test('integration: remove option with custom censor comparison with fast-redact', () => {
|
||||
const obj = { secret: 'hidden', public: 'data' }
|
||||
const options = {
|
||||
paths: ['secret'],
|
||||
censor: '***',
|
||||
remove: true
|
||||
}
|
||||
|
||||
const slowResult = slowRedact(options)(obj)
|
||||
const fastResult = fastRedact(options)(obj)
|
||||
|
||||
assert.strictEqual(slowResult, fastResult)
|
||||
|
||||
// With remove: true, censor value should be ignored
|
||||
const parsed = JSON.parse(slowResult)
|
||||
assert.strictEqual('secret' in parsed, false)
|
||||
assert.strictEqual(parsed.public, 'data')
|
||||
})
|
||||
|
||||
test('integration: remove option serialize false behavior - @pinojs/redact only', () => {
|
||||
// fast-redact doesn't support remove option with serialize: false
|
||||
// so we test @pinojs/redact's behavior only
|
||||
const obj = { secret: 'hidden', public: 'data' }
|
||||
const options = { paths: ['secret'], remove: true, serialize: false }
|
||||
|
||||
const result = slowRedact(options)(obj)
|
||||
|
||||
// Should have the key removed
|
||||
assert.strictEqual('secret' in result, false)
|
||||
assert.strictEqual(result.public, 'data')
|
||||
|
||||
// Should have restore method
|
||||
assert.strictEqual(typeof result.restore, 'function')
|
||||
|
||||
// Original object should be preserved
|
||||
assert.strictEqual(obj.secret, 'hidden')
|
||||
|
||||
// Restore should bring back the removed key
|
||||
const restored = result.restore()
|
||||
assert.strictEqual(restored.secret, 'hidden')
|
||||
})
|
||||
Generated
Vendored
-227
@@ -1,227 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('node:test')
|
||||
const { strict: assert } = require('node:assert')
|
||||
const slowRedact = require('../index.js')
|
||||
|
||||
// Tests for Issue #2319: @pinojs/redact fails to redact patterns with 3+ consecutive wildcards
|
||||
test('three consecutive wildcards: *.*.*.password (4 levels deep)', () => {
|
||||
const obj = {
|
||||
simple: { password: 'secret-2-levels' },
|
||||
user: { auth: { password: 'secret-3-levels' } },
|
||||
nested: { deep: { auth: { password: 'secret-4-levels' } } }
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['*.*.*.password']
|
||||
})
|
||||
const result = redact(obj)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
// Only the 4-level deep password should be redacted
|
||||
assert.strictEqual(parsed.simple.password, 'secret-2-levels', '2-level password should NOT be redacted')
|
||||
assert.strictEqual(parsed.user.auth.password, 'secret-3-levels', '3-level password should NOT be redacted')
|
||||
assert.strictEqual(parsed.nested.deep.auth.password, '[REDACTED]', '4-level password SHOULD be redacted')
|
||||
})
|
||||
|
||||
test('four consecutive wildcards: *.*.*.*.password (5 levels deep)', () => {
|
||||
const obj = {
|
||||
simple: { password: 'secret-2-levels' },
|
||||
user: { auth: { password: 'secret-3-levels' } },
|
||||
nested: { deep: { auth: { password: 'secret-4-levels' } } },
|
||||
config: { user: { auth: { settings: { password: 'secret-5-levels' } } } }
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['*.*.*.*.password']
|
||||
})
|
||||
const result = redact(obj)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
// Only the 5-level deep password should be redacted
|
||||
assert.strictEqual(parsed.simple.password, 'secret-2-levels', '2-level password should NOT be redacted')
|
||||
assert.strictEqual(parsed.user.auth.password, 'secret-3-levels', '3-level password should NOT be redacted')
|
||||
assert.strictEqual(parsed.nested.deep.auth.password, 'secret-4-levels', '4-level password should NOT be redacted')
|
||||
assert.strictEqual(parsed.config.user.auth.settings.password, '[REDACTED]', '5-level password SHOULD be redacted')
|
||||
})
|
||||
|
||||
test('five consecutive wildcards: *.*.*.*.*.password (6 levels deep)', () => {
|
||||
const obj = {
|
||||
simple: { password: 'secret-2-levels' },
|
||||
user: { auth: { password: 'secret-3-levels' } },
|
||||
nested: { deep: { auth: { password: 'secret-4-levels' } } },
|
||||
config: { user: { auth: { settings: { password: 'secret-5-levels' } } } },
|
||||
data: {
|
||||
reqConfig: {
|
||||
data: {
|
||||
credentials: {
|
||||
settings: {
|
||||
password: 'secret-6-levels'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['*.*.*.*.*.password']
|
||||
})
|
||||
const result = redact(obj)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
// Only the 6-level deep password should be redacted
|
||||
assert.strictEqual(parsed.simple.password, 'secret-2-levels', '2-level password should NOT be redacted')
|
||||
assert.strictEqual(parsed.user.auth.password, 'secret-3-levels', '3-level password should NOT be redacted')
|
||||
assert.strictEqual(parsed.nested.deep.auth.password, 'secret-4-levels', '4-level password should NOT be redacted')
|
||||
assert.strictEqual(parsed.config.user.auth.settings.password, 'secret-5-levels', '5-level password should NOT be redacted')
|
||||
assert.strictEqual(parsed.data.reqConfig.data.credentials.settings.password, '[REDACTED]', '6-level password SHOULD be redacted')
|
||||
})
|
||||
|
||||
test('three wildcards with censor function receives correct values', () => {
|
||||
const obj = {
|
||||
nested: { deep: { auth: { password: 'secret-value' } } }
|
||||
}
|
||||
|
||||
const censorCalls = []
|
||||
const redact = slowRedact({
|
||||
paths: ['*.*.*.password'],
|
||||
censor: (value, path) => {
|
||||
censorCalls.push({ value, path: [...path] })
|
||||
return '[REDACTED]'
|
||||
}
|
||||
})
|
||||
|
||||
const result = redact(obj)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
// Should have been called exactly once with the correct value
|
||||
assert.strictEqual(censorCalls.length, 1, 'censor should be called once')
|
||||
assert.strictEqual(censorCalls[0].value, 'secret-value', 'censor should receive the actual value')
|
||||
assert.deepStrictEqual(censorCalls[0].path, ['nested', 'deep', 'auth', 'password'], 'censor should receive correct path')
|
||||
assert.strictEqual(parsed.nested.deep.auth.password, '[REDACTED]')
|
||||
})
|
||||
|
||||
test('three wildcards with multiple matches', () => {
|
||||
const obj = {
|
||||
api1: { v1: { auth: { token: 'token1' } } },
|
||||
api2: { v2: { auth: { token: 'token2' } } },
|
||||
api3: { v1: { auth: { token: 'token3' } } }
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['*.*.*.token']
|
||||
})
|
||||
const result = redact(obj)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
// All three tokens should be redacted
|
||||
assert.strictEqual(parsed.api1.v1.auth.token, '[REDACTED]')
|
||||
assert.strictEqual(parsed.api2.v2.auth.token, '[REDACTED]')
|
||||
assert.strictEqual(parsed.api3.v1.auth.token, '[REDACTED]')
|
||||
})
|
||||
|
||||
test('three wildcards with remove option', () => {
|
||||
const obj = {
|
||||
nested: { deep: { auth: { password: 'secret', username: 'admin' } } }
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['*.*.*.password'],
|
||||
remove: true
|
||||
})
|
||||
const result = redact(obj)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
// Password should be removed entirely
|
||||
assert.strictEqual('password' in parsed.nested.deep.auth, false, 'password key should be removed')
|
||||
assert.strictEqual(parsed.nested.deep.auth.username, 'admin', 'username should remain')
|
||||
})
|
||||
|
||||
test('mixed: two and three wildcards in same redactor', () => {
|
||||
const obj = {
|
||||
user: { auth: { password: 'secret-3-levels' } },
|
||||
config: { deep: { auth: { password: 'secret-4-levels' } } }
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['*.*.password', '*.*.*.password']
|
||||
})
|
||||
const result = redact(obj)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
// Both should be redacted
|
||||
assert.strictEqual(parsed.user.auth.password, '[REDACTED]', '3-level should be redacted by *.*.password')
|
||||
assert.strictEqual(parsed.config.deep.auth.password, '[REDACTED]', '4-level should be redacted by *.*.*.password')
|
||||
})
|
||||
|
||||
test('three wildcards should not call censor for non-existent paths', () => {
|
||||
const obj = {
|
||||
shallow: { data: 'value' },
|
||||
nested: { deep: { auth: { password: 'secret' } } }
|
||||
}
|
||||
|
||||
let censorCallCount = 0
|
||||
const redact = slowRedact({
|
||||
paths: ['*.*.*.password'],
|
||||
censor: (value, path) => {
|
||||
censorCallCount++
|
||||
return '[REDACTED]'
|
||||
}
|
||||
})
|
||||
|
||||
redact(obj)
|
||||
|
||||
// Should only be called once for the path that exists
|
||||
assert.strictEqual(censorCallCount, 1, 'censor should only be called for existing paths')
|
||||
})
|
||||
|
||||
test('three wildcards with arrays', () => {
|
||||
const obj = {
|
||||
users: [
|
||||
{ auth: { password: 'secret1' } },
|
||||
{ auth: { password: 'secret2' } }
|
||||
]
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['*.*.*.password']
|
||||
})
|
||||
const result = redact(obj)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
// Both passwords should be redacted (users[0].auth.password is 4 levels)
|
||||
assert.strictEqual(parsed.users[0].auth.password, '[REDACTED]')
|
||||
assert.strictEqual(parsed.users[1].auth.password, '[REDACTED]')
|
||||
})
|
||||
|
||||
test('four wildcards with authorization header (real-world case)', () => {
|
||||
const obj = {
|
||||
requests: {
|
||||
api1: {
|
||||
config: {
|
||||
headers: {
|
||||
authorization: 'Bearer secret-token'
|
||||
}
|
||||
}
|
||||
},
|
||||
api2: {
|
||||
config: {
|
||||
headers: {
|
||||
authorization: 'Bearer another-token'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['*.*.*.*.authorization']
|
||||
})
|
||||
const result = redact(obj)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
// Both authorization headers should be redacted
|
||||
assert.strictEqual(parsed.requests.api1.config.headers.authorization, '[REDACTED]')
|
||||
assert.strictEqual(parsed.requests.api2.config.headers.authorization, '[REDACTED]')
|
||||
})
|
||||
Generated
Vendored
-223
@@ -1,223 +0,0 @@
|
||||
const { test } = require('node:test')
|
||||
const { strict: assert } = require('node:assert')
|
||||
const slowRedact = require('../index.js')
|
||||
|
||||
/* eslint-disable no-proto */
|
||||
|
||||
test('prototype pollution: __proto__ path should not pollute Object prototype', () => {
|
||||
const obj = {
|
||||
user: { name: 'john' },
|
||||
__proto__: { isAdmin: true }
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['__proto__.isAdmin'],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact(obj)
|
||||
|
||||
// Should not pollute Object.prototype
|
||||
assert.strictEqual(Object.prototype.isAdmin, undefined)
|
||||
assert.strictEqual({}.isAdmin, undefined)
|
||||
|
||||
// Should redact the __proto__ property if it exists as a regular property
|
||||
assert.strictEqual(result.__proto__.isAdmin, '[REDACTED]')
|
||||
})
|
||||
|
||||
test('prototype pollution: constructor.prototype path should not pollute', () => {
|
||||
const obj = {
|
||||
user: { name: 'john' },
|
||||
constructor: {
|
||||
prototype: { isAdmin: true }
|
||||
}
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['constructor.prototype.isAdmin'],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact(obj)
|
||||
|
||||
// Should not pollute Object.prototype
|
||||
assert.strictEqual(Object.prototype.isAdmin, undefined)
|
||||
assert.strictEqual({}.isAdmin, undefined)
|
||||
|
||||
// Should redact the constructor.prototype property if it exists as a regular property
|
||||
assert.strictEqual(result.constructor.prototype.isAdmin, '[REDACTED]')
|
||||
})
|
||||
|
||||
test('prototype pollution: nested __proto__ should not pollute', () => {
|
||||
const obj = {
|
||||
user: {
|
||||
settings: {
|
||||
__proto__: { isAdmin: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['user.settings.__proto__.isAdmin'],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact(obj)
|
||||
|
||||
// Should not pollute Object.prototype
|
||||
assert.strictEqual(Object.prototype.isAdmin, undefined)
|
||||
assert.strictEqual({}.isAdmin, undefined)
|
||||
|
||||
// Should redact the nested __proto__ property
|
||||
assert.strictEqual(result.user.settings.__proto__.isAdmin, '[REDACTED]')
|
||||
})
|
||||
|
||||
test('prototype pollution: bracket notation __proto__ should not pollute', () => {
|
||||
const obj = {
|
||||
user: { name: 'john' },
|
||||
__proto__: { isAdmin: true }
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['["__proto__"]["isAdmin"]'],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact(obj)
|
||||
|
||||
// Should not pollute Object.prototype
|
||||
assert.strictEqual(Object.prototype.isAdmin, undefined)
|
||||
assert.strictEqual({}.isAdmin, undefined)
|
||||
|
||||
// Should redact the __proto__ property when accessed via bracket notation
|
||||
assert.strictEqual(result.__proto__.isAdmin, '[REDACTED]')
|
||||
})
|
||||
|
||||
test('prototype pollution: wildcard with __proto__ should not pollute', () => {
|
||||
const obj = {
|
||||
users: {
|
||||
__proto__: { isAdmin: true },
|
||||
user1: { name: 'john' },
|
||||
user2: { name: 'jane' }
|
||||
}
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['users.*'],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact(obj)
|
||||
|
||||
// Should not pollute Object.prototype
|
||||
assert.strictEqual(Object.prototype.isAdmin, undefined)
|
||||
assert.strictEqual({}.isAdmin, undefined)
|
||||
|
||||
// Should redact only own properties
|
||||
assert.strictEqual(result.users.user1, '[REDACTED]')
|
||||
assert.strictEqual(result.users.user2, '[REDACTED]')
|
||||
|
||||
// __proto__ should only be redacted if it's an own property, not inherited
|
||||
if (Object.prototype.hasOwnProperty.call(obj.users, '__proto__')) {
|
||||
assert.strictEqual(result.users.__proto__, '[REDACTED]')
|
||||
}
|
||||
})
|
||||
|
||||
test('prototype pollution: malicious JSON payload should not pollute', () => {
|
||||
// Simulate a malicious payload that might come from JSON.parse
|
||||
const maliciousObj = JSON.parse('{"user": {"name": "john"}, "__proto__": {"isAdmin": true}}')
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['__proto__.isAdmin'],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact(maliciousObj)
|
||||
|
||||
// Should not pollute Object.prototype
|
||||
assert.strictEqual(Object.prototype.isAdmin, undefined)
|
||||
assert.strictEqual({}.isAdmin, undefined)
|
||||
|
||||
// The malicious payload should have been redacted
|
||||
assert.strictEqual(result.__proto__.isAdmin, '[REDACTED]')
|
||||
})
|
||||
|
||||
test('prototype pollution: verify prototype chain is preserved', () => {
|
||||
function CustomClass () {
|
||||
this.data = 'test'
|
||||
}
|
||||
CustomClass.prototype.method = function () { return 'original' }
|
||||
|
||||
const obj = new CustomClass()
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['data'],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact(obj)
|
||||
|
||||
// Should redact the data property
|
||||
assert.strictEqual(result.data, '[REDACTED]')
|
||||
|
||||
// Should preserve the original prototype chain
|
||||
assert.strictEqual(result.method(), 'original')
|
||||
assert.strictEqual(Object.getPrototypeOf(result), CustomClass.prototype)
|
||||
})
|
||||
|
||||
test('prototype pollution: setValue should not create prototype pollution', () => {
|
||||
const obj = { user: { name: 'john' } }
|
||||
|
||||
// Try to pollute via non-existent path that could create __proto__
|
||||
const redact = slowRedact({
|
||||
paths: ['__proto__.isAdmin'],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact(obj)
|
||||
|
||||
// Should not pollute Object.prototype
|
||||
assert.strictEqual(Object.prototype.isAdmin, undefined)
|
||||
assert.strictEqual({}.isAdmin, undefined)
|
||||
|
||||
// Should not create the path if it doesn't exist
|
||||
// The __proto__ property may exist due to Object.create, but should not contain our redacted value
|
||||
if (result.__proto__) {
|
||||
assert.strictEqual(result.__proto__.isAdmin, undefined)
|
||||
}
|
||||
})
|
||||
|
||||
test('prototype pollution: deep nested prototype properties should not pollute', () => {
|
||||
const obj = {
|
||||
level1: {
|
||||
level2: {
|
||||
level3: {
|
||||
__proto__: { isAdmin: true },
|
||||
constructor: {
|
||||
prototype: { isEvil: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: [
|
||||
'level1.level2.level3.__proto__.isAdmin',
|
||||
'level1.level2.level3.constructor.prototype.isEvil'
|
||||
],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact(obj)
|
||||
|
||||
// Should not pollute Object.prototype
|
||||
assert.strictEqual(Object.prototype.isAdmin, undefined)
|
||||
assert.strictEqual(Object.prototype.isEvil, undefined)
|
||||
assert.strictEqual({}.isAdmin, undefined)
|
||||
assert.strictEqual({}.isEvil, undefined)
|
||||
|
||||
// Should redact the deep nested properties
|
||||
assert.strictEqual(result.level1.level2.level3.__proto__.isAdmin, '[REDACTED]')
|
||||
assert.strictEqual(result.level1.level2.level3.constructor.prototype.isEvil, '[REDACTED]')
|
||||
})
|
||||
Generated
Vendored
-115
@@ -1,115 +0,0 @@
|
||||
const { test } = require('node:test')
|
||||
const { strict: assert } = require('node:assert')
|
||||
const slowRedact = require('../index.js')
|
||||
|
||||
test('selective cloning shares references for non-redacted paths', () => {
|
||||
const sharedObject = { unchanged: 'data' }
|
||||
const obj = {
|
||||
toRedact: 'secret',
|
||||
shared: sharedObject,
|
||||
nested: {
|
||||
toRedact: 'secret2',
|
||||
shared: sharedObject
|
||||
}
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['toRedact', 'nested.toRedact'],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact(obj)
|
||||
|
||||
// Redacted values should be different
|
||||
assert.strictEqual(result.toRedact, '[REDACTED]')
|
||||
assert.strictEqual(result.nested.toRedact, '[REDACTED]')
|
||||
|
||||
// Non-redacted references should be shared (same object reference)
|
||||
assert.strictEqual(result.shared, obj.shared)
|
||||
assert.strictEqual(result.nested.shared, obj.nested.shared)
|
||||
|
||||
// The shared object should be the exact same reference
|
||||
assert.strictEqual(result.shared, sharedObject)
|
||||
assert.strictEqual(result.nested.shared, sharedObject)
|
||||
})
|
||||
|
||||
test('selective cloning works with arrays', () => {
|
||||
const sharedItem = { unchanged: 'data' }
|
||||
const obj = {
|
||||
items: [
|
||||
{ secret: 'hidden1', shared: sharedItem },
|
||||
{ secret: 'hidden2', shared: sharedItem },
|
||||
sharedItem
|
||||
]
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['items.*.secret'],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact(obj)
|
||||
|
||||
// Secrets should be redacted
|
||||
assert.strictEqual(result.items[0].secret, '[REDACTED]')
|
||||
assert.strictEqual(result.items[1].secret, '[REDACTED]')
|
||||
|
||||
// Shared references should be preserved where possible
|
||||
// Note: array items with secrets will be cloned, but their shared properties should still reference the original
|
||||
assert.strictEqual(result.items[0].shared, sharedItem)
|
||||
assert.strictEqual(result.items[1].shared, sharedItem)
|
||||
|
||||
// The third item gets cloned due to wildcard, but should have the same content
|
||||
assert.deepStrictEqual(result.items[2], sharedItem)
|
||||
// Note: Due to wildcard '*', all array items are cloned, even if they don't need redaction
|
||||
// This is still a significant optimization for object properties that aren't in wildcard paths
|
||||
})
|
||||
|
||||
test('selective cloning with no paths returns original object', () => {
|
||||
const obj = { data: 'unchanged' }
|
||||
const redact = slowRedact({
|
||||
paths: [],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact(obj)
|
||||
|
||||
// Should return the exact same object reference
|
||||
assert.strictEqual(result, obj)
|
||||
})
|
||||
|
||||
test('selective cloning performance - large objects with minimal redaction', () => {
|
||||
// Create a large object with mostly shared data
|
||||
const sharedData = { large: 'data'.repeat(1000) }
|
||||
const obj = {
|
||||
secret: 'hidden',
|
||||
shared1: sharedData,
|
||||
shared2: sharedData,
|
||||
nested: {
|
||||
secret: 'hidden2',
|
||||
shared3: sharedData,
|
||||
deep: {
|
||||
shared4: sharedData,
|
||||
moreShared: sharedData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const redact = slowRedact({
|
||||
paths: ['secret', 'nested.secret'],
|
||||
serialize: false
|
||||
})
|
||||
|
||||
const result = redact(obj)
|
||||
|
||||
// Verify redaction worked
|
||||
assert.strictEqual(result.secret, '[REDACTED]')
|
||||
assert.strictEqual(result.nested.secret, '[REDACTED]')
|
||||
|
||||
// Verify shared references are preserved
|
||||
assert.strictEqual(result.shared1, sharedData)
|
||||
assert.strictEqual(result.shared2, sharedData)
|
||||
assert.strictEqual(result.nested.shared3, sharedData)
|
||||
assert.strictEqual(result.nested.deep.shared4, sharedData)
|
||||
assert.strictEqual(result.nested.deep.moreShared, sharedData)
|
||||
})
|
||||
Generated
Vendored
-19
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"lib": [
|
||||
"es6"
|
||||
],
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictNullChecks": true,
|
||||
"types": [],
|
||||
"noEmit": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"files": [
|
||||
"index.d.ts",
|
||||
"index.test-d.ts"
|
||||
]
|
||||
}
|
||||
Generated
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
Copyright (c) 2016, Daniel Wirtz All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* 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.
|
||||
* Neither the name of its author, 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
|
||||
OWNER 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.
|
||||
Generated
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
@protobufjs/aspromise
|
||||
=====================
|
||||
[](https://www.npmjs.com/package/@protobufjs/aspromise)
|
||||
|
||||
Returns a promise from a node-style callback function.
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
* **asPromise(fn: `function`, ctx: `Object`, ...params: `*`): `Promise<*>`**<br />
|
||||
Returns a promise from a node-style callback function.
|
||||
|
||||
**License:** [BSD 3-Clause License](https://opensource.org/licenses/BSD-3-Clause)
|
||||
Generated
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
export = asPromise;
|
||||
|
||||
type asPromiseCallback = (error: Error | null, ...params: any[]) => {};
|
||||
|
||||
/**
|
||||
* Returns a promise from a node-style callback function.
|
||||
* @memberof util
|
||||
* @param {asPromiseCallback} fn Function to call
|
||||
* @param {*} ctx Function context
|
||||
* @param {...*} params Function arguments
|
||||
* @returns {Promise<*>} Promisified function
|
||||
*/
|
||||
declare function asPromise(fn: asPromiseCallback, ctx: any, ...params: any[]): Promise<any>;
|
||||
Generated
Vendored
-52
@@ -1,52 +0,0 @@
|
||||
"use strict";
|
||||
module.exports = asPromise;
|
||||
|
||||
/**
|
||||
* Callback as used by {@link util.asPromise}.
|
||||
* @typedef asPromiseCallback
|
||||
* @type {function}
|
||||
* @param {Error|null} error Error, if any
|
||||
* @param {...*} params Additional arguments
|
||||
* @returns {undefined}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a promise from a node-style callback function.
|
||||
* @memberof util
|
||||
* @param {asPromiseCallback} fn Function to call
|
||||
* @param {*} ctx Function context
|
||||
* @param {...*} params Function arguments
|
||||
* @returns {Promise<*>} Promisified function
|
||||
*/
|
||||
function asPromise(fn, ctx/*, varargs */) {
|
||||
var params = new Array(arguments.length - 1),
|
||||
offset = 0,
|
||||
index = 2,
|
||||
pending = true;
|
||||
while (index < arguments.length)
|
||||
params[offset++] = arguments[index++];
|
||||
return new Promise(function executor(resolve, reject) {
|
||||
params[offset] = function callback(err/*, varargs */) {
|
||||
if (pending) {
|
||||
pending = false;
|
||||
if (err)
|
||||
reject(err);
|
||||
else {
|
||||
var params = new Array(arguments.length - 1),
|
||||
offset = 0;
|
||||
while (offset < params.length)
|
||||
params[offset++] = arguments[offset];
|
||||
resolve.apply(null, params);
|
||||
}
|
||||
}
|
||||
};
|
||||
try {
|
||||
fn.apply(ctx || null, params);
|
||||
} catch (err) {
|
||||
if (pending) {
|
||||
pending = false;
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user