From 26e84f2ffd8594089e62e3cf4a366f1d4dbb4379 Mon Sep 17 00:00:00 2001 From: Colin Casey Date: Fri, 23 Feb 2024 13:04:47 -0400 Subject: [PATCH 1/3] Fix allow listed `files` for what to include in npm package When #296 was merged, the list of files to include in `package.json` was not updated. This PR fixes that. The `.npmignore` file is also removed because I find it causes some unexpected behaviors like ignoring the contents of `.gitignore`. It's a bit of extra work to maintain the allow listed `files` but it's generally safer. --- .npmignore | 5 ----- package.json | 4 +++- 2 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 .npmignore diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 460a9558..00000000 --- a/.npmignore +++ /dev/null @@ -1,5 +0,0 @@ -docker-compose.yml -Dockerfile -.nyc_output -coverage -package-lock.json diff --git a/package.json b/package.json index 74be9da4..366cb0a5 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,9 @@ "types": "./dist/cookie/index.d.ts", "files": [ "dist/*.js", - "dist/*.d.ts" + "dist/*.d.ts", + "dist/cookie/*.js", + "dist/cookie/*.d.ts" ], "scripts": { "build": "npm run clean && tsc", From 02f27cbaa5030359709db85c0e3ab4936c6fc0cc Mon Sep 17 00:00:00 2001 From: Colin Casey Date: Thu, 29 Feb 2024 10:20:12 -0400 Subject: [PATCH 2/3] Fix allow listed `files` for what to include in npm package When #296 was merged, the list of files to include in `package.json` was not updated. This PR fixes that. The `.npmignore` file is also removed because I find it causes some unexpected behaviors like ignoring the contents of `.gitignore`. It's a bit of extra work to maintain the allow listed `files` but it's generally safer. --- package.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 366cb0a5..9744d294 100644 --- a/package.json +++ b/package.json @@ -79,10 +79,9 @@ "main": "./dist/cookie/index.js", "types": "./dist/cookie/index.d.ts", "files": [ - "dist/*.js", - "dist/*.d.ts", - "dist/cookie/*.js", - "dist/cookie/*.d.ts" + "dist/**/*.js", + "dist/**/*.d.ts", + "!__tests__" ], "scripts": { "build": "npm run clean && tsc", From 1b6db592c8a669d8aa2195871272bbf595572974 Mon Sep 17 00:00:00 2001 From: Will Harney <62956339+wjhsf@users.noreply.github.com> Date: Thu, 29 Feb 2024 10:05:17 -0500 Subject: [PATCH 3/3] Handle unlikely edge case in unimportant util (#366) * handle unlikely edge case * actually fix bug correctly, this time * turns out Array#toString is weird --- lib/__tests__/util.spec.ts | 29 +++++++++++++++++++++++++++++ lib/utils.ts | 37 ++++++++++++++++++++++++++++++++----- 2 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 lib/__tests__/util.spec.ts diff --git a/lib/__tests__/util.spec.ts b/lib/__tests__/util.spec.ts new file mode 100644 index 00000000..afcda6c2 --- /dev/null +++ b/lib/__tests__/util.spec.ts @@ -0,0 +1,29 @@ +import { safeToString } from '../utils' + +describe('safeToString', () => { + const recursiveArray: unknown[] = [1] + recursiveArray.push([[recursiveArray], 2, [[recursiveArray]]], 3) + const testCases = [ + [undefined, 'undefined'], + [null, 'null'], + [true, 'true'], + ['string', 'string'], + [123, '123'], + [321n, '321'], + [{ object: 'yes' }, '[object Object]'], + [(a: number, b: number) => a + b, '(a, b) => a + b'], + [Symbol('safeToString'), 'Symbol(safeToString)'], + [Object.create(null), '[object Object]'], + // eslint-disable-next-line no-sparse-arrays + [[1, 'hello', , undefined, , true, null], '1,hello,,,,true,'], + [ + [Object.create(null), Symbol('safeToString')], + '[object Object],Symbol(safeToString)', + ], + [recursiveArray, '1,,2,,3'], + ] + + it.each(testCases)('works on %s', (input, output) => { + expect(safeToString(input)).toBe(String(output)) + }) +}) diff --git a/lib/utils.ts b/lib/utils.ts index 0a5bb07d..83628f5c 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -13,17 +13,44 @@ export interface ErrorCallback { export const objectToString = (obj: unknown) => Object.prototype.toString.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. +/** + * Converts an array to string, safely handling symbols, null prototype objects, and recursive arrays. + */ +const safeArrayToString = ( + arr: unknown[], + seenArrays: WeakSet, +): string => { + // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString#description + if (typeof arr.join !== 'function') return objectToString(arr) + seenArrays.add(arr) + const mapped = arr.map((val) => + val === null || val === undefined || seenArrays.has(val) + ? '' + : safeToStringImpl(val, seenArrays), + ) + return mapped.join() +} + +const safeToStringImpl = ( + val: unknown, + seenArrays?: WeakSet, +): string => { + // Using .toString() fails for null/undefined and implicit conversion (val + "") fails for symbols + // and objects with null prototype if (val === undefined || val === null || typeof val.toString === 'function') { - return String(val) + return Array.isArray(val) + ? // Arrays have a weird custom toString that we need to replicate + safeArrayToString(val, seenArrays ?? new WeakSet()) + : String(val) } else { + // This case should just be objects with null prototype, so we can just use Object#toString return objectToString(val) } } +/** Safely converts any value to string, using the value's own `toString` when available. */ +export const safeToString = (val: unknown) => safeToStringImpl(val) + /** Utility object for promise/callback interop. */ export interface PromiseCallback { promise: Promise