Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clean up usage of null/undefined #380

Merged
merged 17 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions lib/__tests__/domainMatch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ describe('domainMatch', () => {
['example.com', 'example.com.', false], // RFC6265 S4.1.2.3

// nulls and undefineds
[null, 'example.com', null],
['example.com', null, null],
[null, null, null],
[undefined, undefined, null],
[null, 'example.com', undefined],
['example.com', null, undefined],
[null, null, undefined],
[undefined, undefined, undefined],

// suffix matching:
['www.example.com', 'example.com', true], // substr AND suffix
Expand Down
14 changes: 5 additions & 9 deletions lib/__tests__/parse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,22 +379,22 @@ describe('Cookie.parse', () => {
// empty string
{
input: ``,
output: null,
output: undefined,
},
// missing string
{
input: undefined,
output: null,
output: undefined,
},
// some string object
{
input: new String(''),
output: null,
output: undefined,
},
// some empty string object
{
input: new String(),
output: null,
output: undefined,
},
])('Cookie.parse("$input")', (testCase) => {
// Repeating the character in the input makes the jest output obnoxiously long, so instead we
Expand All @@ -406,11 +406,7 @@ describe('Cookie.parse', () => {

const value = input === undefined ? undefined : input.valueOf()
const cookie = Cookie.parse(value as string, parseOptions)
if (output !== undefined) {
expect(cookie).toEqual(output && expect.objectContaining(output))
} else {
expect(cookie).toBe(output)
}
expect(cookie).toEqual(output && expect.objectContaining(output))

if (cookie && typeof assertValidateReturns === 'boolean') {
expect(cookie.validate()).toBe(assertValidateReturns)
Expand Down
9 changes: 5 additions & 4 deletions lib/cookie/canonicalDomain.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as punycode from 'punycode/punycode.js'
import { toASCII } from 'punycode/punycode.js'
import { IP_V6_REGEX_OBJECT } from './constants'
import type { Nullable } from '../utils'

// S5.1.2 Canonicalized Host Names
export function canonicalDomain(str: string | null): string | null {
export function canonicalDomain(str: Nullable<string>): string | undefined {
if (str == null) {
return null
return undefined
}
let _str = str.trim().replace(/^\./, '') // S4.1.2.3 & S5.2.3: ignore leading .

Expand All @@ -15,7 +16,7 @@ export function canonicalDomain(str: string | null): string | null {
// convert to IDN if any non-ASCII characters
// eslint-disable-next-line no-control-regex
if (/[^\u0001-\u007f]/.test(_str)) {
_str = punycode.toASCII(_str)
_str = toASCII(_str)
}

return _str.toLowerCase()
Expand Down
30 changes: 18 additions & 12 deletions lib/cookie/cookie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
*/

// This file was too big before we added max-lines, and it's ongoing work to reduce its size.
/* eslint max-lines: [1, 750] */
/* eslint max-lines: [1, 800] */

import { getPublicSuffix } from '../getPublicSuffix'
import * as validators from '../validators'
Expand Down Expand Up @@ -115,12 +115,16 @@ type ParseCookieOptions = {
loose?: boolean | undefined
}

function parse(
str: string,
options?: ParseCookieOptions,
): Cookie | undefined | null {
/**
* Parses a string into a Cookie object.
* @param str the Set-Cookie header or a Cookie string to parse. Note: when parsing a Cookie header it must be split by ';' before each Cookie string can be parsed.
* @param options configures strict or loose mode for cookie parsing
* @returns `Cookie` object for valid string inputs, `undefined` for invalid string inputs,
* or `null` for non-string inputs or empty string
*/
function parse(str: string, options?: ParseCookieOptions): Cookie | undefined {
if (validators.isEmptyString(str) || !validators.isString(str)) {
return null
return undefined
}

str = str.trim()
Expand Down Expand Up @@ -276,6 +280,10 @@ function parse(
return c
}

// TBD: `null` is valid JSON, but `undefined` is not. Options:
// 1. Change this to return `undefined` - weird because it's not JSON
// 2. Keep this as `null` - weird because it's a violation of our new convention
// 3. Change *everything* to return `null` - a lot of work, maybe other edge cases that we can't change?
wjhsf marked this conversation as resolved.
Show resolved Hide resolved
function fromJSON(str: unknown): Cookie | null {
if (!str || validators.isEmptyString(str)) {
return null
Expand Down Expand Up @@ -528,6 +536,7 @@ export class Cookie {
return obj
}

// TBD: This is a wrapper for `fromJSON`, return type depends on decision there
clone(): Cookie | null {
return fromJSON(this.toJSON())
}
Expand Down Expand Up @@ -693,15 +702,12 @@ export class Cookie {
}

// Mostly S5.1.2 and S5.2.3:
canonicalizedDomain(): string | null {
if (this.domain == null) {
return null
}
canonicalizedDomain(): string | undefined {
return canonicalDomain(this.domain)
}

cdomain(): string | null {
return this.canonicalizedDomain()
cdomain(): string | undefined {
return canonicalDomain(this.domain)
}

static parse = parse
Expand Down
34 changes: 16 additions & 18 deletions lib/cookie/cookieJar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import { pathMatch } from '../pathMatch'
import { Cookie } from './cookie'
import {
Callback,
createPromiseCallback,
ErrorCallback,
Nullable,
createPromiseCallback,
inOperator,
safeToString,
} from '../utils'
Expand Down Expand Up @@ -84,13 +85,13 @@ function getCookieContext(url: unknown): URL | urlParse<string> {
}

type SameSiteLevel = keyof (typeof Cookie)['sameSiteLevel']
function checkSameSiteContext(value: string): SameSiteLevel | null {
function checkSameSiteContext(value: string): SameSiteLevel | undefined {
validators.validate(validators.isNonEmptyString(value), value)
const context = String(value).toLowerCase()
if (context === 'none' || context === 'lax' || context === 'strict') {
return context
} else {
return null
return undefined
}
}

Expand Down Expand Up @@ -161,7 +162,7 @@ export class CookieJar {
readonly prefixSecurity: string

constructor(
store?: Store | null | undefined,
store?: Nullable<Store>,
options?: CreateCookieJarOptions | boolean,
) {
if (typeof options === 'boolean') {
Expand Down Expand Up @@ -267,7 +268,7 @@ export class CookieJar {
return promiseCallback.resolve(undefined)
}

const host = canonicalDomain(context.hostname)
const host = canonicalDomain(context.hostname) ?? null
const loose = options?.loose || this.enableLooseMode

let sameSiteContext = null
Expand Down Expand Up @@ -440,16 +441,16 @@ export class CookieJar {
}
}

function withCookie(
err: Error | null,
oldCookie: Cookie | undefined | null,
const withCookie: Callback<Cookie | undefined> = function withCookie(
err,
oldCookie,
): void {
if (err) {
cb(err)
return
}

const next = function (err: Error | null): void {
const next: ErrorCallback = function (err) {
if (err) {
cb(err)
} else if (typeof cookie === 'string') {
Expand Down Expand Up @@ -491,6 +492,7 @@ export class CookieJar {
}
}

// TODO: Refactor to avoid using a callback
store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie)
return promiseCallback.promise
}
Expand Down Expand Up @@ -748,18 +750,13 @@ export class CookieJar {
callback,
)

const next: Callback<Cookie[]> = function (
err: Error | null,
cookies: Cookie[] | undefined,
) {
const next: Callback<Cookie[] | undefined> = function (err, cookies) {
if (err) {
promiseCallback.callback(err)
} else if (cookies === undefined) {
promiseCallback.callback(null, undefined)
} else {
promiseCallback.callback(
null,
cookies.map((c) => {
cookies?.map((c) => {
return c.toString()
}),
)
Expand Down Expand Up @@ -879,7 +876,7 @@ export class CookieJar {

cookies = cookies.slice() // do not modify the original

const putNext = (err: Error | null): void => {
const putNext: ErrorCallback = (err) => {
if (err) {
return callback(err, undefined)
}
Expand Down Expand Up @@ -995,7 +992,8 @@ export class CookieJar {
let completedCount = 0
const removeErrors: Error[] = []

function removeCookieCb(removeErr: Error | null): void {
// TODO: Refactor to avoid using callback
const removeCookieCb: ErrorCallback = function removeCookieCb(removeErr) {
if (removeErr) {
removeErrors.push(removeErr)
}
Expand Down
4 changes: 3 additions & 1 deletion lib/cookie/defaultPath.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// RFC6265 S5.1.4 Paths and Path-Match

import type { Nullable } from '../utils'

/*
* "The user agent MUST use an algorithm equivalent to the following algorithm
* to compute the default-path of a cookie:"
*
* Assumption: the path (and not query part or absolute uri) is passed in.
*/
export function defaultPath(path?: string | null): string {
export function defaultPath(path?: Nullable<string>): string {
// "2. If the uri-path is empty or if the first character of the uri-path is not
// a %x2F ("/") character, output %x2F ("/") and skip the remaining steps.
if (!path || path.slice(0, 1) !== '/') {
Expand Down
15 changes: 8 additions & 7 deletions lib/cookie/domainMatch.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Nullable } from '../utils'
import { canonicalDomain } from './canonicalDomain'

// Dumped from ip-regex@4.0.0, with the following changes:
Expand All @@ -9,16 +10,16 @@ const IP_REGEX_LOWERCASE =

// S5.1.3 Domain Matching
export function domainMatch(
str?: string | null,
domStr?: string | null,
str?: Nullable<string>,
domStr?: Nullable<string>,
canonicalize?: boolean,
): boolean | null {
): boolean | undefined {
if (str == null || domStr == null) {
return null
return undefined
}

let _str: string | null
let _domStr: string | null
let _str: Nullable<string>
let _domStr: Nullable<string>

if (canonicalize !== false) {
_str = canonicalDomain(str)
Expand All @@ -29,7 +30,7 @@ export function domainMatch(
}

if (_str == null || _domStr == null) {
return null
return undefined
}

/*
Expand Down
5 changes: 4 additions & 1 deletion lib/cookie/parseDate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// date-time parsing constants (RFC6265 S5.1.1)

import type { Nullable } from '../utils'

// eslint-disable-next-line no-control-regex
const DATE_DELIM = /[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]/

Expand Down Expand Up @@ -123,7 +126,7 @@ function parseMonth(token: string): number | null {
/*
* RFC6265 S5.1.1 date parser (see RFC for full grammar)
*/
export function parseDate(str: string | undefined | null): Date | undefined {
export function parseDate(str: Nullable<string>): Date | undefined {
if (!str) {
return undefined
}
Expand Down
5 changes: 3 additions & 2 deletions lib/getPublicSuffix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const defaultGetPublicSuffixOptions: GetPublicSuffixOptions = {
export function getPublicSuffix(
domain: string,
options: GetPublicSuffixOptions = {},
): string | null {
): string | undefined {
options = { ...defaultGetPublicSuffixOptions, ...options }
const domainParts = domain.split('.')
const topLevelDomain = domainParts[domainParts.length - 1]
Expand Down Expand Up @@ -84,8 +84,9 @@ export function getPublicSuffix(
)
}

return getDomain(domain, {
const publicSuffix = getDomain(domain, {
allowIcannDomains: true,
allowPrivateDomains: true,
})
if (publicSuffix) return publicSuffix
}
25 changes: 15 additions & 10 deletions lib/memstore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ import type { Cookie } from './cookie/cookie'
import { pathMatch } from './pathMatch'
import { permuteDomain } from './permuteDomain'
import { Store } from './store'
import { Callback, createPromiseCallback, ErrorCallback } from './utils'
import {
Callback,
createPromiseCallback,
ErrorCallback,
Nullable,
} from './utils'

export type MemoryCookieStoreIndex = {
[domain: string]: {
Expand All @@ -54,20 +59,20 @@ export class MemoryCookieStore extends Store {
}

override findCookie(
domain: string | null,
path: string | null,
key: string | undefined,
domain: Nullable<string>,
path: Nullable<string>,
key: Nullable<string>,
): Promise<Cookie | undefined>
override findCookie(
domain: string | null,
path: string | null,
key: string | undefined,
domain: Nullable<string>,
path: Nullable<string>,
key: Nullable<string>,
callback: Callback<Cookie | undefined>,
): void
override findCookie(
domain: string | null,
path: string | null,
key: string | undefined,
domain: Nullable<string>,
path: Nullable<string>,
key: Nullable<string>,
callback?: Callback<Cookie | undefined>,
): unknown {
const promiseCallback = createPromiseCallback(callback)
Expand Down
Loading
Loading