WIP: bootstrap and partial real Solana watcher implementation
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
'use strict'
|
||||
|
||||
const test = require('node:test')
|
||||
const assert = require('node:assert')
|
||||
const { join } = require('node:path')
|
||||
const { once } = require('node:events')
|
||||
const { setImmediate: immediate } = require('node:timers/promises')
|
||||
|
||||
const pino = require('../../')
|
||||
|
||||
test('pino.transport emits error if the worker exits with 0 unexpectably', async (t) => {
|
||||
// This test will take 10s, because flushSync waits for 10s
|
||||
const transport = pino.transport({
|
||||
target: join(__dirname, '..', 'fixtures', 'crashing-transport.js'),
|
||||
sync: true
|
||||
})
|
||||
t.after(transport.end.bind(transport))
|
||||
|
||||
await once(transport, 'ready')
|
||||
|
||||
let maybeError
|
||||
transport.on('error', (err) => {
|
||||
maybeError = err
|
||||
})
|
||||
|
||||
const logger = pino(transport)
|
||||
for (let i = 0; i < 100000; i++) {
|
||||
logger.info('hello')
|
||||
}
|
||||
|
||||
await once(transport.worker, 'exit')
|
||||
|
||||
await immediate()
|
||||
|
||||
assert.equal(maybeError.message, 'the worker has exited')
|
||||
})
|
||||
@@ -0,0 +1,38 @@
|
||||
// Code generated by Herebyfile.mjs generate:enums from internal/nodebuilder/types.go. DO NOT EDIT.
|
||||
export var NodeBuilderFlags;
|
||||
(function (NodeBuilderFlags) {
|
||||
NodeBuilderFlags[NodeBuilderFlags["None"] = 0] = "None";
|
||||
NodeBuilderFlags[NodeBuilderFlags["NoTruncation"] = 1] = "NoTruncation";
|
||||
NodeBuilderFlags[NodeBuilderFlags["WriteArrayAsGenericType"] = 2] = "WriteArrayAsGenericType";
|
||||
NodeBuilderFlags[NodeBuilderFlags["GenerateNamesForShadowedTypeParams"] = 4] = "GenerateNamesForShadowedTypeParams";
|
||||
NodeBuilderFlags[NodeBuilderFlags["UseStructuralFallback"] = 8] = "UseStructuralFallback";
|
||||
NodeBuilderFlags[NodeBuilderFlags["ForbidIndexedAccessSymbolReferences"] = 16] = "ForbidIndexedAccessSymbolReferences";
|
||||
NodeBuilderFlags[NodeBuilderFlags["WriteTypeArgumentsOfSignature"] = 32] = "WriteTypeArgumentsOfSignature";
|
||||
NodeBuilderFlags[NodeBuilderFlags["UseFullyQualifiedType"] = 64] = "UseFullyQualifiedType";
|
||||
NodeBuilderFlags[NodeBuilderFlags["UseOnlyExternalAliasing"] = 128] = "UseOnlyExternalAliasing";
|
||||
NodeBuilderFlags[NodeBuilderFlags["SuppressAnyReturnType"] = 256] = "SuppressAnyReturnType";
|
||||
NodeBuilderFlags[NodeBuilderFlags["WriteTypeParametersInQualifiedName"] = 512] = "WriteTypeParametersInQualifiedName";
|
||||
NodeBuilderFlags[NodeBuilderFlags["MultilineObjectLiterals"] = 1024] = "MultilineObjectLiterals";
|
||||
NodeBuilderFlags[NodeBuilderFlags["WriteClassExpressionAsTypeLiteral"] = 2048] = "WriteClassExpressionAsTypeLiteral";
|
||||
NodeBuilderFlags[NodeBuilderFlags["UseTypeOfFunction"] = 4096] = "UseTypeOfFunction";
|
||||
NodeBuilderFlags[NodeBuilderFlags["OmitParameterModifiers"] = 8192] = "OmitParameterModifiers";
|
||||
NodeBuilderFlags[NodeBuilderFlags["UseAliasDefinedOutsideCurrentScope"] = 16384] = "UseAliasDefinedOutsideCurrentScope";
|
||||
NodeBuilderFlags[NodeBuilderFlags["UseSingleQuotesForStringLiteralType"] = 268435456] = "UseSingleQuotesForStringLiteralType";
|
||||
NodeBuilderFlags[NodeBuilderFlags["NoTypeReduction"] = 536870912] = "NoTypeReduction";
|
||||
NodeBuilderFlags[NodeBuilderFlags["UseInstantiationExpressions"] = 1073741824] = "UseInstantiationExpressions";
|
||||
NodeBuilderFlags[NodeBuilderFlags["OmitThisParameter"] = 33554432] = "OmitThisParameter";
|
||||
NodeBuilderFlags[NodeBuilderFlags["WriteCallStyleSignature"] = 134217728] = "WriteCallStyleSignature";
|
||||
NodeBuilderFlags[NodeBuilderFlags["AllowThisInObjectLiteral"] = 32768] = "AllowThisInObjectLiteral";
|
||||
NodeBuilderFlags[NodeBuilderFlags["AllowQualifiedNameInPlaceOfIdentifier"] = 65536] = "AllowQualifiedNameInPlaceOfIdentifier";
|
||||
NodeBuilderFlags[NodeBuilderFlags["AllowAnonymousIdentifier"] = 131072] = "AllowAnonymousIdentifier";
|
||||
NodeBuilderFlags[NodeBuilderFlags["AllowEmptyUnionOrIntersection"] = 262144] = "AllowEmptyUnionOrIntersection";
|
||||
NodeBuilderFlags[NodeBuilderFlags["AllowEmptyTuple"] = 524288] = "AllowEmptyTuple";
|
||||
NodeBuilderFlags[NodeBuilderFlags["AllowUniqueESSymbolType"] = 1048576] = "AllowUniqueESSymbolType";
|
||||
NodeBuilderFlags[NodeBuilderFlags["AllowEmptyIndexInfoType"] = 2097152] = "AllowEmptyIndexInfoType";
|
||||
NodeBuilderFlags[NodeBuilderFlags["AllowNodeModulesRelativePaths"] = 67108864] = "AllowNodeModulesRelativePaths";
|
||||
NodeBuilderFlags[NodeBuilderFlags["IgnoreErrors"] = 70221824] = "IgnoreErrors";
|
||||
NodeBuilderFlags[NodeBuilderFlags["InObjectTypeLiteral"] = 4194304] = "InObjectTypeLiteral";
|
||||
NodeBuilderFlags[NodeBuilderFlags["InTypeAlias"] = 8388608] = "InTypeAlias";
|
||||
NodeBuilderFlags[NodeBuilderFlags["InInitialEntityName"] = 16777216] = "InInitialEntityName";
|
||||
})(NodeBuilderFlags || (NodeBuilderFlags = {}));
|
||||
//# sourceMappingURL=nodeBuilderFlags.js.map
|
||||
@@ -0,0 +1,7 @@
|
||||
export declare enum DiagnosticCategory {
|
||||
Warning = 0,
|
||||
Error = 1,
|
||||
Suggestion = 2,
|
||||
Message = 3
|
||||
}
|
||||
//# sourceMappingURL=diagnosticCategory.enum.d.ts.map
|
||||
@@ -0,0 +1,249 @@
|
||||
'use strict'
|
||||
|
||||
const Range = require('../classes/range.js')
|
||||
const Comparator = require('../classes/comparator.js')
|
||||
const { ANY } = Comparator
|
||||
const satisfies = require('../functions/satisfies.js')
|
||||
const compare = require('../functions/compare.js')
|
||||
|
||||
// Complex range `r1 || r2 || ...` is a subset of `R1 || R2 || ...` iff:
|
||||
// - Every simple range `r1, r2, ...` is a null set, OR
|
||||
// - Every simple range `r1, r2, ...` which is not a null set is a subset of
|
||||
// some `R1, R2, ...`
|
||||
//
|
||||
// Simple range `c1 c2 ...` is a subset of simple range `C1 C2 ...` iff:
|
||||
// - If c is only the ANY comparator
|
||||
// - If C is only the ANY comparator, return true
|
||||
// - Else if in prerelease mode, return false
|
||||
// - else replace c with `[>=0.0.0]`
|
||||
// - If C is only the ANY comparator
|
||||
// - if in prerelease mode, return true
|
||||
// - else replace C with `[>=0.0.0]`
|
||||
// - Let EQ be the set of = comparators in c
|
||||
// - If EQ is more than one, return true (null set)
|
||||
// - Let GT be the highest > or >= comparator in c
|
||||
// - Let LT be the lowest < or <= comparator in c
|
||||
// - If GT and LT, and GT.semver > LT.semver, return true (null set)
|
||||
// - If any C is a = range, and GT or LT are set, return false
|
||||
// - If EQ
|
||||
// - If GT, and EQ does not satisfy GT, return true (null set)
|
||||
// - If LT, and EQ does not satisfy LT, return true (null set)
|
||||
// - If EQ satisfies every C, return true
|
||||
// - Else return false
|
||||
// - If GT
|
||||
// - If GT.semver is lower than any > or >= comp in C, return false
|
||||
// - If GT is >=, and GT.semver does not satisfy every C, return false
|
||||
// - If GT.semver has a prerelease, and not in prerelease mode
|
||||
// - If no C has a prerelease and the GT.semver tuple, return false
|
||||
// - If LT
|
||||
// - If LT.semver is greater than any < or <= comp in C, return false
|
||||
// - If LT is <=, and LT.semver does not satisfy every C, return false
|
||||
// - If LT.semver has a prerelease, and not in prerelease mode
|
||||
// - If no C has a prerelease and the LT.semver tuple, return false
|
||||
// - Else return true
|
||||
|
||||
const subset = (sub, dom, options = {}) => {
|
||||
if (sub === dom) {
|
||||
return true
|
||||
}
|
||||
|
||||
sub = new Range(sub, options)
|
||||
dom = new Range(dom, options)
|
||||
let sawNonNull = false
|
||||
|
||||
OUTER: for (const simpleSub of sub.set) {
|
||||
for (const simpleDom of dom.set) {
|
||||
const isSub = simpleSubset(simpleSub, simpleDom, options)
|
||||
sawNonNull = sawNonNull || isSub !== null
|
||||
if (isSub) {
|
||||
continue OUTER
|
||||
}
|
||||
}
|
||||
// the null set is a subset of everything, but null simple ranges in
|
||||
// a complex range should be ignored. so if we saw a non-null range,
|
||||
// then we know this isn't a subset, but if EVERY simple range was null,
|
||||
// then it is a subset.
|
||||
if (sawNonNull) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const minimumVersionWithPreRelease = [new Comparator('>=0.0.0-0')]
|
||||
const minimumVersion = [new Comparator('>=0.0.0')]
|
||||
|
||||
const simpleSubset = (sub, dom, options) => {
|
||||
if (sub === dom) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (sub.length === 1 && sub[0].semver === ANY) {
|
||||
if (dom.length === 1 && dom[0].semver === ANY) {
|
||||
return true
|
||||
} else if (options.includePrerelease) {
|
||||
sub = minimumVersionWithPreRelease
|
||||
} else {
|
||||
sub = minimumVersion
|
||||
}
|
||||
}
|
||||
|
||||
if (dom.length === 1 && dom[0].semver === ANY) {
|
||||
if (options.includePrerelease) {
|
||||
return true
|
||||
} else {
|
||||
dom = minimumVersion
|
||||
}
|
||||
}
|
||||
|
||||
const eqSet = new Set()
|
||||
let gt, lt
|
||||
for (const c of sub) {
|
||||
if (c.operator === '>' || c.operator === '>=') {
|
||||
gt = higherGT(gt, c, options)
|
||||
} else if (c.operator === '<' || c.operator === '<=') {
|
||||
lt = lowerLT(lt, c, options)
|
||||
} else {
|
||||
eqSet.add(c.semver)
|
||||
}
|
||||
}
|
||||
|
||||
if (eqSet.size > 1) {
|
||||
return null
|
||||
}
|
||||
|
||||
let gtltComp
|
||||
if (gt && lt) {
|
||||
gtltComp = compare(gt.semver, lt.semver, options)
|
||||
if (gtltComp > 0) {
|
||||
return null
|
||||
} else if (gtltComp === 0 && (gt.operator !== '>=' || lt.operator !== '<=')) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// will iterate one or zero times
|
||||
for (const eq of eqSet) {
|
||||
if (gt && !satisfies(eq, String(gt), options)) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (lt && !satisfies(eq, String(lt), options)) {
|
||||
return null
|
||||
}
|
||||
|
||||
for (const c of dom) {
|
||||
if (!satisfies(eq, String(c), options)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
let higher, lower
|
||||
let hasDomLT, hasDomGT
|
||||
// if the subset has a prerelease, we need a comparator in the superset
|
||||
// with the same tuple and a prerelease, or it's not a subset
|
||||
let needDomLTPre = lt &&
|
||||
!options.includePrerelease &&
|
||||
lt.semver.prerelease.length ? lt.semver : false
|
||||
let needDomGTPre = gt &&
|
||||
!options.includePrerelease &&
|
||||
gt.semver.prerelease.length ? gt.semver : false
|
||||
// exception: <1.2.3-0 is the same as <1.2.3
|
||||
if (needDomLTPre && needDomLTPre.prerelease.length === 1 &&
|
||||
lt.operator === '<' && needDomLTPre.prerelease[0] === 0) {
|
||||
needDomLTPre = false
|
||||
}
|
||||
|
||||
for (const c of dom) {
|
||||
hasDomGT = hasDomGT || c.operator === '>' || c.operator === '>='
|
||||
hasDomLT = hasDomLT || c.operator === '<' || c.operator === '<='
|
||||
if (gt) {
|
||||
if (needDomGTPre) {
|
||||
if (c.semver.prerelease && c.semver.prerelease.length &&
|
||||
c.semver.major === needDomGTPre.major &&
|
||||
c.semver.minor === needDomGTPre.minor &&
|
||||
c.semver.patch === needDomGTPre.patch) {
|
||||
needDomGTPre = false
|
||||
}
|
||||
}
|
||||
if (c.operator === '>' || c.operator === '>=') {
|
||||
higher = higherGT(gt, c, options)
|
||||
if (higher === c && higher !== gt) {
|
||||
return false
|
||||
}
|
||||
} else if (gt.operator === '>=' && !c.test(gt.semver)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (lt) {
|
||||
if (needDomLTPre) {
|
||||
if (c.semver.prerelease && c.semver.prerelease.length &&
|
||||
c.semver.major === needDomLTPre.major &&
|
||||
c.semver.minor === needDomLTPre.minor &&
|
||||
c.semver.patch === needDomLTPre.patch) {
|
||||
needDomLTPre = false
|
||||
}
|
||||
}
|
||||
if (c.operator === '<' || c.operator === '<=') {
|
||||
lower = lowerLT(lt, c, options)
|
||||
if (lower === c && lower !== lt) {
|
||||
return false
|
||||
}
|
||||
} else if (lt.operator === '<=' && !c.test(lt.semver)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (!c.operator && (lt || gt) && gtltComp !== 0) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// if there was a < or >, and nothing in the dom, then must be false
|
||||
// UNLESS it was limited by another range in the other direction.
|
||||
// Eg, >1.0.0 <1.0.1 is still a subset of <2.0.0
|
||||
if (gt && hasDomLT && !lt && gtltComp !== 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (lt && hasDomGT && !gt && gtltComp !== 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
// we needed a prerelease range in a specific tuple, but didn't get one
|
||||
// then this isn't a subset. eg >=1.2.3-pre is not a subset of >=1.0.0,
|
||||
// because it includes prereleases in the 1.2.3 tuple
|
||||
if (needDomGTPre || needDomLTPre) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// >=1.2.3 is lower than >1.2.3
|
||||
const higherGT = (a, b, options) => {
|
||||
if (!a) {
|
||||
return b
|
||||
}
|
||||
const comp = compare(a.semver, b.semver, options)
|
||||
return comp > 0 ? a
|
||||
: comp < 0 ? b
|
||||
: b.operator === '>' && a.operator === '>=' ? b
|
||||
: a
|
||||
}
|
||||
|
||||
// <=1.2.3 is higher than <1.2.3
|
||||
const lowerLT = (a, b, options) => {
|
||||
if (!a) {
|
||||
return b
|
||||
}
|
||||
const comp = compare(a.semver, b.semver, options)
|
||||
return comp < 0 ? a
|
||||
: comp > 0 ? b
|
||||
: b.operator === '<' && a.operator === '<=' ? b
|
||||
: a
|
||||
}
|
||||
|
||||
module.exports = subset
|
||||
@@ -0,0 +1,124 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import be from "../../../locales/be.js";
|
||||
|
||||
describe("Belarusian localization", () => {
|
||||
const localeError = be().localeError;
|
||||
|
||||
describe("pluralization rules", () => {
|
||||
for (const { type, cases } of TEST_CASES) {
|
||||
describe(`${type} pluralization`, () => {
|
||||
for (const { count, expected } of cases) {
|
||||
it(`correctly pluralizes ${count} ${type}`, () => {
|
||||
const error = localeError({
|
||||
code: "too_small",
|
||||
minimum: count,
|
||||
type: "number",
|
||||
inclusive: true,
|
||||
path: [],
|
||||
origin: type,
|
||||
input: count - 1,
|
||||
});
|
||||
expect(error).toContain(expected);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
it("handles negative numbers correctly", () => {
|
||||
const error = localeError({
|
||||
code: "too_small",
|
||||
minimum: -2,
|
||||
type: "number",
|
||||
inclusive: true,
|
||||
path: [],
|
||||
origin: "array",
|
||||
input: -3,
|
||||
});
|
||||
expect(error).toContain("-2 элементы");
|
||||
});
|
||||
|
||||
it("handles zero correctly", () => {
|
||||
const error = localeError({
|
||||
code: "too_small",
|
||||
minimum: 0,
|
||||
type: "number",
|
||||
inclusive: true,
|
||||
path: [],
|
||||
origin: "array",
|
||||
input: -1,
|
||||
});
|
||||
expect(error).toContain("0 элементаў");
|
||||
});
|
||||
|
||||
it("handles bigint values correctly", () => {
|
||||
const error = localeError({
|
||||
code: "too_small",
|
||||
minimum: BigInt(21),
|
||||
type: "number",
|
||||
inclusive: true,
|
||||
path: [],
|
||||
origin: "array",
|
||||
input: BigInt(20),
|
||||
});
|
||||
expect(error).toContain("21 элемент");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const TEST_CASES = [
|
||||
{
|
||||
type: "array",
|
||||
cases: [
|
||||
{ count: 1, expected: "1 элемент" },
|
||||
{ count: 2, expected: "2 элементы" },
|
||||
{ count: 5, expected: "5 элементаў" },
|
||||
{ count: 11, expected: "11 элементаў" },
|
||||
{ count: 21, expected: "21 элемент" },
|
||||
{ count: 22, expected: "22 элементы" },
|
||||
{ count: 25, expected: "25 элементаў" },
|
||||
{ count: 101, expected: "101 элемент" },
|
||||
{ count: 111, expected: "111 элементаў" },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "set",
|
||||
cases: [
|
||||
{ count: 1, expected: "1 элемент" },
|
||||
{ count: 2, expected: "2 элементы" },
|
||||
{ count: 5, expected: "5 элементаў" },
|
||||
{ count: 11, expected: "11 элементаў" },
|
||||
{ count: 21, expected: "21 элемент" },
|
||||
{ count: 22, expected: "22 элементы" },
|
||||
{ count: 25, expected: "25 элементаў" },
|
||||
{ count: 101, expected: "101 элемент" },
|
||||
{ count: 111, expected: "111 элементаў" },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
cases: [
|
||||
{ count: 1, expected: "1 сімвал" },
|
||||
{ count: 2, expected: "2 сімвалы" },
|
||||
{ count: 5, expected: "5 сімвалаў" },
|
||||
{ count: 11, expected: "11 сімвалаў" },
|
||||
{ count: 21, expected: "21 сімвал" },
|
||||
{ count: 22, expected: "22 сімвалы" },
|
||||
{ count: 25, expected: "25 сімвалаў" },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
cases: [
|
||||
{ count: 0, expected: "0 байтаў" },
|
||||
{ count: 1, expected: "1 байт" },
|
||||
{ count: 2, expected: "2 байты" },
|
||||
{ count: 5, expected: "5 байтаў" },
|
||||
{ count: 11, expected: "11 байтаў" },
|
||||
{ count: 21, expected: "21 байт" },
|
||||
{ count: 22, expected: "22 байты" },
|
||||
{ count: 25, expected: "25 байтаў" },
|
||||
{ count: 101, expected: "101 байт" },
|
||||
{ count: 110, expected: "110 байтаў" },
|
||||
],
|
||||
},
|
||||
] as const;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,122 @@
|
||||
function x509Error(msg, cert) {
|
||||
return new Error('SASL channel binding: ' + msg + ' when parsing public certificate ' + cert.toString('base64'))
|
||||
}
|
||||
|
||||
function readASN1Length(data, index) {
|
||||
let length = data[index++]
|
||||
if (length < 0x80) return { length, index }
|
||||
|
||||
const lengthBytes = length & 0x7f
|
||||
if (lengthBytes > 4) throw x509Error('bad length', data)
|
||||
|
||||
length = 0
|
||||
for (let i = 0; i < lengthBytes; i++) {
|
||||
length = (length << 8) | data[index++]
|
||||
}
|
||||
|
||||
return { length, index }
|
||||
}
|
||||
|
||||
function readASN1OID(data, index) {
|
||||
if (data[index++] !== 0x6) throw x509Error('non-OID data', data) // 6 = OID
|
||||
|
||||
const { length: OIDLength, index: indexAfterOIDLength } = readASN1Length(data, index)
|
||||
index = indexAfterOIDLength
|
||||
const lastIndex = index + OIDLength
|
||||
|
||||
const byte1 = data[index++]
|
||||
let oid = ((byte1 / 40) >> 0) + '.' + (byte1 % 40)
|
||||
|
||||
while (index < lastIndex) {
|
||||
// loop over numbers in OID
|
||||
let value = 0
|
||||
while (index < lastIndex) {
|
||||
// loop over bytes in number
|
||||
const nextByte = data[index++]
|
||||
value = (value << 7) | (nextByte & 0x7f)
|
||||
if (nextByte < 0x80) break
|
||||
}
|
||||
oid += '.' + value
|
||||
}
|
||||
|
||||
return { oid, index }
|
||||
}
|
||||
|
||||
function expectASN1Seq(data, index) {
|
||||
if (data[index++] !== 0x30) throw x509Error('non-sequence data', data) // 30 = Sequence
|
||||
return readASN1Length(data, index)
|
||||
}
|
||||
|
||||
function signatureAlgorithmHashFromCertificate(data, index) {
|
||||
// read this thread: https://www.postgresql.org/message-id/17760-b6c61e752ec07060%40postgresql.org
|
||||
if (index === undefined) index = 0
|
||||
index = expectASN1Seq(data, index).index
|
||||
const { length: certInfoLength, index: indexAfterCertInfoLength } = expectASN1Seq(data, index)
|
||||
index = indexAfterCertInfoLength + certInfoLength // skip over certificate info
|
||||
index = expectASN1Seq(data, index).index // skip over signature length field
|
||||
const { oid, index: indexAfterOID } = readASN1OID(data, index)
|
||||
switch (oid) {
|
||||
// RSA
|
||||
case '1.2.840.113549.1.1.4':
|
||||
return 'MD5'
|
||||
case '1.2.840.113549.1.1.5':
|
||||
return 'SHA-1'
|
||||
case '1.2.840.113549.1.1.11':
|
||||
return 'SHA-256'
|
||||
case '1.2.840.113549.1.1.12':
|
||||
return 'SHA-384'
|
||||
case '1.2.840.113549.1.1.13':
|
||||
return 'SHA-512'
|
||||
case '1.2.840.113549.1.1.14':
|
||||
return 'SHA-224'
|
||||
case '1.2.840.113549.1.1.15':
|
||||
return 'SHA512-224'
|
||||
case '1.2.840.113549.1.1.16':
|
||||
return 'SHA512-256'
|
||||
// ECDSA
|
||||
case '1.2.840.10045.4.1':
|
||||
return 'SHA-1'
|
||||
case '1.2.840.10045.4.3.1':
|
||||
return 'SHA-224'
|
||||
case '1.2.840.10045.4.3.2':
|
||||
return 'SHA-256'
|
||||
case '1.2.840.10045.4.3.3':
|
||||
return 'SHA-384'
|
||||
case '1.2.840.10045.4.3.4':
|
||||
return 'SHA-512'
|
||||
// RSASSA-PSS: hash is indicated separately
|
||||
case '1.2.840.113549.1.1.10': {
|
||||
index = indexAfterOID
|
||||
index = expectASN1Seq(data, index).index
|
||||
if (data[index++] !== 0xa0) throw x509Error('non-tag data', data) // a0 = constructed tag 0
|
||||
index = readASN1Length(data, index).index // skip over tag length field
|
||||
index = expectASN1Seq(data, index).index // skip over sequence length field
|
||||
const { oid: hashOID } = readASN1OID(data, index)
|
||||
switch (hashOID) {
|
||||
// standalone hash OIDs
|
||||
case '1.2.840.113549.2.5':
|
||||
return 'MD5'
|
||||
case '1.3.14.3.2.26':
|
||||
return 'SHA-1'
|
||||
case '2.16.840.1.101.3.4.2.1':
|
||||
return 'SHA-256'
|
||||
case '2.16.840.1.101.3.4.2.2':
|
||||
return 'SHA-384'
|
||||
case '2.16.840.1.101.3.4.2.3':
|
||||
return 'SHA-512'
|
||||
}
|
||||
throw x509Error('unknown hash OID ' + hashOID, data)
|
||||
}
|
||||
// Ed25519 -- see https: return//github.com/openssl/openssl/issues/15477
|
||||
case '1.3.101.110':
|
||||
case '1.3.101.112': // ph
|
||||
return 'SHA-512'
|
||||
// Ed448 -- still not in pg 17.2 (if supported, digest would be SHAKE256 x 64 bytes)
|
||||
case '1.3.101.111':
|
||||
case '1.3.101.113': // ph
|
||||
throw x509Error('Ed448 certificate channel binding is not currently supported by Postgres')
|
||||
}
|
||||
throw x509Error('unknown OID ' + oid, data)
|
||||
}
|
||||
|
||||
module.exports = { signatureAlgorithmHashFromCertificate }
|
||||
@@ -0,0 +1 @@
|
||||
export { _ as default } from "../esm/_ts_metadata.js";
|
||||
@@ -0,0 +1,2 @@
|
||||
export declare const crypto: any;
|
||||
//# sourceMappingURL=crypto.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC"}
|
||||
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
|
||||
function _extends() {
|
||||
exports._ = _extends = Object.assign || function assign(target) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var source = arguments[i];
|
||||
for (var key in source) if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
return _extends.apply(this, arguments);
|
||||
}
|
||||
exports._ = _extends;
|
||||
@@ -0,0 +1,383 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag dangling underscores in variable declarations.
|
||||
* @author Matt DuVall <http://www.mattduvall.com>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
defaultOptions: [
|
||||
{
|
||||
allow: [],
|
||||
allowAfterSuper: false,
|
||||
allowAfterThis: false,
|
||||
allowAfterThisConstructor: false,
|
||||
allowFunctionParams: true,
|
||||
allowInArrayDestructuring: true,
|
||||
allowInObjectDestructuring: true,
|
||||
enforceInClassFields: false,
|
||||
enforceInMethodNames: false,
|
||||
},
|
||||
],
|
||||
|
||||
docs: {
|
||||
description: "Disallow dangling underscores in identifiers",
|
||||
recommended: false,
|
||||
frozen: true,
|
||||
url: "https://eslint.org/docs/latest/rules/no-underscore-dangle",
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
allow: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
allowAfterThis: {
|
||||
type: "boolean",
|
||||
},
|
||||
allowAfterSuper: {
|
||||
type: "boolean",
|
||||
},
|
||||
allowAfterThisConstructor: {
|
||||
type: "boolean",
|
||||
},
|
||||
enforceInMethodNames: {
|
||||
type: "boolean",
|
||||
},
|
||||
allowFunctionParams: {
|
||||
type: "boolean",
|
||||
},
|
||||
enforceInClassFields: {
|
||||
type: "boolean",
|
||||
},
|
||||
allowInArrayDestructuring: {
|
||||
type: "boolean",
|
||||
},
|
||||
allowInObjectDestructuring: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpectedUnderscore:
|
||||
"Unexpected dangling '_' in '{{identifier}}'.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const [
|
||||
{
|
||||
allow,
|
||||
allowAfterSuper,
|
||||
allowAfterThis,
|
||||
allowAfterThisConstructor,
|
||||
allowFunctionParams,
|
||||
allowInArrayDestructuring,
|
||||
allowInObjectDestructuring,
|
||||
enforceInClassFields,
|
||||
enforceInMethodNames,
|
||||
},
|
||||
] = context.options;
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Check if identifier is present inside the allowed option
|
||||
* @param {string} identifier name of the node
|
||||
* @returns {boolean} true if its is present
|
||||
* @private
|
||||
*/
|
||||
function isAllowed(identifier) {
|
||||
return allow.includes(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if identifier has a dangling underscore
|
||||
* @param {string} identifier name of the node
|
||||
* @returns {boolean} true if its is present
|
||||
* @private
|
||||
*/
|
||||
function hasDanglingUnderscore(identifier) {
|
||||
const len = identifier.length;
|
||||
|
||||
return (
|
||||
identifier !== "_" &&
|
||||
(identifier[0] === "_" || identifier[len - 1] === "_")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if identifier is a special case member expression
|
||||
* @param {string} identifier name of the node
|
||||
* @returns {boolean} true if its is a special case
|
||||
* @private
|
||||
*/
|
||||
function isSpecialCaseIdentifierForMemberExpression(identifier) {
|
||||
return identifier === "__proto__";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if identifier is a special case variable expression
|
||||
* @param {string} identifier name of the node
|
||||
* @returns {boolean} true if its is a special case
|
||||
* @private
|
||||
*/
|
||||
function isSpecialCaseIdentifierInVariableExpression(identifier) {
|
||||
// Checks for the underscore library usage here
|
||||
return identifier === "_";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a node is a member reference of this.constructor
|
||||
* @param {ASTNode} node node to evaluate
|
||||
* @returns {boolean} true if it is a reference on this.constructor
|
||||
* @private
|
||||
*/
|
||||
function isThisConstructorReference(node) {
|
||||
return (
|
||||
node.object.type === "MemberExpression" &&
|
||||
node.object.property.name === "constructor" &&
|
||||
node.object.object.type === "ThisExpression"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if function parameter has a dangling underscore.
|
||||
* @param {ASTNode} node function node to evaluate
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkForDanglingUnderscoreInFunctionParameters(node) {
|
||||
if (!allowFunctionParams) {
|
||||
node.params.forEach(param => {
|
||||
const { type } = param;
|
||||
let nodeToCheck;
|
||||
|
||||
if (type === "RestElement") {
|
||||
nodeToCheck = param.argument;
|
||||
} else if (type === "AssignmentPattern") {
|
||||
nodeToCheck = param.left;
|
||||
} else {
|
||||
nodeToCheck = param;
|
||||
}
|
||||
|
||||
if (nodeToCheck.type === "Identifier") {
|
||||
const identifier = nodeToCheck.name;
|
||||
|
||||
if (
|
||||
hasDanglingUnderscore(identifier) &&
|
||||
!isAllowed(identifier)
|
||||
) {
|
||||
context.report({
|
||||
node: param,
|
||||
messageId: "unexpectedUnderscore",
|
||||
data: {
|
||||
identifier,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if function has a dangling underscore
|
||||
* @param {ASTNode} node node to evaluate
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkForDanglingUnderscoreInFunction(node) {
|
||||
if (node.type === "FunctionDeclaration" && node.id) {
|
||||
const identifier = node.id.name;
|
||||
|
||||
if (
|
||||
typeof identifier !== "undefined" &&
|
||||
hasDanglingUnderscore(identifier) &&
|
||||
!isAllowed(identifier)
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpectedUnderscore",
|
||||
data: {
|
||||
identifier,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
checkForDanglingUnderscoreInFunctionParameters(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if variable expression has a dangling underscore
|
||||
* @param {ASTNode} node node to evaluate
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkForDanglingUnderscoreInVariableExpression(node) {
|
||||
sourceCode.getDeclaredVariables(node).forEach(variable => {
|
||||
const definition = variable.defs.find(def => def.node === node);
|
||||
const identifierNode = definition.name;
|
||||
const identifier = identifierNode.name;
|
||||
let parent = identifierNode.parent;
|
||||
|
||||
while (
|
||||
![
|
||||
"VariableDeclarator",
|
||||
"ArrayPattern",
|
||||
"ObjectPattern",
|
||||
].includes(parent.type)
|
||||
) {
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
if (
|
||||
hasDanglingUnderscore(identifier) &&
|
||||
!isSpecialCaseIdentifierInVariableExpression(identifier) &&
|
||||
!isAllowed(identifier) &&
|
||||
!(
|
||||
allowInArrayDestructuring &&
|
||||
parent.type === "ArrayPattern"
|
||||
) &&
|
||||
!(
|
||||
allowInObjectDestructuring &&
|
||||
parent.type === "ObjectPattern"
|
||||
)
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpectedUnderscore",
|
||||
data: {
|
||||
identifier,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if member expression has a dangling underscore
|
||||
* @param {ASTNode} node node to evaluate
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkForDanglingUnderscoreInMemberExpression(node) {
|
||||
const identifier = node.property.name,
|
||||
isMemberOfThis = node.object.type === "ThisExpression",
|
||||
isMemberOfSuper = node.object.type === "Super",
|
||||
isMemberOfThisConstructor = isThisConstructorReference(node);
|
||||
|
||||
if (
|
||||
typeof identifier !== "undefined" &&
|
||||
hasDanglingUnderscore(identifier) &&
|
||||
!(isMemberOfThis && allowAfterThis) &&
|
||||
!(isMemberOfSuper && allowAfterSuper) &&
|
||||
!(isMemberOfThisConstructor && allowAfterThisConstructor) &&
|
||||
!isSpecialCaseIdentifierForMemberExpression(identifier) &&
|
||||
!isAllowed(identifier)
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpectedUnderscore",
|
||||
data: {
|
||||
identifier,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if method declaration or method property has a dangling underscore
|
||||
* @param {ASTNode} node node to evaluate
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkForDanglingUnderscoreInMethod(node) {
|
||||
const identifier = node.key.name;
|
||||
const isMethod =
|
||||
node.type === "MethodDefinition" ||
|
||||
(node.type === "Property" && node.method);
|
||||
|
||||
if (
|
||||
typeof identifier !== "undefined" &&
|
||||
enforceInMethodNames &&
|
||||
isMethod &&
|
||||
hasDanglingUnderscore(identifier) &&
|
||||
!isAllowed(identifier)
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpectedUnderscore",
|
||||
data: {
|
||||
identifier:
|
||||
node.key.type === "PrivateIdentifier"
|
||||
? `#${identifier}`
|
||||
: identifier,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a class field has a dangling underscore
|
||||
* @param {ASTNode} node node to evaluate
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkForDanglingUnderscoreInClassField(node) {
|
||||
const identifier = node.key.name;
|
||||
|
||||
if (
|
||||
typeof identifier !== "undefined" &&
|
||||
hasDanglingUnderscore(identifier) &&
|
||||
enforceInClassFields &&
|
||||
!isAllowed(identifier)
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpectedUnderscore",
|
||||
data: {
|
||||
identifier:
|
||||
node.key.type === "PrivateIdentifier"
|
||||
? `#${identifier}`
|
||||
: identifier,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public API
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
FunctionDeclaration: checkForDanglingUnderscoreInFunction,
|
||||
VariableDeclarator: checkForDanglingUnderscoreInVariableExpression,
|
||||
MemberExpression: checkForDanglingUnderscoreInMemberExpression,
|
||||
MethodDefinition: checkForDanglingUnderscoreInMethod,
|
||||
PropertyDefinition: checkForDanglingUnderscoreInClassField,
|
||||
Property: checkForDanglingUnderscoreInMethod,
|
||||
FunctionExpression: checkForDanglingUnderscoreInFunction,
|
||||
ArrowFunctionExpression: checkForDanglingUnderscoreInFunction,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* @fileoverview Rule to check multiple var declarations per line
|
||||
* @author Alberto Rodríguez
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: {
|
||||
message: "Formatting rules are being moved out of ESLint core.",
|
||||
url: "https://eslint.org/blog/2023/10/deprecating-formatting-rules/",
|
||||
deprecatedSince: "8.53.0",
|
||||
availableUntil: "11.0.0",
|
||||
replacedBy: [
|
||||
{
|
||||
message:
|
||||
"ESLint Stylistic now maintains deprecated stylistic core rules.",
|
||||
url: "https://eslint.style/guide/migration",
|
||||
plugin: {
|
||||
name: "@stylistic/eslint-plugin",
|
||||
url: "https://eslint.style",
|
||||
},
|
||||
rule: {
|
||||
name: "one-var-declaration-per-line",
|
||||
url: "https://eslint.style/rules/one-var-declaration-per-line",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description:
|
||||
"Require or disallow newlines around variable declarations",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/one-var-declaration-per-line",
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["always", "initializations"],
|
||||
},
|
||||
],
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
messages: {
|
||||
expectVarOnNewline:
|
||||
"Expected variable declaration to be on a new line.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const always = context.options[0] === "always";
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determine if provided keyword is a variant of for specifiers
|
||||
* @private
|
||||
* @param {string} keyword keyword to test
|
||||
* @returns {boolean} True if `keyword` is a variant of for specifier
|
||||
*/
|
||||
function isForTypeSpecifier(keyword) {
|
||||
return (
|
||||
keyword === "ForStatement" ||
|
||||
keyword === "ForInStatement" ||
|
||||
keyword === "ForOfStatement"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks newlines around variable declarations.
|
||||
* @private
|
||||
* @param {ASTNode} node `VariableDeclaration` node to test
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkForNewLine(node) {
|
||||
if (isForTypeSpecifier(node.parent.type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const declarations = node.declarations;
|
||||
let prev;
|
||||
|
||||
declarations.forEach(current => {
|
||||
if (prev && prev.loc.end.line === current.loc.start.line) {
|
||||
if (always || prev.init || current.init) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "expectVarOnNewline",
|
||||
loc: current.loc,
|
||||
fix: fixer => fixer.insertTextBefore(current, "\n"),
|
||||
});
|
||||
}
|
||||
}
|
||||
prev = current;
|
||||
});
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
VariableDeclaration: checkForNewLine,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
'use strict'
|
||||
|
||||
const pino = require('../..')
|
||||
const transport = pino.transport({
|
||||
targets: [{
|
||||
level: 'info',
|
||||
target: 'pino/file',
|
||||
options: {
|
||||
destination: process.argv[2]
|
||||
}
|
||||
}]
|
||||
})
|
||||
const logger = pino(transport)
|
||||
|
||||
const toWrite = 1000000
|
||||
transport.on('ready', run)
|
||||
|
||||
let total = 0
|
||||
|
||||
function run () {
|
||||
if (total++ === 8) {
|
||||
return
|
||||
}
|
||||
|
||||
for (let i = 0; i < toWrite; i++) {
|
||||
logger.info(`hello ${i}`)
|
||||
}
|
||||
transport.once('drain', run)
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* @fileoverview Rule to check use of chained assignment expressions
|
||||
* @author Stewart Rand
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
defaultOptions: [
|
||||
{
|
||||
ignoreNonDeclaration: false,
|
||||
},
|
||||
],
|
||||
|
||||
docs: {
|
||||
description: "Disallow use of chained assignment expressions",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/no-multi-assign",
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
ignoreNonDeclaration: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpectedChain: "Unexpected chained assignment.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const [{ ignoreNonDeclaration }] = context.options;
|
||||
const selectors = [
|
||||
"VariableDeclarator > AssignmentExpression.init",
|
||||
"PropertyDefinition > AssignmentExpression.value",
|
||||
];
|
||||
|
||||
if (!ignoreNonDeclaration) {
|
||||
selectors.push("AssignmentExpression > AssignmentExpression.right");
|
||||
}
|
||||
|
||||
return {
|
||||
[selectors](node) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpectedChain",
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
var util = require('util');
|
||||
var eyes = require('../lib/eyes');
|
||||
|
||||
eyes.inspect({
|
||||
number: 42,
|
||||
string: "John Galt",
|
||||
regexp: /[a-z]+/,
|
||||
array: [99, 168, 'x', {}],
|
||||
func: function () {},
|
||||
bool: false,
|
||||
nil: null,
|
||||
undef: undefined,
|
||||
object: {attr: []}
|
||||
}, "native types");
|
||||
|
||||
eyes.inspect({
|
||||
number: new(Number)(42),
|
||||
string: new(String)("John Galt"),
|
||||
regexp: new(RegExp)(/[a-z]+/),
|
||||
array: new(Array)(99, 168, 'x', {}),
|
||||
bool: new(Boolean)(false),
|
||||
object: new(Object)({attr: []}),
|
||||
date: new(Date)
|
||||
}, "wrapped types");
|
||||
|
||||
var obj = {};
|
||||
obj.that = { self: obj };
|
||||
obj.self = obj;
|
||||
|
||||
eyes.inspect(obj, "circular object");
|
||||
eyes.inspect({hello: 'moto'}, "small object");
|
||||
eyes.inspect({hello: new(Array)(6) }, "big object");
|
||||
eyes.inspect(["hello 'world'", 'hello "world"'], "quotes");
|
||||
eyes.inspect({
|
||||
recommendations: [{
|
||||
id: 'a7a6576c2c822c8e2bd81a27e41437d8',
|
||||
key: [ 'spree', 3.764316258020699 ],
|
||||
value: {
|
||||
_id: 'a7a6576c2c822c8e2bd81a27e41437d8',
|
||||
_rev: '1-2e2d2f7fd858c4a5984bcf809d22ed98',
|
||||
type: 'domain',
|
||||
domain: 'spree',
|
||||
weight: 3.764316258020699,
|
||||
product_id: 30
|
||||
}
|
||||
}]
|
||||
}, 'complex');
|
||||
|
||||
eyes.inspect([null], "null in array");
|
||||
|
||||
var inspect = eyes.inspector({ stream: null });
|
||||
|
||||
util.puts(inspect('something', "something"));
|
||||
util.puts(inspect("something else"));
|
||||
|
||||
util.puts(inspect(["no color"], null, { styles: false }));
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* NIST secp521r1 aka p521.
|
||||
* @module
|
||||
*/
|
||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||
import {} from "./abstract/hash-to-curve.js";
|
||||
import { p521_hasher, p521 as p521n } from "./nist.js";
|
||||
/** @deprecated use `import { p521 } from '@noble/curves/nist.js';` */
|
||||
export const p521 = p521n;
|
||||
/** @deprecated use `import { p521 } from '@noble/curves/nist.js';` */
|
||||
export const secp521r1 = p521n;
|
||||
/** @deprecated use `import { p521_hasher } from '@noble/curves/nist.js';` */
|
||||
export const hashToCurve = /* @__PURE__ */ (() => p521_hasher.hashToCurve)();
|
||||
/** @deprecated use `import { p521_hasher } from '@noble/curves/nist.js';` */
|
||||
export const encodeToCurve = /* @__PURE__ */ (() => p521_hasher.encodeToCurve)();
|
||||
//# sourceMappingURL=p521.js.map
|
||||
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* @fileoverview restrict values that can be used as Promise rejection reasons
|
||||
* @author Teddy Katz
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
defaultOptions: [
|
||||
{
|
||||
allowEmptyReject: false,
|
||||
},
|
||||
],
|
||||
|
||||
docs: {
|
||||
description:
|
||||
"Require using Error objects as Promise rejection reasons",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/prefer-promise-reject-errors",
|
||||
},
|
||||
|
||||
fixable: null,
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
allowEmptyReject: { type: "boolean" },
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
|
||||
messages: {
|
||||
rejectAnError:
|
||||
"Expected the Promise rejection reason to be an Error.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const [{ allowEmptyReject }] = context.options;
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Helpers
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks the argument of a reject() or Promise.reject() CallExpression, and reports it if it can't be an Error
|
||||
* @param {ASTNode} callExpression A CallExpression node which is used to reject a Promise
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkRejectCall(callExpression) {
|
||||
if (!callExpression.arguments.length && allowEmptyReject) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rejectionReason = callExpression.arguments[0];
|
||||
|
||||
if (
|
||||
!callExpression.arguments.length ||
|
||||
!astUtils.couldBeError(rejectionReason) ||
|
||||
(rejectionReason.type === "Identifier" &&
|
||||
rejectionReason.name === "undefined" &&
|
||||
sourceCode.isGlobalReference(rejectionReason))
|
||||
) {
|
||||
context.report({
|
||||
node: callExpression,
|
||||
messageId: "rejectAnError",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a function call is a Promise.reject() call
|
||||
* @param {ASTNode} node A CallExpression node
|
||||
* @returns {boolean} `true` if the call is a Promise.reject() call
|
||||
*/
|
||||
function isPromiseRejectCall(node) {
|
||||
return (
|
||||
astUtils.isSpecificMemberAccess(
|
||||
node.callee,
|
||||
"Promise",
|
||||
"reject",
|
||||
) &&
|
||||
sourceCode.isGlobalReference(
|
||||
astUtils.skipChainExpression(node.callee).object,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Public
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
// Check `Promise.reject(value)` calls.
|
||||
CallExpression(node) {
|
||||
if (isPromiseRejectCall(node)) {
|
||||
checkRejectCall(node);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Check for `new Promise((resolve, reject) => {})`, and check for reject() calls.
|
||||
* This function is run on "NewExpression:exit" instead of "NewExpression" to ensure that
|
||||
* the nodes in the expression already have the `parent` property.
|
||||
*/
|
||||
"NewExpression:exit"(node) {
|
||||
if (
|
||||
node.callee.type === "Identifier" &&
|
||||
node.callee.name === "Promise" &&
|
||||
sourceCode.isGlobalReference(node.callee) &&
|
||||
node.arguments.length &&
|
||||
astUtils.isFunction(node.arguments[0]) &&
|
||||
node.arguments[0].params.length > 1 &&
|
||||
node.arguments[0].params[1].type === "Identifier"
|
||||
) {
|
||||
sourceCode
|
||||
.getDeclaredVariables(node.arguments[0])
|
||||
|
||||
/*
|
||||
* Find the first variable that matches the second parameter's name.
|
||||
* If the first parameter has the same name as the second parameter, then the variable will actually
|
||||
* be "declared" when the first parameter is evaluated, but then it will be immediately overwritten
|
||||
* by the second parameter. It's not possible for an expression with the variable to be evaluated before
|
||||
* the variable is overwritten, because functions with duplicate parameters cannot have destructuring or
|
||||
* default assignments in their parameter lists. Therefore, it's not necessary to explicitly account for
|
||||
* this case.
|
||||
*/
|
||||
.find(
|
||||
variable =>
|
||||
variable.name ===
|
||||
node.arguments[0].params[1].name,
|
||||
)
|
||||
|
||||
// Get the references to that variable.
|
||||
.references // Only check the references that read the parameter's value.
|
||||
.filter(ref => ref.isRead())
|
||||
|
||||
// Only check the references that are used as the callee in a function call, e.g. `reject(foo)`.
|
||||
.filter(
|
||||
ref =>
|
||||
ref.identifier.parent.type ===
|
||||
"CallExpression" &&
|
||||
ref.identifier === ref.identifier.parent.callee,
|
||||
)
|
||||
|
||||
// Check the argument of the function call to determine whether it's an Error.
|
||||
.forEach(ref => checkRejectCall(ref.identifier.parent));
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
"use strict";
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Scope = void 0;
|
||||
const scopeManager = __importStar(require("@typescript-eslint/scope-manager"));
|
||||
var Scope;
|
||||
(function (Scope) {
|
||||
Scope.ScopeType = scopeManager.ScopeType;
|
||||
Scope.DefinitionType = scopeManager.DefinitionType;
|
||||
})(Scope || (exports.Scope = Scope = {}));
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
||||
'use strict'
|
||||
|
||||
const metadata = Symbol.for('pino.metadata')
|
||||
const split = require('split2')
|
||||
const { Duplex } = require('stream')
|
||||
const { parentPort, workerData } = require('worker_threads')
|
||||
|
||||
function createDeferred () {
|
||||
let resolve
|
||||
let reject
|
||||
const promise = new Promise((_resolve, _reject) => {
|
||||
resolve = _resolve
|
||||
reject = _reject
|
||||
})
|
||||
promise.resolve = resolve
|
||||
promise.reject = reject
|
||||
return promise
|
||||
}
|
||||
|
||||
module.exports = function build (fn, opts = {}) {
|
||||
const waitForConfig = opts.expectPinoConfig === true && workerData?.workerData?.pinoWillSendConfig === true
|
||||
const parseLines = opts.parse === 'lines'
|
||||
const parseLine = typeof opts.parseLine === 'function' ? opts.parseLine : JSON.parse
|
||||
const close = opts.close || defaultClose
|
||||
const stream = split(function (line) {
|
||||
let value
|
||||
|
||||
try {
|
||||
value = parseLine(line)
|
||||
} catch (error) {
|
||||
this.emit('unknown', line, error)
|
||||
return
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
this.emit('unknown', line, 'Null value ignored')
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof value !== 'object') {
|
||||
value = {
|
||||
data: value,
|
||||
time: Date.now()
|
||||
}
|
||||
}
|
||||
|
||||
if (stream[metadata]) {
|
||||
stream.lastTime = value.time
|
||||
stream.lastLevel = value.level
|
||||
stream.lastObj = value
|
||||
}
|
||||
|
||||
if (parseLines) {
|
||||
return line
|
||||
}
|
||||
|
||||
return value
|
||||
}, { autoDestroy: true })
|
||||
|
||||
stream._destroy = function (err, cb) {
|
||||
const promise = close(err, cb)
|
||||
if (promise && typeof promise.then === 'function') {
|
||||
promise.then(cb, cb)
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.expectPinoConfig === true && workerData?.workerData?.pinoWillSendConfig !== true) {
|
||||
setImmediate(() => {
|
||||
stream.emit('error', new Error('This transport is not compatible with the current version of pino. Please upgrade pino to the latest version.'))
|
||||
})
|
||||
}
|
||||
|
||||
if (opts.metadata !== false) {
|
||||
stream[metadata] = true
|
||||
stream.lastTime = 0
|
||||
stream.lastLevel = 0
|
||||
stream.lastObj = null
|
||||
}
|
||||
|
||||
if (waitForConfig) {
|
||||
let pinoConfig = {}
|
||||
const configReceived = createDeferred()
|
||||
parentPort.on('message', function handleMessage (message) {
|
||||
if (message.code === 'PINO_CONFIG') {
|
||||
pinoConfig = message.config
|
||||
configReceived.resolve()
|
||||
parentPort.off('message', handleMessage)
|
||||
}
|
||||
})
|
||||
|
||||
Object.defineProperties(stream, {
|
||||
levels: {
|
||||
get () { return pinoConfig.levels }
|
||||
},
|
||||
messageKey: {
|
||||
get () { return pinoConfig.messageKey }
|
||||
},
|
||||
errorKey: {
|
||||
get () { return pinoConfig.errorKey }
|
||||
}
|
||||
})
|
||||
|
||||
return configReceived.then(finish)
|
||||
}
|
||||
|
||||
return finish()
|
||||
|
||||
function finish () {
|
||||
let res = fn(stream)
|
||||
|
||||
if (res && typeof res.catch === 'function') {
|
||||
res.catch((err) => {
|
||||
stream.destroy(err)
|
||||
})
|
||||
|
||||
// set it to null to not retain a reference to the promise
|
||||
res = null
|
||||
} else if (opts.enablePipelining && res) {
|
||||
return Duplex.from({ writable: stream, readable: res })
|
||||
}
|
||||
|
||||
return stream
|
||||
}
|
||||
}
|
||||
|
||||
function defaultClose (err, cb) {
|
||||
process.nextTick(cb, err)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import type { LibDefinition } from '../variable';
|
||||
export declare const es2020_string: LibDefinition;
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"moduleResolutionKind.d.ts","sourceRoot":"","sources":["../../src/enums/moduleResolutionKind.ts"],"names":[],"mappings":"AACA,eAAO,IAAI,oBAAoB,EAAE,GAAG,CAAC"}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
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
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
|
||||
/// <reference no-default-lib="true"/>
|
||||
|
||||
/// <reference lib="es2017" />
|
||||
/// <reference lib="dom" />
|
||||
/// <reference lib="webworker.importscripts" />
|
||||
/// <reference lib="scripthost" />
|
||||
/// <reference lib="dom.iterable" />
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"jubjub.d.ts","sourceRoot":"","sources":["../src/jubjub.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,IAAI,OAAO,EAAE,MAAM,WAAW,CAAC;AAEtF,wEAAwE;AACxE,eAAO,MAAM,MAAM,EAAE,OAAO,OAAiB,CAAC;AAC9C,sFAAsF;AACtF,eAAO,MAAM,aAAa,EAAE,OAAO,oBAA2C,CAAC;AAC/E,kFAAkF;AAClF,eAAO,MAAM,SAAS,EAAE,OAAO,gBAAmC,CAAC"}
|
||||
@@ -0,0 +1,62 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const utils_1 = require("@typescript-eslint/utils");
|
||||
const ast_utils_1 = require("@typescript-eslint/utils/ast-utils");
|
||||
const util_1 = require("../util");
|
||||
exports.default = (0, util_1.createRule)({
|
||||
name: 'no-array-constructor',
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
description: 'Disallow generic `Array` constructors',
|
||||
extendsBaseRule: true,
|
||||
recommended: 'recommended',
|
||||
},
|
||||
fixable: 'code',
|
||||
messages: {
|
||||
useLiteral: 'The array literal notation [] is preferable.',
|
||||
},
|
||||
schema: [],
|
||||
},
|
||||
defaultOptions: [],
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
function getArgumentsText(node) {
|
||||
const lastToken = sourceCode.getLastToken(node);
|
||||
if (lastToken == null || !(0, ast_utils_1.isClosingParenToken)(lastToken)) {
|
||||
return '';
|
||||
}
|
||||
let firstToken = node.callee;
|
||||
do {
|
||||
firstToken = sourceCode.getTokenAfter(firstToken);
|
||||
if (!firstToken || firstToken === lastToken) {
|
||||
return '';
|
||||
}
|
||||
} while (!(0, ast_utils_1.isOpeningParenToken)(firstToken));
|
||||
return sourceCode.text.slice(firstToken.range[1], lastToken.range[0]);
|
||||
}
|
||||
/**
|
||||
* Disallow construction of dense arrays using the Array constructor
|
||||
* @param node node to evaluate
|
||||
*/
|
||||
function check(node) {
|
||||
if (node.arguments.length !== 1 &&
|
||||
node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
||||
node.callee.name === 'Array' &&
|
||||
!node.typeArguments) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'useLiteral',
|
||||
fix(fixer) {
|
||||
const argsText = getArgumentsText(node);
|
||||
return fixer.replaceText(node, `[${argsText}]`);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
return {
|
||||
CallExpression: check,
|
||||
NewExpression: check,
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
import { isCI } from 'std-env';
|
||||
|
||||
const isNode = typeof process < "u" && typeof process.stdout < "u" && !process.versions?.deno && !globalThis.window;
|
||||
const isDeno = typeof process < "u" && typeof process.stdout < "u" && process.versions?.deno !== void 0;
|
||||
const isWindows = (isNode || isDeno) && process.platform === "win32";
|
||||
const isTTY = (isNode || isDeno) && process.stdout?.isTTY && !isCI;
|
||||
|
||||
export { isWindows as a, isTTY as i };
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"montgomery.d.ts","sourceRoot":"","sources":["../../src/abstract/montgomery.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM/C,KAAK,GAAG,GAAG,MAAM,GAAG,UAAU,CAAC;AAE/B,MAAM,MAAM,SAAS,GAAG;IACtB,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACxB,iBAAiB,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,UAAU,CAAC;IACrD,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAClC,WAAW,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,MAAM,KAAK,UAAU,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,KAAK,UAAU,CAAC;IAChD,cAAc,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,UAAU,CAAC;IAC5C,eAAe,EAAE,CAAC,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,KAAK,UAAU,CAAC;IAClE,YAAY,EAAE,CAAC,SAAS,EAAE,GAAG,KAAK,UAAU,CAAC;IAC7C,KAAK,EAAE;QACL,eAAe,EAAE,MAAM,UAAU,CAAC;QAClC,wCAAwC;QACxC,gBAAgB,EAAE,MAAM,UAAU,CAAC;KACpC,CAAC;IACF,OAAO,EAAE,UAAU,CAAC;IACpB,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,UAAU,KAAK;QAAE,SAAS,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE,UAAU,CAAA;KAAE,CAAC;CACjF,CAAC;AACF,MAAM,MAAM,OAAO,GAAG,cAAc,CAAC;AAUrC,wBAAgB,UAAU,CAAC,QAAQ,EAAE,SAAS,GAAG,cAAc,CAyI9D"}
|
||||
@@ -0,0 +1,824 @@
|
||||
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]')
|
||||
})
|
||||
Reference in New Issue
Block a user