From a5f10fff999854f9636573270f8dbaf13d9bb435 Mon Sep 17 00:00:00 2001 From: Mathieu Acthernoene Date: Thu, 9 Jan 2025 17:28:27 +0100 Subject: [PATCH] Initial implementation (#2) --- .github/CODE_OF_CONDUCT.md | 76 +++ .github/ISSUE_TEMPLATE/bug_report.yml | 69 ++ .github/ISSUE_TEMPLATE/feature_request.yml | 34 + .github/PULL_REQUEST_TEMPLATE.md | 27 + .github/stale.yml | 32 + .github/workflows/build.yml | 33 + README.md | 131 +++- __tests__/cache.ts | 58 ++ __tests__/cx.tsx | 71 ++ __tests__/index.ts | 5 - __tests__/preprocess.ts | 60 ++ __tests__/sheet.ts | 71 ++ __tests__/utils.ts | 53 ++ package.json | 21 +- pnpm-lock.yaml | 618 +++++++++++------- src/hyphenateName.ts | 12 + src/index.ts | 32 +- src/normalizeValue.ts | 100 +++ src/preprocess.ts | 178 +++++ src/sheet.ts | 341 ++++++++++ src/types.ts | 145 ++++ src/types/react-native__normalize-colors.d.ts | 4 + src/utils.ts | 16 + testSetup.ts | 1 + tsconfig.json | 4 +- vite.config.mts | 9 +- 26 files changed, 1932 insertions(+), 269 deletions(-) create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/stale.yml create mode 100644 .github/workflows/build.yml create mode 100644 __tests__/cache.ts create mode 100644 __tests__/cx.tsx delete mode 100644 __tests__/index.ts create mode 100644 __tests__/preprocess.ts create mode 100644 __tests__/sheet.ts create mode 100644 __tests__/utils.ts create mode 100644 src/hyphenateName.ts create mode 100644 src/normalizeValue.ts create mode 100644 src/preprocess.ts create mode 100644 src/sheet.ts create mode 100644 src/types.ts create mode 100644 src/types/react-native__normalize-colors.d.ts create mode 100644 src/utils.ts create mode 100644 testSetup.ts diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..07f60e1 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at mathieu.acthernoene@swan.io. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..14ecd51 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,69 @@ +name: 🐛 Bug report +description: Report a reproducible bug or regression in this library. +labels: [bug] +assignees: + - zoontek +body: + - type: markdown + attributes: + value: | + # Bug report + + 👋 Hi! + + **🚨 Please read the following carefully before opening a new issue 🚨** + *(Your issue may be closed if it doesn't provide the required pieces of information)* + + Before submitting a new issue, please: + + - Test using the latest release of the library, as maybe your bug has been already fixed. + - Check for possible duplicate issues, with possible answers. + + Is this still needed? Fill the form 👇 + - type: textarea + id: summary + attributes: + label: Bug summary + description: | + Provide a clear and concise description of what the bug is. + If needed, you can also provide other samples: error messages / stack traces, screenshots, videos, etc. + validations: + required: true + - type: input + id: library-version + attributes: + label: Library version + description: What version of the library are you using? + placeholder: "x.x.x" + validations: + required: true + - type: textarea + id: browser-info + attributes: + label: Environment info + description: Go to [WhatIsMyBrowser](https://www.whatismybrowser.com) and copy / paste your browser & OS names + versions. + render: shell + validations: + required: true + - type: textarea + id: steps-to-reproduce + attributes: + label: Steps to reproduce + description: | + - You must provide a clear list of steps and code to reproduce the problem. + - Keep the code reproducing the bug as simple as possible, with the minimum amount of code required to reproduce the issue. See https://stackoverflow.com/help/mcve. + - Either re-create the bug using the repository's example app or link to a GitHub repository with code that reproduces the bug. + - Explain the steps we need to take to reproduce the issue: + value: | + 1. … + 2. … + validations: + required: true + - type: textarea + id: reproducible-sample-code + attributes: + label: Reproducible sample code + description: Please add minimal runnable repro as explained above so that the bug can be tested in isolation. + render: ts + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..a8d9f75 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,34 @@ +name: 💡 Feature request +description: Submit your idea for a change in the codebase. +labels: [feature request] +assignees: + - zoontek +body: + - type: markdown + attributes: + value: | + # Feature request + + This issue should serve for you to present or pitch an idea to the maintainers - but remember that it would be better if you were to submit a PR instead 🤗 + - type: textarea + id: why-is-this-needed + attributes: + label: Why it is needed? + description: Please tell us a bit more of why you want this feature to be added, what's its origin. + validations: + required: true + - type: textarea + id: possible-implementation + attributes: + label: Possible implementation + description: It really helps if you could describe from a technical POV how this new feature would work, which code it rely on, etc. + validations: + required: false + - type: textarea + id: code-sample + attributes: + label: Code sample + description: Please show how the new code could work, if doable. + render: ts + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..97ef465 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,27 @@ + + +# Summary + + + +## Test Plan + + + +### What's required for testing (prerequisites)? + +### What are the steps to reproduce (after prerequisites)? + +## Checklist + + + +- [ ] I added the documentation in `README.md` +- [ ] I wrote / updated some tests (`__tests__/`) diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..427567c --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,32 @@ +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 60 +# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. +daysUntilClose: 7 +# Issues or Pull Requests. +exemptLabels: + - pinned + - security + - discussion +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: false +# Set to true to ignore issues with an assignee (defaults to false) +exemptAssignees: false +# Label to use when marking as stale +staleLabel: stale +# Comment to post when marking as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. You may also mark this issue as a "discussion" and I + will leave this open. +# Comment to post when closing a stale Issue or Pull Request. +closeComment: > + Closing this issue after a prolonged period of inactivity. Fell free to reopen + this issue, if this still affecting you. +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: + 30 + # Limit to only `issues` or `pulls` +only: issues diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..7b00f10 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,33 @@ +name: Build + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 60 + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: lts/* + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Typecheck + run: pnpm typecheck + + - name: Run tests + run: pnpm test:ci + + - name: Build + run: pnpm build diff --git a/README.md b/README.md index 8b55b6e..b85d6d7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,130 @@ -# css +# @swan-io/css -An atomic CSS-in-JS library inspired by React Native `StyleSheet` +[![mit licence](https://img.shields.io/dub/l/vibe-d.svg?style=for-the-badge)](https://github.com/swan-io/css/blob/main/LICENSE) +[![npm version](https://img.shields.io/npm/v/@swan-io/css?style=for-the-badge)](https://www.npmjs.org/package/@swan-io/css) +[![bundlephobia](https://img.shields.io/bundlephobia/minzip/@swan-io/css?label=size&style=for-the-badge)](https://bundlephobia.com/result?p=@swan-io/css) + +A lightweight and performant atomic CSS-in-JS library. + +## Installation + +```bash +$ yarn add @swan-io/css +# --- or --- +$ npm install --save @swan-io/css +``` + +## Quickstart + +```tsx +import { css, cx } from "@swan-io/css"; + +const sheet = css.make({ + box: { + backgroundColor: "blue", + padding: 16, + }, + large: { + padding: 24, + }, + text: { + color: "white", + fontSize: 20, + ":hover": { + color: "gray", + }, + }, +}); + +const Component = ({ large }: { large: boolean }) => ( +
+ Hello world +
+); +``` + +## API + +### css.make + +Create a new sheet object and inject the associated styles. + +```tsx +const sheet = css.make({ + box: { + backgroundColor: "hotpink", + paddingHorizontal: 16, + + // supports :hover, :focus and :active + ":hover": { color: "red" }, + ":focus": { color: "green" }, + ":active": { color: "blue" }, + }, +}); + +console.log(sheet.box); // a string list of generated classes +``` + +> [!TIP] +> Styles prefixed with `$` will be inserted as non-atomic CSS-in-JS, which is particularly useful for resetting the styles of an HTML element. + +```tsx +const sheet = css.make({ + // generates a single class, inserted before the rest + $reset: { + margin: 0, + padding: 0, + }, + // generates multiple classes + input: { + color: "grey", + display: "flex", + }, +}); +``` + +### css.keyframes + +Inject a keyframes rule and generate a unique name for it. + +```tsx +const sheet = css.make({ + box: { + animationDuration: "300ms", + animationName: css.keyframes({ + "0%": { opacity: 0 }, + "100%": { opacity: 1 }, + }), + }, +}); +``` + +### cx + +Concatenate the generated classes from left to right, with subsequent styles overwriting the property values of earlier ones. + +```tsx +const sheet = css.make({ + box: { + display: "flex", + color: "red", + }, + inline: { + display: "inline-flex", + }, +}); + +// with inline={false}, applied style will be display: flex; color: red; +// with inline={true}, applied style will be display: inline-flex; color: red; +const Component = ({ inline }: { inline: boolean }) => ( +
+); +``` + +## Links + +- ⚖️ [**License**](./LICENSE) + +## 🙌 Acknowledgements + +- [react-native-web](https://github.com/necolas/react-native-web) by [@necolas](https://github.com/necolas) diff --git a/__tests__/cache.ts b/__tests__/cache.ts new file mode 100644 index 0000000..063de7c --- /dev/null +++ b/__tests__/cache.ts @@ -0,0 +1,58 @@ +import { expect, test } from "vitest"; +import { css } from "../src"; +import { getSheets } from "./utils"; + +test("cache don't insert identical property + value pairs", () => { + css.make({ + foo: { + color: "red", + ":hover": { color: "green" }, + ":focus": { color: "blue" }, + ":active": { color: "rebeccapurple" }, + }, + bar: { + color: "red", + ":hover": { color: "green" }, + ":focus": { color: "blue" }, + ":active": { color: "rebeccapurple" }, + }, + baz: { + // insert identical colors (they are normalized) + color: "color: rgb(255, 0, 0)", + ":hover": { color: "rgb(0, 128, 0)" }, + ":focus": { color: "rgb(0, 0, 255)" }, + ":active": { color: "rgb(102, 51, 153)" }, + }, + // insert identical resets + $qux: { margin: 0, padding: 0 }, + $quux: { margin: 0, padding: 0 }, + }); + + const { reset, atomic, hover, focus, active } = getSheets(); + + expect(reset.rules).toHaveLength(1); + expect(atomic.rules).toHaveLength(1); + expect(hover.rules).toHaveLength(1); + expect(focus.rules).toHaveLength(1); + expect(active.rules).toHaveLength(1); + + expect(reset.rules.join("\n")).toMatchInlineSnapshot( + `".r-1je9j89 { margin: 0px; padding: 0px; }"`, + ); + + expect(atomic.rules.join("\n")).toMatchInlineSnapshot( + `".x-1tkyx38 { color: rgb(255, 0, 0); }"`, + ); + + expect(hover.rules.join("\n")).toMatchInlineSnapshot( + `".h-1w6oenc:hover { color: rgb(0, 128, 0); }"`, + ); + + expect(focus.rules.join("\n")).toMatchInlineSnapshot( + `".f-rc30ek:focus-visible { color: rgb(0, 0, 255); }"`, + ); + + expect(active.rules.join("\n")).toMatchInlineSnapshot( + `".a-1ngrkn9:active { color: rgb(102, 51, 153); }"`, + ); +}); diff --git a/__tests__/cx.tsx b/__tests__/cx.tsx new file mode 100644 index 0000000..887f42d --- /dev/null +++ b/__tests__/cx.tsx @@ -0,0 +1,71 @@ +import * as React from "react"; +import { expect, test } from "vitest"; +import { render } from "vitest-browser-react"; +import { css, cx } from "../src"; + +test("cx concatenates atomic classes", () => { + const sheet = css.make({ + foo: { + backgroundColor: "red", + color: "blue", + }, + bar: { + color: "green", + }, + }); + + expect(sheet.foo).toMatchInlineSnapshot(`"x-7ogb2w x-rc30ek"`); + expect(sheet.bar).toMatchInlineSnapshot(`"x-1w6oenc"`); + + expect(cx(sheet.foo, sheet.bar)).toMatchInlineSnapshot( + `"x-7ogb2w x-1w6oenc"`, + ); +}); + +test("cx allows one reset style", async () => { + const sheet = css.make({ + $foo: { + backgroundColor: "red", + color: "blue", + }, + $bar: { + color: "green", + }, + baz: { + color: "rebeccapurple", + ":hover": { + color: "gray", + }, + }, + }); + + expect(sheet.$foo).toMatchInlineSnapshot(`"r-1vjaegw"`); + expect(sheet.$bar).toMatchInlineSnapshot(`"r-1w6oenc"`); + expect(sheet.baz).toMatchInlineSnapshot(`"x-1ngrkn9 h-1g5wjl1"`); + + const className = cx(sheet.$foo, sheet.$bar, sheet.baz); + expect(className).toMatchInlineSnapshot(`"r-1vjaegw x-1ngrkn9 h-1g5wjl1"`); + + const screen = render(
); + const div = await screen.getByTestId("div"); + const style = window.getComputedStyle(div.element()); + + expect(style.backgroundColor).toBe("rgb(255, 0, 0)"); + expect(style.color).toBe("rgb(102, 51, 153)"); +}); + +test("cx allows external classes", async () => { + const sheet = css.make({ + foo: { lineClamp: 1 }, + bar: { lineClamp: 2 }, + }); + + const className = cx(sheet.foo, false && sheet.bar, true && ["foo"]); + expect(className).toMatchInlineSnapshot(`"foo x-1acs8jx"`); + + const screen = render(); + const div = await screen.getByTestId("div"); + const style = window.getComputedStyle(div.element()); + + expect(style.webkitLineClamp).toBe("1"); +}); diff --git a/__tests__/index.ts b/__tests__/index.ts deleted file mode 100644 index d3b08e2..0000000 --- a/__tests__/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { expect, test } from "vitest"; - -test("adds 1 + 2 to equal 3", () => { - expect(1 + 2).toBe(3); -}); diff --git a/__tests__/preprocess.ts b/__tests__/preprocess.ts new file mode 100644 index 0000000..cae9d7a --- /dev/null +++ b/__tests__/preprocess.ts @@ -0,0 +1,60 @@ +import { expect, test } from "vitest"; +import { css } from "../src"; +import { getSheets } from "./utils"; + +test("longhands properties are expanded", () => { + const sheet = css.make({ + box: { + backgroundPosition: "top", + borderColor: "red", + borderRadius: 4, + borderStyle: "dotted", + borderWidth: 2, + flex: 1, + inset: 10, + margin: 10, + padding: 10, + }, + }); + + expect(sheet.box).toMatchInlineSnapshot( + `"x-1ajv4b8 x-1gj51ll x-19tkz8q x-1ogglhr x-yx774m x-1h88r7n x-1bdvcgl x-misotl x-qirwg6 x-1ppxwh4 x-1o74rcf x-1vqxhxy x-9xwnqj x-29ghif x-1h9xl6o x-s861vp x-1a0ix0g x-fh1clx x-1t1j3t x-tklrwr x-o0zst5 x-zs882z x-4esd6u x-jwnrsh x-8wdww5 x-1f13l4q x-lx60ht x-cl7tym x-jm3jfd x-jlcvdv x-6rz8sj x-h002ba"`, + ); + + const { atomic } = getSheets(); + + expect(atomic.rules.join("\n")).toMatchInlineSnapshot(` + ".x-1ajv4b8 { background-position-y: 0%; } + .x-1gj51ll { border-top-color: rgb(255, 0, 0); } + .x-19tkz8q { border-right-color: rgb(255, 0, 0); } + .x-1ogglhr { border-bottom-color: rgb(255, 0, 0); } + .x-yx774m { border-left-color: rgb(255, 0, 0); } + .x-1h88r7n { border-top-left-radius: 4px; } + .x-1bdvcgl { border-top-right-radius: 4px; } + .x-misotl { border-bottom-right-radius: 4px; } + .x-qirwg6 { border-bottom-left-radius: 4px; } + .x-1ppxwh4 { border-top-style: dotted; } + .x-1o74rcf { border-right-style: dotted; } + .x-1vqxhxy { border-bottom-style: dotted; } + .x-9xwnqj { border-left-style: dotted; } + .x-29ghif { border-top-width: 2px; } + .x-1h9xl6o { border-right-width: 2px; } + .x-s861vp { border-bottom-width: 2px; } + .x-1a0ix0g { border-left-width: 2px; } + .x-fh1clx { flex-grow: 1; } + .x-1t1j3t { flex-shrink: 1; } + .x-tklrwr { flex-basis: 0%; } + .x-o0zst5 { top: 10px; } + .x-zs882z { right: 10px; } + .x-4esd6u { bottom: 10px; } + .x-jwnrsh { left: 10px; } + .x-8wdww5 { margin-top: 10px; } + .x-1f13l4q { margin-right: 10px; } + .x-lx60ht { margin-bottom: 10px; } + .x-cl7tym { margin-left: 10px; } + .x-jm3jfd { padding-top: 10px; } + .x-jlcvdv { padding-right: 10px; } + .x-6rz8sj { padding-bottom: 10px; } + .x-h002ba { padding-left: 10px; }" + `); +}); diff --git a/__tests__/sheet.ts b/__tests__/sheet.ts new file mode 100644 index 0000000..e697ba5 --- /dev/null +++ b/__tests__/sheet.ts @@ -0,0 +1,71 @@ +import { expect, test } from "vitest"; +import { css } from "../src"; +import { getSheets } from "./utils"; + +test("sheet create and use different subsheets", async () => { + css.make({ + $reset: { + display: "flex", + width: 100, + height: 100, + }, + box: { + animationDuration: "200ms", + backgroundColor: "white", + color: "red", + + animationName: css.keyframes({ + from: { opacity: 0 }, + to: { opacity: 1 }, + }), + + ":hover": { color: "green" }, + ":focus": { color: "blue" }, + ":active": { color: "rebeccapurple" }, + }, + }); + + const { main, keyframes, reset, atomic, hover, focus, active } = getSheets(); + + expect(keyframes.media).toBe("all"); + expect(reset.media).toBe("all"); + expect(atomic.media).toBe("all"); + expect(hover.media).toBe("(hover: hover)"); + expect(focus.media).toBe("all"); + expect(active.media).toBe("all"); + + expect(main.rules).toHaveLength(6); + expect(keyframes.rules).toHaveLength(1); + expect(reset.rules).toHaveLength(1); + expect(atomic.rules).toHaveLength(4); + expect(hover.rules).toHaveLength(1); + expect(focus.rules).toHaveLength(1); + expect(active.rules).toHaveLength(1); + + expect(keyframes.rules.join("\n")).toMatchInlineSnapshot( + `"@keyframes k-1mf61dn { 0% { opacity: 0; } 100% { opacity: 1; } }"`, + ); + + expect(reset.rules.join("\n")).toMatchInlineSnapshot( + `".r-1wfww0e { display: flex; width: 100px; height: 100px; }"`, + ); + + expect(atomic.rules.join("\n")).toMatchInlineSnapshot(` + ".x-brsnw3 { animation-duration: 200ms; } + .x-15y6h4w { background-color: rgb(255, 255, 255); } + .x-1tkyx38 { color: rgb(255, 0, 0); } + .x-j8yzpo { animation-name: k-1mf61dn; }" + `); + + expect(hover.rules.join("\n")).toMatchInlineSnapshot( + `".h-1w6oenc:hover { color: rgb(0, 128, 0); }"`, + ); + + expect(focus.rules.join("\n")).toMatchInlineSnapshot( + `".f-rc30ek:focus-visible { color: rgb(0, 0, 255); }"`, + ); + + expect(active.rules.join("\n")).toMatchInlineSnapshot( + `".a-1ngrkn9:active { color: rgb(102, 51, 153); }"`, + ); +}); diff --git a/__tests__/utils.ts b/__tests__/utils.ts new file mode 100644 index 0000000..6b92eb5 --- /dev/null +++ b/__tests__/utils.ts @@ -0,0 +1,53 @@ +const isMediaRule = (rule: CSSRule | undefined) => + rule != null && rule instanceof CSSMediaRule; + +const convertSheet = (sheet: CSSStyleSheet | CSSMediaRule) => ({ + media: sheet.media.toString(), + rules: [...sheet.cssRules].map((rule) => rule.cssText.replace(/\s+/g, " ")), +}); + +export const getSheets = () => { + const main = document.querySelector( + `style[id="swan-stylesheet"]`, + )?.sheet; + + if (main == null) { + throw new Error("Cannot get main CSSStyleSheet"); + } + + const keyframes = main.cssRules[0]; + const reset = main.cssRules[1]; + const atomic = main.cssRules[2]; + const hover = main.cssRules[3]; + const focus = main.cssRules[4]; + const active = main.cssRules[5]; + + if (!isMediaRule(keyframes)) { + throw new Error("Cannot get keyframes CSSMediaRule"); + } + if (!isMediaRule(reset)) { + throw new Error("Cannot get reset CSSMediaRule"); + } + if (!isMediaRule(atomic)) { + throw new Error("Cannot get atomic CSSMediaRule"); + } + if (!isMediaRule(hover)) { + throw new Error("Cannot get hover CSSMediaRule"); + } + if (!isMediaRule(focus)) { + throw new Error("Cannot get focus CSSMediaRule"); + } + if (!isMediaRule(active)) { + throw new Error("Cannot get active CSSMediaRule"); + } + + return { + main: convertSheet(main), + keyframes: convertSheet(keyframes), + reset: convertSheet(reset), + atomic: convertSheet(atomic), + hover: convertSheet(hover), + focus: convertSheet(focus), + active: convertSheet(active), + }; +}; diff --git a/package.json b/package.json index 3925fdc..7092fd6 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,11 @@ "type": "git", "url": "https://github.com/swan-io/bot-detector.git" }, + "packageManager": "pnpm@9.15.3", + "engines": { + "node": ">=22.13.0", + "pnpm": "^9.15.3" + }, "source": "src/index.ts", "main": "dist/index.js", "module": "dist/index.mjs", @@ -32,10 +37,12 @@ "clean": "rm -rf dist", "test:ci": "playwright install --with-deps --only-shell chromium && CI=true vitest --run", "test": "vitest --run", + "test:webkit": "BROWSER=webkit vitest --run", + "test:firefox": "BROWSER=firefox vitest --run", "test:watch": "vitest --watch", "typecheck": "tsc --noEmit", "build": "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly", - "prepack": "pnpm test && pnpm build" + "prepack": "pnpm typecheck && pnpm test && pnpm build" }, "prettier": { "plugins": [ @@ -43,15 +50,23 @@ ] }, "dependencies": { - "@emotion/hash": "^0.9.2" + "@emotion/hash": "^0.9.2", + "@react-native/normalize-colors": "^0.76.5", + "csstype": "^3.1.3", + "postcss-value-parser": "^4.2.0" }, "devDependencies": { + "@types/react": "^19.0.3", + "@types/react-dom": "^19.0.2", "@vitest/browser": "^2.1.8", "playwright": "^1.49.1", "prettier": "^3.4.2", "prettier-plugin-organize-imports": "^4.1.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", "tsup": "^8.3.5", "typescript": "^5.7.2", - "vitest": "^2.1.8" + "vitest": "^2.1.8", + "vitest-browser-react": "^0.0.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da335d1..8a2c9b7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,10 +11,25 @@ importers: '@emotion/hash': specifier: ^0.9.2 version: 0.9.2 + '@react-native/normalize-colors': + specifier: ^0.76.5 + version: 0.76.5 + csstype: + specifier: ^3.1.3 + version: 3.1.3 + postcss-value-parser: + specifier: ^4.2.0 + version: 4.2.0 devDependencies: + '@types/react': + specifier: ^19.0.3 + version: 19.0.3 + '@types/react-dom': + specifier: ^19.0.2 + version: 19.0.2(@types/react@19.0.3) '@vitest/browser': specifier: ^2.1.8 - version: 2.1.8(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.2))(vitest@2.1.8) + version: 2.1.8(@types/node@22.10.5)(playwright@1.49.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.5))(vitest@2.1.8) playwright: specifier: ^1.49.1 version: 1.49.1 @@ -24,6 +39,12 @@ importers: prettier-plugin-organize-imports: specifier: ^4.1.0 version: 4.1.0(prettier@3.4.2)(typescript@5.7.2) + react: + specifier: ^19.0.0 + version: 19.0.0 + react-dom: + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) tsup: specifier: ^8.3.5 version: 8.3.5(postcss@8.4.49)(typescript@5.7.2) @@ -32,7 +53,10 @@ importers: version: 5.7.2 vitest: specifier: ^2.1.8 - version: 2.1.8(@types/node@22.10.2)(@vitest/browser@2.1.8)(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2)) + version: 2.1.8(@types/node@22.10.5)(@vitest/browser@2.1.8)(msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2)) + vitest-browser-react: + specifier: ^0.0.4 + version: 0.0.4(@types/react-dom@19.0.2(@types/react@19.0.3))(@types/react@19.0.3)(@vitest/browser@2.1.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(vitest@2.1.8) packages: @@ -66,8 +90,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.24.0': - resolution: {integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==} + '@esbuild/aix-ppc64@0.24.2': + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -78,8 +102,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.24.0': - resolution: {integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==} + '@esbuild/android-arm64@0.24.2': + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -90,8 +114,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.24.0': - resolution: {integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==} + '@esbuild/android-arm@0.24.2': + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -102,8 +126,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.24.0': - resolution: {integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==} + '@esbuild/android-x64@0.24.2': + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -114,8 +138,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.24.0': - resolution: {integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==} + '@esbuild/darwin-arm64@0.24.2': + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -126,8 +150,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.24.0': - resolution: {integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==} + '@esbuild/darwin-x64@0.24.2': + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -138,8 +162,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.24.0': - resolution: {integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==} + '@esbuild/freebsd-arm64@0.24.2': + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -150,8 +174,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.24.0': - resolution: {integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==} + '@esbuild/freebsd-x64@0.24.2': + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -162,8 +186,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.24.0': - resolution: {integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==} + '@esbuild/linux-arm64@0.24.2': + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -174,8 +198,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.24.0': - resolution: {integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==} + '@esbuild/linux-arm@0.24.2': + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -186,8 +210,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.24.0': - resolution: {integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==} + '@esbuild/linux-ia32@0.24.2': + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -198,8 +222,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.24.0': - resolution: {integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==} + '@esbuild/linux-loong64@0.24.2': + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -210,8 +234,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.24.0': - resolution: {integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==} + '@esbuild/linux-mips64el@0.24.2': + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -222,8 +246,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.24.0': - resolution: {integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==} + '@esbuild/linux-ppc64@0.24.2': + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -234,8 +258,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.24.0': - resolution: {integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==} + '@esbuild/linux-riscv64@0.24.2': + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -246,8 +270,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.24.0': - resolution: {integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==} + '@esbuild/linux-s390x@0.24.2': + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -258,26 +282,32 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.24.0': - resolution: {integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==} + '@esbuild/linux-x64@0.24.2': + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.24.2': + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.24.0': - resolution: {integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==} + '@esbuild/netbsd-x64@0.24.2': + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.24.0': - resolution: {integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==} + '@esbuild/openbsd-arm64@0.24.2': + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -288,8 +318,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.24.0': - resolution: {integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==} + '@esbuild/openbsd-x64@0.24.2': + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -300,8 +330,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.24.0': - resolution: {integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==} + '@esbuild/sunos-x64@0.24.2': + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -312,8 +342,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.24.0': - resolution: {integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==} + '@esbuild/win32-arm64@0.24.2': + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -324,8 +354,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.24.0': - resolution: {integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==} + '@esbuild/win32-ia32@0.24.2': + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -336,28 +366,28 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.24.0': - resolution: {integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==} + '@esbuild/win32-x64@0.24.2': + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@inquirer/confirm@5.1.0': - resolution: {integrity: sha512-osaBbIMEqVFjTX5exoqPXs6PilWQdjaLhGtMDXMXg/yxkHXNq43GlxGyTA35lK2HpzUgDN+Cjh/2AmqCN0QJpw==} + '@inquirer/confirm@5.1.1': + resolution: {integrity: sha512-vVLSbGci+IKQvDOtzpPTCOiEJCNidHcAq9JYVoWTW0svb5FiwSLotkM+JXNXejfjnzVYV9n0DTBythl9+XgTxg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' - '@inquirer/core@10.1.1': - resolution: {integrity: sha512-rmZVXy9iZvO3ZStEe/ayuuwIJ23LSF13aPMlLMTQARX6lGUBDHGV8UB5i9MRrfy0+mZwt5/9bdy8llszSD3NQA==} + '@inquirer/core@10.1.2': + resolution: {integrity: sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==} engines: {node: '>=18'} - '@inquirer/figures@1.0.8': - resolution: {integrity: sha512-tKd+jsmhq21AP1LhexC0pPwsCxEhGgAkg28byjJAd+xhmIs8LUX8JbUc3vBf3PhLxWiB5EvyBE5X7JSPAqMAqg==} + '@inquirer/figures@1.0.9': + resolution: {integrity: sha512-BXvGj0ehzrngHTPTDqUoDT3NXL8U0RxUk2zJm2A66RhCEIWdtU1v6GuUqNAgArW4PQ9CinqIWyHdQgdwOj06zQ==} engines: {node: '>=18'} - '@inquirer/type@3.0.1': - resolution: {integrity: sha512-+ksJMIy92sOAiAccGpcKZUc3bYO07cADnscIxHBknEm3uNts3movSmBofc1908BNy5edKscxYeAdaX1NXkHS6A==} + '@inquirer/type@3.0.2': + resolution: {integrity: sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -384,8 +414,8 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@mswjs/interceptors@0.37.3': - resolution: {integrity: sha512-USvgCL/uOGFtVa6SVyRrC8kIAedzRohxIXN5LISlg5C5vLZCn7dgMFVSNhSF9cuBEFrm/O2spDWEZeMnw4ZXYg==} + '@mswjs/interceptors@0.37.5': + resolution: {integrity: sha512-AAwRb5vXFcY4L+FvZ7LZusDuZ0vEe0Zm8ohn1FM6/X7A3bj4mqmkAcGRWuvC2JwSygNwHAAmMnAI73vPHeqsHA==} engines: {node: '>=18'} '@open-draft/deferred-promise@2.2.0': @@ -404,98 +434,101 @@ packages: '@polka/url@1.0.0-next.28': resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} - '@rollup/rollup-android-arm-eabi@4.28.1': - resolution: {integrity: sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==} + '@react-native/normalize-colors@0.76.5': + resolution: {integrity: sha512-6QRLEok1r55gLqj+94mEWUENuU5A6wsr2OoXpyq/CgQ7THWowbHtru/kRGRr6o3AQXrVnZheR60JNgFcpNYIug==} + + '@rollup/rollup-android-arm-eabi@4.30.1': + resolution: {integrity: sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.28.1': - resolution: {integrity: sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==} + '@rollup/rollup-android-arm64@4.30.1': + resolution: {integrity: sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.28.1': - resolution: {integrity: sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==} + '@rollup/rollup-darwin-arm64@4.30.1': + resolution: {integrity: sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.28.1': - resolution: {integrity: sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==} + '@rollup/rollup-darwin-x64@4.30.1': + resolution: {integrity: sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.28.1': - resolution: {integrity: sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==} + '@rollup/rollup-freebsd-arm64@4.30.1': + resolution: {integrity: sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.28.1': - resolution: {integrity: sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==} + '@rollup/rollup-freebsd-x64@4.30.1': + resolution: {integrity: sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.28.1': - resolution: {integrity: sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==} + '@rollup/rollup-linux-arm-gnueabihf@4.30.1': + resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.28.1': - resolution: {integrity: sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==} + '@rollup/rollup-linux-arm-musleabihf@4.30.1': + resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.28.1': - resolution: {integrity: sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==} + '@rollup/rollup-linux-arm64-gnu@4.30.1': + resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.28.1': - resolution: {integrity: sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==} + '@rollup/rollup-linux-arm64-musl@4.30.1': + resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.28.1': - resolution: {integrity: sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==} + '@rollup/rollup-linux-loongarch64-gnu@4.30.1': + resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.28.1': - resolution: {integrity: sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==} + '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': + resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.28.1': - resolution: {integrity: sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==} + '@rollup/rollup-linux-riscv64-gnu@4.30.1': + resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.28.1': - resolution: {integrity: sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==} + '@rollup/rollup-linux-s390x-gnu@4.30.1': + resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.28.1': - resolution: {integrity: sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==} + '@rollup/rollup-linux-x64-gnu@4.30.1': + resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.28.1': - resolution: {integrity: sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==} + '@rollup/rollup-linux-x64-musl@4.30.1': + resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.28.1': - resolution: {integrity: sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==} + '@rollup/rollup-win32-arm64-msvc@4.30.1': + resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.28.1': - resolution: {integrity: sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==} + '@rollup/rollup-win32-ia32-msvc@4.30.1': + resolution: {integrity: sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.28.1': - resolution: {integrity: sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==} + '@rollup/rollup-win32-x64-msvc@4.30.1': + resolution: {integrity: sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==} cpu: [x64] os: [win32] @@ -518,8 +551,16 @@ packages: '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - '@types/node@22.10.2': - resolution: {integrity: sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==} + '@types/node@22.10.5': + resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} + + '@types/react-dom@19.0.2': + resolution: {integrity: sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==} + peerDependencies: + '@types/react': ^19.0.0 + + '@types/react@19.0.3': + resolution: {integrity: sha512-UavfHguIjnnuq9O67uXfgy/h3SRJbidAYvNjLceB+2RIKVRBzVsh0QO+Pw6BCSQqFS9xwzKfwstXx0m6AbAREA==} '@types/statuses@2.0.5': resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} @@ -611,8 +652,8 @@ packages: brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - bundle-require@5.0.0: - resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==} + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: esbuild: '>=0.18' @@ -633,8 +674,8 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} - chokidar@4.0.2: - resolution: {integrity: sha512-/b57FK+bblSU+dfewfFe0rT1YjVDfOmeLQwCAuC+vwvgLkXboATqqmy+Ipux6JrF6L5joe5CBnFOw+gLWH6yKg==} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} cli-width@4.1.0: @@ -656,8 +697,8 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} - consola@3.2.3: - resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} + consola@3.3.3: + resolution: {integrity: sha512-Qil5KwghMzlqd51UXM0b6fyaGHtOC22scxrwrz4A2882LyUMwQjnvaedN1HAeXzphspQ6CpHkzMAWxBTUruDLg==} engines: {node: ^14.18.0 || >=16.10.0} cookie@0.7.2: @@ -668,6 +709,9 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + debug@4.4.0: resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} @@ -697,16 +741,16 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - es-module-lexer@1.5.4: - resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + es-module-lexer@1.6.0: + resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} hasBin: true - esbuild@0.24.0: - resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==} + esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} engines: {node: '>=18'} hasBin: true @@ -913,6 +957,9 @@ packages: yaml: optional: true + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.4.49: resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} engines: {node: ^10 || ^12 || >=14} @@ -946,9 +993,18 @@ packages: querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + react-dom@19.0.0: + resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} + peerDependencies: + react: ^19.0.0 + react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react@19.0.0: + resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} + engines: {node: '>=0.10.0'} + readdirp@4.0.2: resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==} engines: {node: '>= 14.16.0'} @@ -967,11 +1023,14 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - rollup@4.28.1: - resolution: {integrity: sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==} + rollup@4.30.1: + resolution: {integrity: sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + scheduler@0.25.0: + resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1047,8 +1106,8 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.1: - resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} tinyglobby@0.2.10: resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==} @@ -1107,8 +1166,8 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - type-fest@4.30.2: - resolution: {integrity: sha512-UJShLPYi1aWqCdq9HycOL/gwsuqda1OISdBO3t8RlXQC4QvtuIz4b5FCfe2dQIWEpmlRExKmcTBfP1r9bhY7ig==} + type-fest@4.31.0: + resolution: {integrity: sha512-yCxltHW07Nkhv/1F6wWBr8kz+5BGMfP+RbRSYFnegVb0qV/UMT0G0ElBloPVerqn4M2ZV80Ir1FtCcYv1cT6vQ==} engines: {node: '>=16'} typescript@5.7.2: @@ -1162,6 +1221,22 @@ packages: terser: optional: true + vitest-browser-react@0.0.4: + resolution: {integrity: sha512-4uK8zgo5eHlhrBVEPX8ejRt8Bn4gzV6OZFTPdb1en3FtgjEhhst400XkIQHUC875Q90rOO5Tc4zPpCl8YXvoxg==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + '@types/react': '>18.0.0' + '@types/react-dom': '>18.0.0' + '@vitest/browser': '>=2.1.0' + react: '>18.0.0' + react-dom: '>18.0.0' + vitest: '>=2.1.0' + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + vitest@2.1.8: resolution: {integrity: sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1275,154 +1350,157 @@ snapshots: '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/aix-ppc64@0.24.0': + '@esbuild/aix-ppc64@0.24.2': optional: true '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm64@0.24.0': + '@esbuild/android-arm64@0.24.2': optional: true '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-arm@0.24.0': + '@esbuild/android-arm@0.24.2': optional: true '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/android-x64@0.24.0': + '@esbuild/android-x64@0.24.2': optional: true '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.24.0': + '@esbuild/darwin-arm64@0.24.2': optional: true '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/darwin-x64@0.24.0': + '@esbuild/darwin-x64@0.24.2': optional: true '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.24.0': + '@esbuild/freebsd-arm64@0.24.2': optional: true '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.24.0': + '@esbuild/freebsd-x64@0.24.2': optional: true '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm64@0.24.0': + '@esbuild/linux-arm64@0.24.2': optional: true '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-arm@0.24.0': + '@esbuild/linux-arm@0.24.2': optional: true '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-ia32@0.24.0': + '@esbuild/linux-ia32@0.24.2': optional: true '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-loong64@0.24.0': + '@esbuild/linux-loong64@0.24.2': optional: true '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-mips64el@0.24.0': + '@esbuild/linux-mips64el@0.24.2': optional: true '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-ppc64@0.24.0': + '@esbuild/linux-ppc64@0.24.2': optional: true '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.24.0': + '@esbuild/linux-riscv64@0.24.2': optional: true '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-s390x@0.24.0': + '@esbuild/linux-s390x@0.24.2': optional: true '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/linux-x64@0.24.0': + '@esbuild/linux-x64@0.24.2': + optional: true + + '@esbuild/netbsd-arm64@0.24.2': optional: true '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.24.0': + '@esbuild/netbsd-x64@0.24.2': optional: true - '@esbuild/openbsd-arm64@0.24.0': + '@esbuild/openbsd-arm64@0.24.2': optional: true '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.24.0': + '@esbuild/openbsd-x64@0.24.2': optional: true '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.24.0': + '@esbuild/sunos-x64@0.24.2': optional: true '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-arm64@0.24.0': + '@esbuild/win32-arm64@0.24.2': optional: true '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-ia32@0.24.0': + '@esbuild/win32-ia32@0.24.2': optional: true '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.24.0': + '@esbuild/win32-x64@0.24.2': optional: true - '@inquirer/confirm@5.1.0(@types/node@22.10.2)': + '@inquirer/confirm@5.1.1(@types/node@22.10.5)': dependencies: - '@inquirer/core': 10.1.1(@types/node@22.10.2) - '@inquirer/type': 3.0.1(@types/node@22.10.2) - '@types/node': 22.10.2 + '@inquirer/core': 10.1.2(@types/node@22.10.5) + '@inquirer/type': 3.0.2(@types/node@22.10.5) + '@types/node': 22.10.5 - '@inquirer/core@10.1.1(@types/node@22.10.2)': + '@inquirer/core@10.1.2(@types/node@22.10.5)': dependencies: - '@inquirer/figures': 1.0.8 - '@inquirer/type': 3.0.1(@types/node@22.10.2) + '@inquirer/figures': 1.0.9 + '@inquirer/type': 3.0.2(@types/node@22.10.5) ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 @@ -1433,11 +1511,11 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@inquirer/figures@1.0.8': {} + '@inquirer/figures@1.0.9': {} - '@inquirer/type@3.0.1(@types/node@22.10.2)': + '@inquirer/type@3.0.2(@types/node@22.10.5)': dependencies: - '@types/node': 22.10.2 + '@types/node': 22.10.5 '@isaacs/cliui@8.0.2': dependencies: @@ -1465,7 +1543,7 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@mswjs/interceptors@0.37.3': + '@mswjs/interceptors@0.37.5': dependencies: '@open-draft/deferred-promise': 2.2.0 '@open-draft/logger': 0.3.0 @@ -1488,61 +1566,63 @@ snapshots: '@polka/url@1.0.0-next.28': {} - '@rollup/rollup-android-arm-eabi@4.28.1': + '@react-native/normalize-colors@0.76.5': {} + + '@rollup/rollup-android-arm-eabi@4.30.1': optional: true - '@rollup/rollup-android-arm64@4.28.1': + '@rollup/rollup-android-arm64@4.30.1': optional: true - '@rollup/rollup-darwin-arm64@4.28.1': + '@rollup/rollup-darwin-arm64@4.30.1': optional: true - '@rollup/rollup-darwin-x64@4.28.1': + '@rollup/rollup-darwin-x64@4.30.1': optional: true - '@rollup/rollup-freebsd-arm64@4.28.1': + '@rollup/rollup-freebsd-arm64@4.30.1': optional: true - '@rollup/rollup-freebsd-x64@4.28.1': + '@rollup/rollup-freebsd-x64@4.30.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.28.1': + '@rollup/rollup-linux-arm-gnueabihf@4.30.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.28.1': + '@rollup/rollup-linux-arm-musleabihf@4.30.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.28.1': + '@rollup/rollup-linux-arm64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.28.1': + '@rollup/rollup-linux-arm64-musl@4.30.1': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.28.1': + '@rollup/rollup-linux-loongarch64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.28.1': + '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.28.1': + '@rollup/rollup-linux-riscv64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.28.1': + '@rollup/rollup-linux-s390x-gnu@4.30.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.28.1': + '@rollup/rollup-linux-x64-gnu@4.30.1': optional: true - '@rollup/rollup-linux-x64-musl@4.28.1': + '@rollup/rollup-linux-x64-musl@4.30.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.28.1': + '@rollup/rollup-win32-arm64-msvc@4.30.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.28.1': + '@rollup/rollup-win32-ia32-msvc@4.30.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.28.1': + '@rollup/rollup-win32-x64-msvc@4.30.1': optional: true '@testing-library/dom@10.4.0': @@ -1566,25 +1646,33 @@ snapshots: '@types/estree@1.0.6': {} - '@types/node@22.10.2': + '@types/node@22.10.5': dependencies: undici-types: 6.20.0 + '@types/react-dom@19.0.2(@types/react@19.0.3)': + dependencies: + '@types/react': 19.0.3 + + '@types/react@19.0.3': + dependencies: + csstype: 3.1.3 + '@types/statuses@2.0.5': {} '@types/tough-cookie@4.0.5': {} - '@vitest/browser@2.1.8(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.2))(vitest@2.1.8)': + '@vitest/browser@2.1.8(@types/node@22.10.5)(playwright@1.49.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.5))(vitest@2.1.8)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) - '@vitest/mocker': 2.1.8(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(vite@5.4.11(@types/node@22.10.2)) + '@vitest/mocker': 2.1.8(msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2))(vite@5.4.11(@types/node@22.10.5)) '@vitest/utils': 2.1.8 magic-string: 0.30.17 - msw: 2.7.0(@types/node@22.10.2)(typescript@5.7.2) + msw: 2.7.0(@types/node@22.10.5)(typescript@5.7.2) sirv: 3.0.0 tinyrainbow: 1.2.0 - vitest: 2.1.8(@types/node@22.10.2)(@vitest/browser@2.1.8)(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2)) + vitest: 2.1.8(@types/node@22.10.5)(@vitest/browser@2.1.8)(msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2)) ws: 8.18.0 optionalDependencies: playwright: 1.49.1 @@ -1602,14 +1690,14 @@ snapshots: chai: 5.1.2 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.8(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(vite@5.4.11(@types/node@22.10.2))': + '@vitest/mocker@2.1.8(msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2))(vite@5.4.11(@types/node@22.10.5))': dependencies: '@vitest/spy': 2.1.8 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - msw: 2.7.0(@types/node@22.10.2)(typescript@5.7.2) - vite: 5.4.11(@types/node@22.10.2) + msw: 2.7.0(@types/node@22.10.5)(typescript@5.7.2) + vite: 5.4.11(@types/node@22.10.5) '@vitest/pretty-format@2.1.8': dependencies: @@ -1666,9 +1754,9 @@ snapshots: dependencies: balanced-match: 1.0.2 - bundle-require@5.0.0(esbuild@0.24.0): + bundle-require@5.1.0(esbuild@0.24.2): dependencies: - esbuild: 0.24.0 + esbuild: 0.24.2 load-tsconfig: 0.2.5 cac@6.7.14: {} @@ -1688,7 +1776,7 @@ snapshots: check-error@2.1.1: {} - chokidar@4.0.2: + chokidar@4.0.3: dependencies: readdirp: 4.0.2 @@ -1708,7 +1796,7 @@ snapshots: commander@4.1.1: {} - consola@3.2.3: {} + consola@3.3.3: {} cookie@0.7.2: {} @@ -1718,6 +1806,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + csstype@3.1.3: {} + debug@4.4.0: dependencies: ms: 2.1.3 @@ -1734,7 +1824,7 @@ snapshots: emoji-regex@9.2.2: {} - es-module-lexer@1.5.4: {} + es-module-lexer@1.6.0: {} esbuild@0.21.5: optionalDependencies: @@ -1762,32 +1852,33 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - esbuild@0.24.0: + esbuild@0.24.2: optionalDependencies: - '@esbuild/aix-ppc64': 0.24.0 - '@esbuild/android-arm': 0.24.0 - '@esbuild/android-arm64': 0.24.0 - '@esbuild/android-x64': 0.24.0 - '@esbuild/darwin-arm64': 0.24.0 - '@esbuild/darwin-x64': 0.24.0 - '@esbuild/freebsd-arm64': 0.24.0 - '@esbuild/freebsd-x64': 0.24.0 - '@esbuild/linux-arm': 0.24.0 - '@esbuild/linux-arm64': 0.24.0 - '@esbuild/linux-ia32': 0.24.0 - '@esbuild/linux-loong64': 0.24.0 - '@esbuild/linux-mips64el': 0.24.0 - '@esbuild/linux-ppc64': 0.24.0 - '@esbuild/linux-riscv64': 0.24.0 - '@esbuild/linux-s390x': 0.24.0 - '@esbuild/linux-x64': 0.24.0 - '@esbuild/netbsd-x64': 0.24.0 - '@esbuild/openbsd-arm64': 0.24.0 - '@esbuild/openbsd-x64': 0.24.0 - '@esbuild/sunos-x64': 0.24.0 - '@esbuild/win32-arm64': 0.24.0 - '@esbuild/win32-ia32': 0.24.0 - '@esbuild/win32-x64': 0.24.0 + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 escalade@3.2.0: {} @@ -1873,13 +1964,13 @@ snapshots: ms@2.1.3: {} - msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2): + msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2): dependencies: '@bundled-es-modules/cookie': 2.0.1 '@bundled-es-modules/statuses': 1.0.1 '@bundled-es-modules/tough-cookie': 0.1.6 - '@inquirer/confirm': 5.1.0(@types/node@22.10.2) - '@mswjs/interceptors': 0.37.3 + '@inquirer/confirm': 5.1.1(@types/node@22.10.5) + '@mswjs/interceptors': 0.37.5 '@open-draft/deferred-promise': 2.2.0 '@open-draft/until': 2.1.0 '@types/cookie': 0.6.0 @@ -1891,7 +1982,7 @@ snapshots: path-to-regexp: 6.3.0 picocolors: 1.1.1 strict-event-emitter: 0.5.1 - type-fest: 4.30.2 + type-fest: 4.31.0 yargs: 17.7.2 optionalDependencies: typescript: 5.7.2 @@ -1947,6 +2038,8 @@ snapshots: optionalDependencies: postcss: 8.4.49 + postcss-value-parser@4.2.0: {} + postcss@8.4.49: dependencies: nanoid: 3.3.8 @@ -1974,8 +2067,15 @@ snapshots: querystringify@2.2.0: {} + react-dom@19.0.0(react@19.0.0): + dependencies: + react: 19.0.0 + scheduler: 0.25.0 + react-is@17.0.2: {} + react@19.0.0: {} + readdirp@4.0.2: {} regenerator-runtime@0.14.1: {} @@ -1986,31 +2086,33 @@ snapshots: resolve-from@5.0.0: {} - rollup@4.28.1: + rollup@4.30.1: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.28.1 - '@rollup/rollup-android-arm64': 4.28.1 - '@rollup/rollup-darwin-arm64': 4.28.1 - '@rollup/rollup-darwin-x64': 4.28.1 - '@rollup/rollup-freebsd-arm64': 4.28.1 - '@rollup/rollup-freebsd-x64': 4.28.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.28.1 - '@rollup/rollup-linux-arm-musleabihf': 4.28.1 - '@rollup/rollup-linux-arm64-gnu': 4.28.1 - '@rollup/rollup-linux-arm64-musl': 4.28.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.28.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.28.1 - '@rollup/rollup-linux-riscv64-gnu': 4.28.1 - '@rollup/rollup-linux-s390x-gnu': 4.28.1 - '@rollup/rollup-linux-x64-gnu': 4.28.1 - '@rollup/rollup-linux-x64-musl': 4.28.1 - '@rollup/rollup-win32-arm64-msvc': 4.28.1 - '@rollup/rollup-win32-ia32-msvc': 4.28.1 - '@rollup/rollup-win32-x64-msvc': 4.28.1 + '@rollup/rollup-android-arm-eabi': 4.30.1 + '@rollup/rollup-android-arm64': 4.30.1 + '@rollup/rollup-darwin-arm64': 4.30.1 + '@rollup/rollup-darwin-x64': 4.30.1 + '@rollup/rollup-freebsd-arm64': 4.30.1 + '@rollup/rollup-freebsd-x64': 4.30.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.30.1 + '@rollup/rollup-linux-arm-musleabihf': 4.30.1 + '@rollup/rollup-linux-arm64-gnu': 4.30.1 + '@rollup/rollup-linux-arm64-musl': 4.30.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.30.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.30.1 + '@rollup/rollup-linux-riscv64-gnu': 4.30.1 + '@rollup/rollup-linux-s390x-gnu': 4.30.1 + '@rollup/rollup-linux-x64-gnu': 4.30.1 + '@rollup/rollup-linux-x64-musl': 4.30.1 + '@rollup/rollup-win32-arm64-msvc': 4.30.1 + '@rollup/rollup-win32-ia32-msvc': 4.30.1 + '@rollup/rollup-win32-x64-msvc': 4.30.1 fsevents: 2.3.3 + scheduler@0.25.0: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -2085,7 +2187,7 @@ snapshots: tinybench@2.9.0: {} - tinyexec@0.3.1: {} + tinyexec@0.3.2: {} tinyglobby@0.2.10: dependencies: @@ -2117,20 +2219,20 @@ snapshots: tsup@8.3.5(postcss@8.4.49)(typescript@5.7.2): dependencies: - bundle-require: 5.0.0(esbuild@0.24.0) + bundle-require: 5.1.0(esbuild@0.24.2) cac: 6.7.14 - chokidar: 4.0.2 - consola: 3.2.3 + chokidar: 4.0.3 + consola: 3.3.3 debug: 4.4.0 - esbuild: 0.24.0 + esbuild: 0.24.2 joycon: 3.1.1 picocolors: 1.1.1 postcss-load-config: 6.0.1(postcss@8.4.49) resolve-from: 5.0.0 - rollup: 4.28.1 + rollup: 4.30.1 source-map: 0.8.0-beta.0 sucrase: 3.35.0 - tinyexec: 0.3.1 + tinyexec: 0.3.2 tinyglobby: 0.2.10 tree-kill: 1.2.2 optionalDependencies: @@ -2144,7 +2246,7 @@ snapshots: type-fest@0.21.3: {} - type-fest@4.30.2: {} + type-fest@4.31.0: {} typescript@5.7.2: {} @@ -2157,13 +2259,13 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 - vite-node@2.1.8(@types/node@22.10.2): + vite-node@2.1.8(@types/node@22.10.5): dependencies: cac: 6.7.14 debug: 4.4.0 - es-module-lexer: 1.5.4 + es-module-lexer: 1.6.0 pathe: 1.1.2 - vite: 5.4.11(@types/node@22.10.2) + vite: 5.4.11(@types/node@22.10.5) transitivePeerDependencies: - '@types/node' - less @@ -2175,19 +2277,29 @@ snapshots: - supports-color - terser - vite@5.4.11(@types/node@22.10.2): + vite@5.4.11(@types/node@22.10.5): dependencies: esbuild: 0.21.5 postcss: 8.4.49 - rollup: 4.28.1 + rollup: 4.30.1 optionalDependencies: - '@types/node': 22.10.2 + '@types/node': 22.10.5 fsevents: 2.3.3 - vitest@2.1.8(@types/node@22.10.2)(@vitest/browser@2.1.8)(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2)): + vitest-browser-react@0.0.4(@types/react-dom@19.0.2(@types/react@19.0.3))(@types/react@19.0.3)(@vitest/browser@2.1.8)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(vitest@2.1.8): + dependencies: + '@vitest/browser': 2.1.8(@types/node@22.10.5)(playwright@1.49.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.5))(vitest@2.1.8) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + vitest: 2.1.8(@types/node@22.10.5)(@vitest/browser@2.1.8)(msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2)) + optionalDependencies: + '@types/react': 19.0.3 + '@types/react-dom': 19.0.2(@types/react@19.0.3) + + vitest@2.1.8(@types/node@22.10.5)(@vitest/browser@2.1.8)(msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2)): dependencies: '@vitest/expect': 2.1.8 - '@vitest/mocker': 2.1.8(msw@2.7.0(@types/node@22.10.2)(typescript@5.7.2))(vite@5.4.11(@types/node@22.10.2)) + '@vitest/mocker': 2.1.8(msw@2.7.0(@types/node@22.10.5)(typescript@5.7.2))(vite@5.4.11(@types/node@22.10.5)) '@vitest/pretty-format': 2.1.8 '@vitest/runner': 2.1.8 '@vitest/snapshot': 2.1.8 @@ -2200,15 +2312,15 @@ snapshots: pathe: 1.1.2 std-env: 3.8.0 tinybench: 2.9.0 - tinyexec: 0.3.1 + tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 1.2.0 - vite: 5.4.11(@types/node@22.10.2) - vite-node: 2.1.8(@types/node@22.10.2) + vite: 5.4.11(@types/node@22.10.5) + vite-node: 2.1.8(@types/node@22.10.5) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.10.2 - '@vitest/browser': 2.1.8(@types/node@22.10.2)(playwright@1.49.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.2))(vitest@2.1.8) + '@types/node': 22.10.5 + '@vitest/browser': 2.1.8(@types/node@22.10.5)(playwright@1.49.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.5))(vitest@2.1.8) transitivePeerDependencies: - less - lightningcss diff --git a/src/hyphenateName.ts b/src/hyphenateName.ts new file mode 100644 index 0000000..f1295a2 --- /dev/null +++ b/src/hyphenateName.ts @@ -0,0 +1,12 @@ +// https://github.com/facebook/react/blob/v19.0.0/packages/react-dom-bindings/src/shared/hyphenateStyleName.js + +const uppercasePattern = /([A-Z])/g; +const hyphenateNameCache: Record = {}; + +export const hyphenateName = (name: string): string => { + hyphenateNameCache[name] ??= name + .replace(uppercasePattern, "-$1") + .toLowerCase(); + + return hyphenateNameCache[name]; +}; diff --git a/src/index.ts b/src/index.ts index 1fd4236..abb0d21 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,31 @@ -export const helloWorld = () => { - console.log("Hello world"); +import { + preprocessAtomicStyle, + preprocessKeyframes, + preprocessResetStyle, +} from "./preprocess"; +import { createSheet } from "./sheet"; +import { Keyframes, Nestable, Style } from "./types"; +import { forEach } from "./utils"; + +const sheet = createSheet(); + +const keyframes = (keyframes: Keyframes): string | undefined => + sheet.insertKeyframes(preprocessKeyframes(keyframes)); + +const make = ( + styles: Record>, +): Record => { + const output = {} as Record; + + forEach(styles, (key, value) => { + output[key] = + key[0] === "$" + ? sheet.insertResetRule(preprocessResetStyle(value)) + : sheet.insertAtomicRules(preprocessAtomicStyle(value)); + }); + + return output; }; + +export const css = { keyframes, make }; +export const cx = sheet.cx; diff --git a/src/normalizeValue.ts b/src/normalizeValue.ts new file mode 100644 index 0000000..0ba0467 --- /dev/null +++ b/src/normalizeValue.ts @@ -0,0 +1,100 @@ +import normalizeColor from "@react-native/normalize-colors"; +import { Property } from "./types"; + +const normalizeValueCache: Record = {}; + +/** + * CSS properties which accept numbers but are not in units of "px" + * From https://github.com/facebook/react/blob/v19.0.0/packages/react-dom-bindings/src/shared/isUnitlessNumber.js + */ +const unitlessProperties = new Set([ + "animationIterationCount", + "aspectRatio", + "borderImageOutset", + "borderImageSlice", + "borderImageWidth", + "columnCount", + "flex", + "flexGrow", + "flexShrink", + "fontWeight", + "gridColumnEnd", + "gridColumnStart", + "gridRowEnd", + "gridRowStart", + "lineClamp", + "lineHeight", + "opacity", + "order", + "orphans", + "scale", + "tabSize", + "widows", + "zIndex", + "zoom", + + // SVG-related properties + "fillOpacity", + "floodOpacity", + "stopOpacity", + "strokeDasharray", + "strokeDashoffset", + "strokeMiterlimit", + "strokeOpacity", + "strokeWidth", +] satisfies Property[]); + +/** + * From https://github.com/necolas/react-native-web/blob/0.19.13/packages/react-native-web/src/exports/StyleSheet/compiler/normalizeValueWithProperty.js#L13 + */ +const colorProperties = new Set([ + "backgroundColor", + "borderBottomColor", + "borderColor", + "borderLeftColor", + "borderRightColor", + "borderTopColor", + "color", + "textDecorationColor", +] satisfies Property[]); + +const isWebColor = (color: string): boolean => + color === "currentcolor" || + color === "currentColor" || + color === "inherit" || + color.indexOf("var(") === 0; + +export const normalizeValue = ( + value: string | number | undefined, + property: string, +): string | undefined => { + if (typeof value === "number") { + return unitlessProperties.has(property) ? String(value) : `${value}px`; + } + + if (colorProperties.has(property)) { + if (value == null || isWebColor(value)) { + return value; + } + + if (normalizeValueCache[value] != null) { + return normalizeValueCache[value]; + } + + const normalizedColor = normalizeColor(value); + + if (normalizedColor != null) { + const int = ((normalizedColor << 24) | (normalizedColor >>> 8)) >>> 0; + + const r = (int >> 16) & 255; + const g = (int >> 8) & 255; + const b = int & 255; + const a = ((int >> 24) & 255) / 255; + + normalizeValueCache[value] = `rgba(${r}, ${g}, ${b}, ${a.toFixed(2)})`; + return normalizeValueCache[value]; + } + } + + return value; +}; diff --git a/src/preprocess.ts b/src/preprocess.ts new file mode 100644 index 0000000..0726632 --- /dev/null +++ b/src/preprocess.ts @@ -0,0 +1,178 @@ +import valueParser from "postcss-value-parser"; +import { + Keyframes, + LonghandProperty, + Nestable, + Property, + ShorthandProperty, + Style, + ValueOf, +} from "./types"; +import { forEach } from "./utils"; + +const shorthands: Partial> = { + borderColor: [ + "borderTopColor", + "borderRightColor", + "borderBottomColor", + "borderLeftColor", + ], + borderRadius: [ + "borderTopLeftRadius", + "borderTopRightRadius", + "borderBottomRightRadius", + "borderBottomLeftRadius", + ], + borderStyle: [ + "borderTopStyle", + "borderRightStyle", + "borderBottomStyle", + "borderLeftStyle", + ], + borderWidth: [ + "borderTopWidth", + "borderRightWidth", + "borderBottomWidth", + "borderLeftWidth", + ], + gap: ["rowGap", "columnGap"], + inset: ["top", "right", "bottom", "left"], + insetBlock: ["insetBlockStart", "insetBlockEnd"], + insetInline: ["insetInlineStart", "insetInlineEnd"], + margin: ["marginTop", "marginRight", "marginBottom", "marginLeft"], + marginBlock: ["marginBlockStart", "marginBlockEnd"], + marginHorizontal: ["marginRight", "marginLeft"], + marginInline: ["marginInlineStart", "marginInlineEnd"], + marginVertical: ["marginTop", "marginBottom"], + overflow: ["overflowX", "overflowY"], + overscrollBehavior: ["overscrollBehaviorX", "overscrollBehaviorY"], + padding: ["paddingTop", "paddingRight", "paddingBottom", "paddingLeft"], + paddingBlock: ["paddingBlockStart", "paddingBlockEnd"], + paddingHorizontal: ["paddingRight", "paddingLeft"], + paddingInline: ["paddingInlineStart", "paddingInlineEnd"], + paddingVertical: ["paddingTop", "paddingBottom"], + scrollMarginBlock: ["scrollMarginBlockStart", "scrollMarginBlockEnd"], + scrollMarginInline: ["scrollMarginInlineStart", "scrollMarginInlineEnd"], + scrollPaddingBlock: ["scrollPaddingBlockStart", "scrollPaddingBlockEnd"], + scrollPaddingInline: ["scrollPaddingInlineStart", "scrollPaddingInlineEnd"], +} satisfies Record< + Exclude, + LonghandProperty[] +>; + +const preprocessRule = ( + acc: Style, + key: Property, + value: ValueOf