Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/cyan-chefs-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@httpx/plain-object": minor
---

Deprecate isStaticBuiltInClass, it will be removed in next major version
23 changes: 23 additions & 0 deletions .changeset/weak-onions-walk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
"@httpx/plain-object": minor
---

Exclude static built-in classes (Atomics, JSON, Math) from being passed as argument.

This change should not produce any runtime change, but will now produce a typescript error
if one of these classes is passed as argument.

Note that it's totally an edge case that probably never happens in real world code.

```typescript
import { isPlainObject } from '@httpx/plain-object';

// ⚠️ Now produce a typescript error
isPlainObject(Math);
// TS2345: Argument of type Math | JSON | Atomics is not assignable to parameter of type never
// Type Math is not assignable to type never

// Same for Atomics and JSON static built-in classes
isPlainObject(Atomics);
isPlainObject(JSON);
```
4 changes: 2 additions & 2 deletions devtools/vitest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@
"playwright": "1.56.0",
"prettier": "3.6.2",
"typescript": "5.9.3",
"vite": "7.1.9",
"vite": "7.1.10",
"vite-tsconfig-paths": "5.1.4",
"vitest-browser-react": "1.0.1"
},
"devDependencies": {
"@types/node": "24.7.2",
"@types/react": "19.2.2",
"@types/react-dom": "19.2.1",
"@types/react-dom": "19.2.2",
"is-ci": "4.1.0",
"react": "19.2.0",
"react-dom": "19.2.0",
Expand Down
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"@belgattitude/eslint-config-bases": "8.3.0",
"@tailwindcss/postcss": "4.1.14",
"@types/react": "19.2.2",
"@types/react-dom": "19.2.1",
"@types/react-dom": "19.2.2",
"eslint": "8.57.1",
"eslint-config-next": "15.5.5",
"postcss": "8.5.6",
Expand Down
42 changes: 23 additions & 19 deletions docs/src/pages/plain-object.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,6 @@ const runInNewContext = await import('node:vm').then(
);
isPlainObject(runInNewContext('({})')); // ✅

// ✅👇 Static built-in classes are treated as plain objects
// check for `isStaticBuiltInClass` to exclude if needed

isPlainObject(Math); // ✅
isPlainObject(JSON); // ✅
isPlainObject(Atomics); // ✅

// ❌👇 False

class Test { };
Expand All @@ -101,8 +94,19 @@ isPlainObject(Promise.resolve({})); // ❌
isPlainObject(Object.create({})); // ❌
isPlainObject(new (class Cls {})); // ❌
isPlainObject(globalThis); // ❌,

// ⚠️👇 Note that at runtime static built-in classes will return true
// but the typing guards will prevent you from passing them.
// This is a trade-off to keep the isPlainObject as performant as possible
// while preventing accidental usage of static built-in classes (edge case).

isPlainObject(Math); // ⚠️️ Typecheck error
isPlainObject(JSON); // ⚠️ Typecheck error
isPlainObject(Atomics); // ⚠️ Typecheck error

```


### assertPlainObject

```typescript
Expand Down Expand Up @@ -245,22 +249,23 @@ Bundle size is tracked by a [size-limit configuration](https://github.com/belgat
| `import { isPlainObject } from '@httpx/plain-object` | ~ 80B |
| `import { assertPlainObject } from '@httpx/plain-object` | ~ 134B |
| `Both isPlainObject and assertPlainObject` | ~ 142B |
| `import { isStaticBuiltInClass } from '@httpx/plain-object` | ~ 37B |

> For CJS usage (not recommended) track the size on [bundlephobia](https://bundlephobia.com/package/@httpx/plain-object@latest).

## Compatibility

| Level | CI | Description |
|------------|----|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Node | ✅ | CI for 18.x, 20.x & 22.x. |
| Browser | ✅ | Tested with latest chrome (vitest/playwright) |
| Browsers | ✅ | [> 96%](https://browserslist.dev/?q=ZGVmYXVsdHMsIGNocm9tZSA%2BPSA5NixmaXJlZm94ID49IDkwLGVkZ2UgPj0gMTksc2FmYXJpID49IDEyLGlvcyA%2BPSAxMixvcGVyYSA%2BPSA3Nw%3D%3D) on 07/2024. Mins to [Chrome 96+, Firefox 90+, Edge 19+, iOS 12+, Safari 12+, Opera 77+](https://github.com/belgattitude/httpx/blob/main/packages/plain-object/.browserslistrc) |
| Edge | ✅ | Ensured on CI with [@vercel/edge-runtime](https://github.com/vercel/edge-runtime). |
| Cloudflare | ✅ | Ensured with @cloudflare/vitest-pool-workers (see [wrangler.toml](https://github.com/belgattitude/httpx/blob/main/devtools/vitest/wrangler.toml) |
| Typescript | ✅ | TS 5.0 + / [are-the-type-wrong](https://github.com/arethetypeswrong/arethetypeswrong.github.io) checks on CI. |
| ES2022 | ✅ | Dist files checked with [es-check](https://github.com/yowainwright/es-check) |
| Performance| ✅ | Monitored with [codspeed.io](https://codspeed.io/belgattitude/httpx) |

| Level | CI | Description |
|--------------|----|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Node | ✅ | CI for 20.x, 22.x & 24.x. |
| Browser | ✅ | Tested with latest chrome (vitest/playwright) |
| Browserslist | ✅ | [> 95%](https://browserslist.dev/?q=ZGVmYXVsdHMsIGNocm9tZSA%2BPSA5NiwgZmlyZWZveCA%2BPSAxMDUsIGVkZ2UgPj0gMTEzLCBzYWZhcmkgPj0gMTUsIGlvcyA%2BPSAxNSwgb3BlcmEgPj0gMTAzLCBub3QgZGVhZA%3D%3D) on 01/2025. [defaults, chrome >= 96, firefox >= 105, edge >= 113, safari >= 15, ios >= 15, opera >= 103, not dead](https://github.com/belgattitude/httpx/blob/main/packages/plain-object/.browserslistrc) |
| Edge | ✅ | Ensured on CI with [@vercel/edge-runtime](https://github.com/vercel/edge-runtime). |
| Cloudflare | ✅ | Ensured with @cloudflare/vitest-pool-workers (see [wrangler.toml](https://github.com/belgattitude/httpx/blob/main/devtools/vitest/wrangler.toml) |
| Typescript | ✅ | TS 5.0 + / [are-the-type-wrong](https://github.com/arethetypeswrong/arethetypeswrong.github.io) checks on CI. |
| ES2022 | ✅ | Dist files checked with [es-check](https://github.com/yowainwright/es-check) |
| Performance | ✅ | Monitored with [codspeed.io](https://codspeed.io/belgattitude/httpx) |


> For _older_ browsers: most frontend frameworks can transpile the library (ie: [nextjs](https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages)...)

Expand Down Expand Up @@ -291,7 +296,6 @@ Notable differences:

Since v2, it diverges from `is-plain-obj` by

- [x] Static built-in classes are considered as plain objects (use [isStaticBuiltInClass](#isstaticbuiltinclass) to exclude).
- [x] `[Symbol.iterator]` is considered as a valid property for plain objects.
- [x] `[Symbol.toStringTag]` is considered as a valid property for plain objects.`

14 changes: 7 additions & 7 deletions examples/nextjs-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@
"@httpx/xcache": "workspace:^",
"axios": "1.12.2",
"clsx": "2.1.1",
"hono": "4.9.10",
"ky": "1.11.0",
"next": "15.5.4",
"hono": "4.9.12",
"ky": "1.12.0",
"next": "15.5.5",
"pino": "10.0.0",
"primereact": "10.9.7",
"react": "19.2.0",
Expand All @@ -45,24 +45,24 @@
"zod": "4.1.12"
},
"devDependencies": {
"@belgattitude/eslint-config-bases": "8.0.1",
"@belgattitude/eslint-config-bases": "8.3.0",
"@tailwindcss/postcss": "4.1.14",
"@types/node": "24.7.2",
"@types/react": "19.2.2",
"@types/react-dom": "19.2.1",
"@types/react-dom": "19.2.2",
"@vitejs/plugin-react-swc": "4.1.0",
"@vitest/coverage-v8": "3.2.4",
"@vitest/ui": "3.2.4",
"cross-env": "10.1.0",
"eslint": "8.57.1",
"eslint-config-next": "15.5.4",
"eslint-config-next": "15.5.5",
"happy-dom": "20.0.0",
"postcss": "8.5.6",
"prettier": "3.6.2",
"rimraf": "6.0.1",
"tailwindcss": "4.1.14",
"typescript": "5.9.3",
"vite": "7.1.9",
"vite": "7.1.10",
"vite-plugin-svgr": "4.5.0",
"vite-tsconfig-paths": "5.1.4",
"vitest": "3.2.4",
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,15 @@
"@commitlint/cli": "20.1.0",
"@commitlint/config-conventional": "20.0.0",
"@types/shell-quote": "1.7.5",
"@typescript-eslint/eslint-plugin": "8.46.0",
"@typescript-eslint/parser": "8.46.0",
"@typescript-eslint/eslint-plugin": "8.46.1",
"@typescript-eslint/parser": "8.46.1",
"cross-env": "10.1.0",
"eslint": "8.57.1",
"gh-pages": "6.3.0",
"husky": "9.1.7",
"is-ci": "4.1.0",
"lint-staged": "16.2.4",
"npm-check-updates": "19.0.0",
"npm-check-updates": "19.1.0",
"npm-run-all2": "8.0.4",
"nx": "21.6.4",
"pkg-pr-new": "0.0.60",
Expand Down
Loading
Loading