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 validate function. #275

Merged
merged 6 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
25 changes: 16 additions & 9 deletions lib/cookie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import * as validators from './validators'
import { version } from './version'
import { permuteDomain } from './permuteDomain'
import { getCustomInspectSymbol } from './utilHelper'
import { ErrorCallback, safeToString } from './utils'

// From RFC6265 S4.1.1
// note that it excludes \x3B ";"
Expand Down Expand Up @@ -357,7 +358,7 @@ function parseDate(str: string | undefined | null): Date | undefined {
}

function formatDate(date: Date) {
validators.validate(validators.isDate(date), date)
validators.validate(validators.isDate(date), safeToString(date))
return date.toUTCString()
}

Expand Down Expand Up @@ -699,7 +700,7 @@ function parse(
* @returns boolean
*/
function isSecurePrefixConditionMet(cookie: Cookie) {
validators.validate(validators.isObject(cookie), cookie)
validators.validate(validators.isObject(cookie), safeToString(cookie))
const startsWithSecurePrefix =
typeof cookie.key === 'string' && cookie.key.startsWith('__Secure-')
return !startsWithSecurePrefix || cookie.secure
Expand Down Expand Up @@ -786,8 +787,8 @@ function fromJSON(str: string | SerializedCookie | null | undefined) {
*/

function cookieCompare(a: Cookie, b: Cookie) {
validators.validate(validators.isObject(a), a)
validators.validate(validators.isObject(b), b)
validators.validate(validators.isObject(a), safeToString(a))
validators.validate(validators.isObject(b), safeToString(b))
let cmp = 0

// descending for length: b CMP a
Expand Down Expand Up @@ -977,8 +978,11 @@ export class Cookie {
) {
return false
}
// @ts-ignore
if (this.maxAge != null && this.maxAge <= 0) {
if (
this.maxAge != null &&
this.maxAge !== 'Infinity' &&
(this.maxAge === '-Infinity' || this.maxAge <= 0)
) {
return false // "Max-Age=" non-zero-digit *DIGIT
}
if (this.path != null && !PATH_VALUE.test(this.path)) {
Expand Down Expand Up @@ -1343,7 +1347,11 @@ export class CookieJar {
const promiseCallback = createPromiseCallback<Cookie>(arguments)
const cb = promiseCallback.callback

validators.validate(validators.isNonEmptyString(url), callback, options)
validators.validate(
validators.isNonEmptyString(url),
callback,
safeToString(options),
)
let err

if (validators.isFunction(url)) {
Expand Down Expand Up @@ -1629,7 +1637,7 @@ export class CookieJar {
if (typeof options === 'function' || options === undefined) {
options = defaultGetCookieOptions
}
validators.validate(validators.isObject(options), cb, options)
validators.validate(validators.isObject(options), cb, safeToString(options))
validators.validate(validators.isFunction(cb), cb)

const host = canonicalDomain(context.hostname)
Expand Down Expand Up @@ -2247,4 +2255,3 @@ export type Callback<T> = (
error: Error | undefined,
result: T | undefined,
) => void
export type ErrorCallback = (error: Error) => void
19 changes: 19 additions & 0 deletions lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/** Signature for a callback function that expects an error to be passed. */
export type ErrorCallback = (error: Error, result?: never) => void

/** Unbound `Object.prototype.toString`. */
const unboundToString = Object.prototype.toString

/** Wrapped `Object.prototype.toString`, so that you don't need to remember to use `.call()`. */
export const objectToString = (obj: unknown) => unboundToString.call(obj)

/** Safely converts any value to string, using the value's own `toString` when available. */
export const safeToString = (val: unknown) => {
// Ideally, we'd just use String() for everything, but it breaks if `toString` is missing (mostly
// values with no prototype), so we have to use Object#toString as a fallback.
if (val === undefined || val === null || typeof val.toString === 'function') {
return String(val)
} else {
return objectToString(val)
}
}
69 changes: 45 additions & 24 deletions lib/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,58 +25,79 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

************************************************************************************ */
'use strict'

const toString = Object.prototype.toString
import { ErrorCallback, objectToString, safeToString } from './utils'

/* Validation functions copied from check-types package - https://www.npmjs.com/package/check-types */
export function isFunction(data: unknown): boolean {

/** Determines whether the argument is a function. */
export function isFunction(data: unknown): data is Function {
return typeof data === 'function'
}

/** Determines whether the argument is a non-empty string. */
export function isNonEmptyString(data: unknown): boolean {
return isString(data) && data !== ''
}

/** Determines whether the argument is a *valid* Date. */
export function isDate(data: unknown): boolean {
if (data instanceof Date) {
return isInteger(data.getTime())
}
return false
return isInstanceStrict(data, Date) && isInteger(data.getTime())
}

/** Determines whether the argument is the empty string. */
export function isEmptyString(data: unknown): boolean {
return data === '' || (data instanceof String && data.toString() === '')
}

/** Determines whether the argument is a string. */
export function isString(data: unknown): boolean {
return typeof data === 'string' || data instanceof String
}

/** Determines whether the string representation of the argument is "[object Object]". */
export function isObject(data: unknown): boolean {
return toString.call(data) === '[object Object]'
return objectToString(data) === '[object Object]'
}

/** Determines whether the first argument is an instance of the second. */
export function isInstanceStrict<T extends Function>(
data: unknown,
Constructor: T,
): data is T['prototype'] {
try {
return data instanceof Constructor
} catch {
return false
}
}

/** Determines whether the argument is an integer. */
export function isInteger(data: unknown): boolean {
return typeof data === 'number' && data % 1 === 0
}
/* End validation functions */

export function validate(bool: boolean, cb?: unknown, options?: unknown): void {
if (!isFunction(cb)) {
options = cb
cb = null
}
if (!isObject(options)) options = { Error: 'Failed Check' }
if (!bool) {
if (typeof cb === 'function') {
// @ts-ignore
cb(new ParameterError(options))
} else {
// @ts-ignore
throw new ParameterError(options)
}
}
/* -- End validation functions -- */

/**
* When the first argument is false, an error is created with the given message. If a callback is
* provided, the error is passed to the callback, otherwise the error is thrown.
*/
export function validate(
bool: boolean,
cbOrMessage?: ErrorCallback | string,
message?: string,
): void {
if (bool) return // Validation passes
const cb = isFunction(cbOrMessage) ? cbOrMessage : null
let options = isFunction(cbOrMessage) ? message : cbOrMessage
// The default message prior to v5 was '[object Object]' due to a bug, and the message is kept
// for backwards compatibility.
if (!isObject(options)) options = '[object Object]'

const err = new ParameterError(safeToString(options))
if (cb) cb(err)
else throw err
}

export class ParameterError extends Error {}
13 changes: 7 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,14 @@
},
"devDependencies": {
"@types/jest": "^29",
"@types/node": "^16.18.23",
"@types/psl": "^1",
"@types/punycode": "^2",
"@types/url-parse": "^1.4.8",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
"async": "2.6.4",
"eslint": "^8.36.0",
"@typescript-eslint/parser": "^5.57.0",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"genversion": "^3.1.1",
Expand Down