diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b9d2559 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,27 @@ +# The one place this repository states its own formatting. +# +# Without it, style lives only inside the committed files as a convention, and +# every editor applies its own defaults instead — which is how `styles.css` +# came back from a save at four-space indent while the other forty files stayed +# at two. Every mainstream editor reads this file natively, with nothing to +# install, so the rule travels with the checkout rather than with whoever set +# their editor up correctly. +# +# `.gitattributes` is the other half: it holds the line endings, which git +# rewrites on checkout and an editor never sees. + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true +max_line_length = 100 + +# Two trailing spaces are a hard line break in Markdown, so trimming them here +# would silently edit the prose rather than tidy it. +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..05d068c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,19 @@ +# Line endings are this repository's decision, not each machine's `core.autocrlf`. +# +# Unset, the same file is different bytes on Windows and on Linux: twenty files +# in this tree carry CRLF, every git command on a Windows checkout warns that it +# is converting them, and a diff can come back as "every line changed" for a +# commit that changed one. `eol=lf` normalises the index *and* the working tree, +# so the bytes are the same everywhere — including in the published package, +# where `styles.css` and `tailwind.css` ship as source and get read by somebody +# else's build. +# +# Every tracked file here is text; there is nothing `text=auto` could mangle. +* text=auto eol=lf + +# Generated: collapsed by default in GitHub's diff view, where a dependency +# change otherwise arrives as two thousand lines nobody reads. Not marked +# `-diff`, because suppressing the diff locally too would hide the one thing +# worth checking in a lockfile — whether a dependency turned up that the +# manifest change did not explain. +pnpm-lock.yaml linguist-generated diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d4778a..bdd6495 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: node-version-file: .nvmrc cache: pnpm - run: pnpm install --frozen-lockfile + - run: pnpm format:check - run: pnpm lint - run: pnpm build - run: pnpm test - - run: pnpm test:gates diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..917d7fd --- /dev/null +++ b/.prettierignore @@ -0,0 +1,22 @@ +# Build output and generated files. +dist/ +pnpm-lock.yaml + +# Markdown is formatted by hand here, and Prettier's markdown printer fights +# that on two counts rather than helping: +# +# 1. It rewrites `*emphasis*` as `_emphasis_`. The repository uses the asterisk +# form throughout, the two render identically, and the change is 40-odd +# lines of diff that mean nothing. +# 2. It pads table cells so the pipes line up. `DESIGN.md`'s tables carry long +# sentences, so alignment pushes single lines past 170 characters — in the +# two files whose whole convention is a hard wrap at 100. +# +# Prose wrapping itself is safe (`proseWrap` defaults to preserve); it is the +# other two that make the docs worse. They stay hand-formatted, held to the +# 100-column guide in `.editorconfig`. +*.md + +# Process records, kept as written at the time. +docs/superpowers/ +.superpowers/ diff --git a/DESIGN.md b/DESIGN.md index 80748f1..5a91211 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -2,139 +2,196 @@ ## Purpose and Ownership -This document governs how the Robomous design system looks and behaves. It is not a component -catalogue: the components are shadcn's, and their classes, anatomy and states are read from -the files the CLI wrote rather than transcribed here. What it owns is the *governance* — which -layer decides what, what this package may add, what it may not change, and which gate holds each -rule. Where a rule is machine-checked the gate is named, because a rule nothing checks is a -preference. - -**This package owns the foundation; consumers own their extensions.** The foundation is the -shadcn preset plus `brand`, the twenty-one primitives held canonical-plus-additive, and the -status vocabulary (`statusTone.ts`, the Badge's four status variants). A consumer's domain -components, screens, navigation, and product behaviour are the consumer's own, governed in the -consumer's repository — see *Per-consumer extensions*. - -## Source of Truth - -Four layers decide the visual result inside this package. **When two disagree, the earlier one -wins.** A consumer's patterns and screens sit above all four, in the consumer's repo. - -| # | Layer | Where it lives | Decides | -| --- | --- | --- | --- | -| 1 | CLI configuration | [`components.json`](components.json) | The fields shadcn's own tools read — style, base colour, icon library, menu treatment, aliases | -| 2 | Decoded preset intent | preset code `b2iH`, shadcn CLI **4.19.0** | Every value the config schema has no field for — radius, fonts, chart palette, both themes | -| 3 | Official registry output | [`shadcn/`](shadcn) snapshots, realised in [`src/primitives/`](src/primitives) | A primitive's API, anatomy, variants, sizes, states and styling | -| 4 | Foundation additive extensions | `src/primitives/badge.tsx`, `src/tokens.ts`, `src/styles.css` | Semantic variants and tokens the preset has no name for | - -A consumer may not reach past its own pattern layer to restyle a primitive, and a primitive may -not encode a screen's decision. A value missing from `components.json` is not drift: the config -schema is **strict** and rejects a property it has no field for, so the preset's radius, fonts -and chart palette live in `styles.css` as runtime values instead. The stylesheet is the one that -runs; [`src/tokens.ts`](src/tokens.ts) mirrors it for callers that cannot read CSS (a -``, a test), and `src/tokens.test.ts` asserts the two agree declaration for declaration. - -## shadcn Foundation - -Preset `b2iH`, generated with shadcn CLI **4.19.0**, decodes to: - -| Property | Value | Where it lands | -| --- | --- | --- | -| `style` | `nova` on the Radix base | `components.json` → `"style": "radix-nova"` | -| `baseColor` | `neutral` | `components.json` → `tailwind.baseColor` | -| `chart` | `neutral` | `styles.css` → `--chart-1` … `--chart-5` | -| `icons` | `lucide` | `components.json` → `iconLibrary` | -| `font` | `geist` | `styles.css` → `--font-sans: 'Geist Variable', sans-serif` | -| `heading` | `inherit` | `styles.css` → `--font-heading: var(--font-sans)` | -| `radius` | `medium` | `styles.css` → `--radius: 0.625rem` | -| `menu` | `default` / `subtle` | `components.json` → `menuColor`, `menuAccent` | -| `pointer` | on | `styles.css` base layer → `cursor: pointer` on pressable controls | - -**What `components.json` can hold** is its whole current content: `style`, `rsc` (`false`), -`tsx` (`true`), `tailwind` (`config` empty, `css` `src/styles.css`, `baseColor` `neutral`, -`cssVariables` `true`, `prefix` empty), `iconLibrary`, `rtl` (`false`), the five `aliases` -(`@/components`, `@/lib/cn`, `@/primitives`, `@/lib`, `@/hooks`), `menuColor`, `menuAccent`, -`registries` (empty). **What it cannot hold:** the radius, either font, the chart palette, any -colour — `gates/tokens.test.mjs` holds the file to that set, so nobody adds a documentary -field the CLI then rejects. Radix supplies behaviour under every primitive and Nova is the -styling on top; Base UI is an accepted official dependency for the same reason — shadcn's own -Combobox is Base UI-backed, and nothing else here uses it. - -## Primitive Governance - -> Components in the primitive layer preserve the API, anatomy, official variants, default -> variants, sizes, states, data attributes, accessibility behavior, and Nova styling generated -> by the configured shadcn foundation. This package may add semantic variants only when they are -> additive and do not reinterpret an upstream variant. Product-specific compositions belong -> above primitives, in a consumer. A behavioral deviation from upstream requires a reproduced -> defect, a regression test, and an explicit SHADCN DEVIATION comment. - -The primitive layer is exactly these twenty-one files: `alert`, `badge`, `button`, `card`, -`combobox`, `dialog`, `dropdown-menu`, `field`, `input`, `input-group`, `label`, `progress`, -`select`, `separator`, `sheet`, `skeleton`, `sonner`, `table`, `tabs`, `textarea`, `tooltip`. - -**The snapshot gate.** `shadcn/.tsx` is the CLI's pristine output, and -`gates/canonical.test.mjs` refuses any primitive that is not its snapshot **plus -added lines** — a removed line, a reworded class, a renamed prop all fail. Import paths are -relativised (`@/lib/cn` → `../lib/cn`, `@/primitives/x` → `./x`) because the package is compiled -by `tsc` rather than bundled with a path alias; the gate accounts for that rewrite and nothing -else. - -**Allowed:** an added `cva` variant line whose meaning the foundation has no name for (today, -four on the Badge), and a framework adapter under *Allowed shadcn Deviations*. **Forbidden:** -changing an official variant's classes; renaming or dropping a subcomponent; adding a prop; -changing a default variant or size; touching focus, keyboard or `aria` behaviour; adjusting -geometry. Each of those belongs above the primitive — at a call site, or in a consumer's -pattern. +This package owns its components. Not a copy of them, not a styled wrapper around something it +must leave intact — the twenty-one files in `src/components/` are this repository's to edit, and +a change to one of them needs a reason, a test and a review, which is the same thing every other +file here needs. A component here is **written, not generated**: it is ordinary source, and the +only thing that separates it from a screen's own component is the set of rules below. + +This document is not a component catalogue. A component's anatomy, its variants and its class +strings are read from `src/components/`, where they live, rather than transcribed here where they +would go stale. What this document governs is the smaller and more durable thing: **the rules +this package holds itself to** — where a colour may be spelled, where a token may live, what the +brand paints, which vocabulary a status speaks. Where a rule is machine-checked the gate is +named, because a rule nothing checks is a preference. + +**This package owns its components; consumers own their extensions.** A consumer's domain +components, screens, navigation and product behaviour are the consumer's own, governed in the +consumer's repository — see *Per-consumer extensions*, which is the contract two downstream +repositories run on today. + +## The Rules, and the Gate That Holds Each + +Seven rules, and each one has a gate that holds it. The gates are ordinary vitest files under +`src/gates/`, so `pnpm test` runs them with everything else; there is no second test command. + +| Rule | Held by | +| --- | --- | +| Never a colour inside a class string | `colouredClassesIn`, run by [`src/gates/tokens.test.ts`](src/gates/tokens.test.ts) over every tracked `.ts`, `.tsx` and `.css` | +| The brand colour is identity; it paints nothing here | `brandUsagesIn`, same file, asserted against an empty list of sites | +| The tokens have exactly one home, the stylesheet | Same file refuses a `tailwind.config.*` anywhere in the tree; [`src/theme/tokens.test.ts`](src/theme/tokens.test.ts) holds the TypeScript mirror to it declaration for declaration | +| One icon set, and only one | Same file, over every manifest and every import | +| The status palette lives in `Badge` and `statusTone`, nowhere else | `statusPaletteIn`, run by [`src/gates/design.test.ts`](src/gates/design.test.ts) over the package's own sources | +| No rival colour family stands in for the status palette | `competingStatusPaletteIn`, same file | +| Every component module is part of the public surface | Same file, comparing `src/components/` against the exports in `src/index.ts` | + +Each scanner is a pure function over one file's text, so its own test proves it twice: once on +fabricated input that must be caught, once on input that merely resembles a violation and must +pass. A gate that cannot be shown to fail is not evidence. + +**The scanners are published**, because the rules do not stop at this package's edge. A consumer +imports them and runs the same scans over its own sources, with its own extension registry: + +```ts +import { colouredClassesIn, foundationTokenNames } from "@robomous/ui-core/gates"; +``` + +`@robomous/ui-core/gates` exports exactly eight things — the four scanners above, three CSS +readers (`blockBody`, `rawDeclarations`, `declarations`) that a consumer needs to parse its own +stylesheet the way this package parses its own, and `foundationTokenNames()`, which reads the +token names off the shipped stylesheet so a consumer can assert that none of its extensions +shadows one. + +A rule stated in this document that no gate holds is a rule under review, not an exemption. + +## Tokens + +**The tokens have one home: [`src/theme/styles.css`](src/theme/styles.css).** Tailwind v4 is +CSS-first, so the stylesheet is not a mirror of a config — it *is* the config, and every utility +in this package and in every consuming app resolves through it. There is no `tailwind.config.js` +in this repository and there must not be one: a second file declaring the same names would win +for some utilities and not others, and the failure would read as a styling bug rather than as +two sources of truth. + +[`src/theme/tokens.ts`](src/theme/tokens.ts) is that stylesheet as TypeScript, for the two kinds +of caller that cannot read CSS — a `` or `` that needs a colour as a string, and a +test. It is a mirror, never a second definition: `src/theme/tokens.test.ts` parses the +stylesheet's `:root`, `.dark` and `@theme inline` blocks and asserts the two agree declaration +for declaration, in both directions, so a value added to one and forgotten in the other fails. + +Both themes are declared in full, which is what makes switching theme a variable swap rather +than a cascade. Every rule in this document is therefore written against a **role** rather than +a light-mode value: "the page" resolves per theme and is never asserted to be white. + +**Radius is a value, not a setting.** `--radius: 0.625rem` lives in the stylesheet, and every +step derives from it in `@theme inline` (`radius-sm` at `× 0.6` through `radius-4xl` at `× 2.6`). +No arbitrary radius appears outside that scale. A design value belongs where it runs: written in +the stylesheet it is one declaration every utility resolves through, and written anywhere else it +is documentation that drifts from the thing it describes. + +**The variant layer.** [`src/theme/tailwind.css`](src/theme/tailwind.css), imported by the +stylesheet ahead of the token blocks, declares the `data-*` variants the components' state styles +qualify on — `data-open`, `data-closed`, `data-active`, `data-disabled` and the rest. Each matches +both the `data-state="open"` attribute the behaviour libraries set and the bare `[data-open]` +form, which is the reason it is declared rather than left to Tailwind's own shorthand: the +shorthand covers only the bare attribute, so it would emit a rule for every state style in the +package and match none of them. That failure is silent — the CSS compiles and the components stop +reacting — so `src/theme/imports.test.ts` asserts the file both exists and is committed. ## Color and Theme -shadcn's semantic names are the only vocabulary. There is no second layer (`surface`, `error`, +The semantic names are the only vocabulary. There is no second layer (`surface`, `error`, `foreground-secondary`) and no alias renaming what a token already means — `destructive` stays `destructive`. Two roles are worth restating because they are the two that get confused: `primary` is the one high-emphasis colour, and `accent` is the interactive hover/focus surface, the token a row or a menu item lights up with and never what a button fills with. `chart-1`…`chart-5` identify a series, never a status. -Both themes are declared in full, so switching theme is a variable swap. Every rule here is -written against a role rather than a light-mode value: "the page" resolves per theme and is -never asserted to be white. +**Never a colour inside a class string.** `bg-[#eb5a47]`, `text-[rgb(...)]`, +`ring-[var(--something)]` — all of them put a colour where the token contract cannot see it, and +`colouredClassesIn` refuses them across the whole tree. An arbitrary value that is *not* a colour +stays legal, because the rule is about colour: a one-off `translate-y-[3px]` is geometry. The one +sanctioned exception is a `color-mix` whose arguments are all tokens — the Button's own hover +step is one — because mixing two tokens names no colour of its own. -**The one retained extension** is `brand` (Robomous coral, identity only). Consumer vocabulary -such as VisionSet's `stage` and `origin-*` lives in the consumer's own stylesheet and token -module — see *Per-consumer extensions*. - -The former `success` and `warning` tokens are **retired**, with their `-foreground` companions: -status colour is a Badge variant or `src/statusTone.ts`, so a status hue is spelled in one -place instead of two. `gates/tokens.test.mjs` re-checks the retired vocabulary -by an independent method from `src/tokens.test.ts`. **There is no `info` token** — the Badge has -an `info` variant and `statusTone.ts` an `info` ink, neither of which implies one; adding one is -a design decision to make here first. +**The one name** beyond that vocabulary is `brand`: Robomous orange, identity only. ### Where the brand is -Robomous coral is identity: a consuming app's wordmark and the styleguide swatch that shows the -value off. **Two sites, enumerated per consuming app**, in that app's own gate. A functional -control reaching for `brand` is a semantic-colour violation however many other sites already use -it correctly, and the gate and this section move together or not at all. +Robomous orange — `oklch(0.663 0.205 39.9)`, `#F5580B` — is identity: a wordmark, and the +styleguide swatch that shows the value off. It is not a functional-UI colour, and a control +reaching for it is a semantic-colour violation however many other sites already use it correctly. + +It measures 3.34:1 against `background`, which clears 3:1 for large text and non-text marks and +misses 4.5:1 for body copy. A wordmark is exempt from that threshold; a button label would not +be, which is the contrast argument for the rule the paragraph above states on other grounds. -This package itself has **zero brand sites**: it declares the `--brand` token and never paints -with it, and `gates/tokens.test.mjs` enforces that. +**This package has zero brand sites.** It declares the `--brand` token and never paints with it; +`brandUsagesIn` is asserted against an empty list, so the first `bg-brand` written here fails. +Brand sites are a consuming app's decision, enumerated and gated in that app's own repository. ### Menus and the page's palette -`menuColor: default` means a menu, select or combobox surface paints on the same `popover` -tokens as the page around it, and follows that page from light to dark. Nova's alternative, -`inverted`, gives each of those surfaces the literal `dark` class, so a floating panel is the -dark theme's `popover` whatever the page is doing; in a light application it arrives as a black -rectangle that reads as a different product. The tooltip is not a counterexample: Nova paints it -`bg-foreground text-background`, which flips by construction, because one line of text has no -palette of its own to keep in step. `menuAccent: subtle` means items highlight with `accent`, -the token every other hover state uses. +A menu, select or combobox surface paints on the same `popover` tokens as the page around it, +and follows that page from light to dark. The alternative — giving each floating surface the +literal `dark` class — makes a panel the dark theme's `popover` whatever the page is doing, and +in a light application it arrives as a black rectangle that reads as a different product. The +tooltip is not a counterexample: it is painted `bg-foreground text-background`, which flips by +construction, because one line of text has no palette of its own to keep in step. Menu items +highlight with `accent`, the token every other hover state uses. + +## Status Vocabulary + +**Four names, and one place each of them is spelled.** + +`Badge` carries the four status variants — `success`, `warning`, `info`, `quiet` — and +[`src/theme/statusTone.ts`](src/theme/statusTone.ts) carries the tones for everything that is not +a chip. Those two files are the entire vocabulary. A third file naming the same colour family is +not a use of the palette; it is a fork of it, which is why `statusPaletteIn` scans for the family +and allows exactly `badge.tsx`, `statusTone.ts` and `statusTone.test.ts`. + +The reason is not tidiness. A status hue spelled in two places drifts, and it drifts silently: +the second spelling looks correct in isolation and only reads as wrong beside the first, on a +screen neither author was looking at. Held to one place, "warning" is a name, and its value is a +detail. + +### The Badge's four status variants + +| Variant | Reads as | +| --- | --- | +| `success` | emerald — settled | +| `warning` | amber — waiting on a person | +| `info` | sky — informational | +| `quiet` | muted — a state that exists without asking for attention | + +Each hue is the `destructive` recipe on another family: a `/10` surface, ink at `700` (`400` in +dark), a focus ring at matching opacity, a hover step for the anchor case. `quiet` is the one +that is not a hue — the absence of one. **Geometry is untouched**: every status variant keeps the +base string's height, padding, type size, radius and transparent hairline, and names no size, no +radius and no border colour. `src/gates/design.test.ts` asserts each of the four paints a soft +surface and readable ink and never a coloured stroke. + +### The status palette + +One family, five roles: **emerald** settled, **amber** waiting on a person, **sky** +informational, **muted** no signal, **destructive** failed. Those three hues are the only colour +families outside the semantic tokens, and the treatment is always **soft surface plus moderate +ink**, so a chip carries a hue without competing with `primary`. **No rival family** — `green`, +`lime`, `teal`, `yellow`, `orange`, `blue`, `cyan`, `red` — appears anywhere; +`competingStatusPaletteIn` holds that half, because a rule with one allowed spelling is only +enforceable if the near-misses are refused too. + +**Screen authors never pick a shade.** A status takes a Badge variant, or reads its colour from +`statusTone.ts`, whose utilities are written out whole rather than assembled — Tailwind scans +source *text*, so a class built at runtime is a rule the build never emitted, and the failure is +silent: + +| Export | Values | Use | +| --- | --- | --- | +| `StatusTone` | `neutral · accent · success · warning · destructive` | The type every tone-taking prop speaks | +| `TONE_BORDER` | `border-emerald-500 dark:border-emerald-400`, `border-amber-500 dark:border-amber-400`, `border-border`, `border-primary`, `border-destructive` | A tone's stroke, where the stroke is the mark | +| `TONE_FILL` | `bg-emerald-500 dark:bg-emerald-400`, `bg-amber-500 dark:bg-amber-400`, `bg-muted-foreground`, `bg-primary`, `bg-destructive` | A solid mark — a dot, a timeline cell — where a `/10` surface would vanish at 4px | +| `STATUS_INK` | `text-emerald-700 dark:text-emerald-400`, `text-amber-700 dark:text-amber-400`, `text-sky-700 dark:text-sky-400` | An icon or a run of inline text carrying a status | + +There is **no `info` token** and no `success`/`warning` token. A status hue is a Badge variant or +a `statusTone` entry, so it is spelled once; adding a token for one is a design decision to make +here first. Status Badge contrast is measured against page and card surfaces — on a full +`bg-muted` panel the warning ink measures ~4.3:1, so a status Badge is not placed on a muted +panel without a re-check. ## Action Hierarchy -Six official Button variants, one intent each: +Six Button variants, one intent each: | Variant | Intent | | --- | --- | @@ -145,14 +202,16 @@ Six official Button variants, one intent each: | `destructive` | The action that ends something | | `link` | Navigation wearing a control's affordance | -Sizes are `default`, `xs`, `sm`, `lg`, `icon`, `icon-xs`, `icon-sm`, `icon-lg`. **One dominant -action per view**; which action that is on which screen is a consumer's product behaviour. +Sizes are `default`, `xs`, `sm`, `lg`, `icon`, `icon-xs`, `icon-sm`, `icon-lg`, and `inline`. +`inline` is the odd one and earns its place: `h-auto p-0`, for a `link` button that lives inside +a sentence or a table cell, where a control's height and padding would push the line apart. + +**One dominant action per view**; which action that is on which screen is a consumer's product +behaviour. **A status is not a step in the action hierarchy.** `success` describes an outcome, never a -control's emphasis, and there is no variant for it — a saved form reports itself with a toast or -a Badge while its button stays `default`. Three names a v1 call site may still reach for — -`primary`, `success`, `md` — **do not exist** here, and -`gates/extensions.test.mjs` pins the variant and size lists exactly. +control's emphasis, and there is no Button variant for it — a saved form reports itself with a +toast or a Badge while its button stays `default`. ## Status and Feedback @@ -166,59 +225,11 @@ a Badge while its button stays `default`. Three names a v1 call site may still r **Colour is never the only signal.** Every status carries a redundant channel — a word, an icon, a shape. -## Badge Variants - -Official: `default`, `secondary`, `destructive`, `outline`, `ghost`, `link`. The foundation adds -exactly four status variants, as four added `cva` entries, verbatim: - -| Variant | Classes | -| --- | --- | -| `success` | `bg-emerald-500/10 text-emerald-700 focus-visible:ring-emerald-500/20 dark:bg-emerald-400/10 dark:text-emerald-400 dark:focus-visible:ring-emerald-400/40 [a]:hover:bg-emerald-500/20 dark:[a]:hover:bg-emerald-400/20` | -| `warning` | `bg-amber-500/10 text-amber-700 focus-visible:ring-amber-500/20 dark:bg-amber-400/10 dark:text-amber-400 dark:focus-visible:ring-amber-400/40 [a]:hover:bg-amber-500/20 dark:[a]:hover:bg-amber-400/20` | -| `info` | `bg-sky-500/10 text-sky-700 focus-visible:ring-sky-500/20 dark:bg-sky-400/10 dark:text-sky-400 dark:focus-visible:ring-sky-400/40 [a]:hover:bg-sky-500/20 dark:[a]:hover:bg-sky-400/20` | -| `quiet` | `bg-muted text-muted-foreground [a]:hover:bg-muted/80` | - -Each is Nova's own `destructive` recipe on another hue: a `/10` surface, ink at `700` (`400` in -dark), a focus ring at matching opacity, a hover step for the anchor case. `quiet` is the one -that is not a hue — the absence of one, for a state that exists without asking for attention. - -**Geometry is untouched.** The base string keeps its height, padding, type size, radius and -transparent hairline, and no added variant names a size, a radius or a border colour. - -## Status Palette - -One family, five roles: **emerald** settled, **amber** waiting on a person, **sky** -informational, **muted** no signal, **destructive** failed. Those three hues are the only ones -outside the semantic tokens, and the treatment is always **soft surface plus moderate ink** — a -`/10` fill with `700`/`400` text — so a chip carries a hue without competing with `primary`. - -The `emerald`, `amber` and `sky` utilities are permitted in exactly three files: -`src/primitives/badge.tsx`, [`src/statusTone.ts`](src/statusTone.ts) and its test. A fourth -place naming the family is a fork of the palette, not a use of it. **No competing family** — -`green`, `lime`, `teal`, `yellow`, `orange`, `blue`, `cyan`, `red` — appears anywhere. -`gates/extensions.test.mjs` holds both halves, and a consumer runs the same scans over its own -sources with the helpers from `@robomous/ui-core/gates`. - -**Screen authors never pick a shade.** A status takes a Badge variant, or reads its colour from -`statusTone.ts`, whose utilities are written out whole rather than assembled — Tailwind scans -source text, so a class built at runtime is a rule the build never emitted: - -| Export | Values | Use | -| --- | --- | --- | -| `StatusTone` | `neutral · accent · success · warning · destructive` | The type every tone-taking prop speaks | -| `TONE_BORDER` | `border-emerald-500 dark:border-emerald-400`, `border-amber-500 dark:border-amber-400`, `border-border`, `border-primary`, `border-destructive` | A tone's stroke, where the stroke is the mark | -| `TONE_FILL` | `bg-emerald-500 dark:bg-emerald-400`, `bg-amber-500 dark:bg-amber-400`, `bg-muted-foreground`, `bg-primary`, `bg-destructive` | A solid mark — a dot, a timeline cell — where the Badge's `/10` surface would vanish at 4px | -| `STATUS_INK` | `text-emerald-700 dark:text-emerald-400`, `text-amber-700 dark:text-amber-400`, `text-sky-700 dark:text-sky-400` | An icon or a run of inline text carrying a status | - -Status Badge contrast is measured against page/card surfaces; on a full `bg-muted` panel the -warning ink measures ~4.3:1, so a status Badge is not placed on a muted panel without a re-check. - ## Alerts -The official anatomy, and only it: `Alert`, `AlertTitle`, `AlertDescription`, `AlertAction` — a -title is a child, never a prop. Variants are `default` and `destructive`; there is no -informational or settled Alert, because a condition worth interrupting the page for is either -neutral or bad. +The anatomy, and only it: `Alert`, `AlertTitle`, `AlertDescription`, `AlertAction` — a title is a +child, never a prop. Variants are `default` and `destructive`; there is no informational or +settled Alert, because a condition worth interrupting the page for is either neutral or bad. An Alert's border is **structural**: `default` takes the card surface, and `destructive` keeps that same surface, recolouring only the ink. No saturated surface, no coloured stroke. An Alert @@ -231,7 +242,7 @@ floats and closes; `muted` recesses. A component picks one — it does not compo Elevation is a ring plus a resting shadow, never a coloured border and never a gradient. **Borders are structural**: a hairline separates or contains. **Status is not a stroke** — no -status is communicated by recolouring a border, which is why every added Badge variant keeps +status is communicated by recolouring a border, which is why every status Badge keeps `border-transparent`, and why `TONE_BORDER` exists only for marks whose whole body *is* the stroke. `outline` on its own, Badge or Button, takes `border-border`, because `outline` means "bounded", not "notable". @@ -240,47 +251,44 @@ stroke. `outline` on its own, Badge or Button, takes `border-border`, because `o The base layer applies `border-border` and `outline-ring/50` to every element, naming the outline's **colour only**. Focus *geometry* belongs to the component: one blanket -`:focus-visible` declaration in the stylesheet overrides every primitive's ring at once and wins -on layer order, so each focusable primitive carries Nova's own treatment instead. Focus is never -removed and never colour-only. +`:focus-visible` declaration in the stylesheet overrides every component's ring at once and wins +on layer order, so each focusable component carries its own `focus-visible:ring-3 +focus-visible:ring-ring/50` instead. `src/theme/tokens.test.ts` asserts that absence, because the +absence is the point — a single global rule is how thirteen components' rings got overridden at +once. Focus is never removed and never colour-only. ## Typography - **One family: Geist**, through `--font-sans`, bundled offline through `@fontsource-variable/geist`. No runtime fetch to a font host, ever. - **`font-heading` survives as a hook, not a difference.** It resolves to `--font-sans`, so a - heading is the same face at another size and weight; `h1`–`h4` carry it in the base layer, so - a later preset that splits the families again lands in one declaration. + heading is the same face at another size and weight; `h1`–`h4` carry it in the base layer, so a + later decision to split the families again lands in one declaration. - **One justified technical role: `font-mono`**, on Tailwind's default stack. It marks machine-shaped content — identifiers, hashes, model references, measurements. Prose never wears it. -- Size and weight come from the primitive, or from Tailwind's ordinary scale. There is no - custom type-scale token: `--text-page`, `--text-section`, `--text-body` and `--text-meta` - are retired, and `gates/tokens.test.mjs` keeps them retired. +- Size and weight come from the component, or from Tailwind's ordinary scale. There is no custom + type-scale token, and `src/theme/tokens.test.ts` keeps the retired ones retired. ## Density, Spacing, and Radius Two scales answer two questions, and conflating them is the anti-pattern this section exists to -prevent. **Component geometry is Nova's**, fixed per control by the foundation: a control's -height, padding and internal gaps are not a per-screen choice, and this document does not -restate them — `src/primitives/` carries them and the snapshot gate holds them. **Page and -layout rhythm is the consumer's own composition**, on Tailwind's ordinary spacing scale: `p-6`, -`gap-4`, `space-y-8`. Dense where the content is plural — data surfaces show more of the thing -the reader came for; generous where prose and forms are read one thing at a time. - -**Radius is a runtime value, not a config field.** The preset's `radius: medium` materialises as -`--radius: 0.625rem` (10px) in `styles.css`, mirrored by `tokens.ts` and pinned by -`src/tokens.test.ts`; `components.json` holds no radius field because the schema defines none. -Every other step derives from `--radius` in `@theme inline` (`radius-sm` at `× 0.6` through -`radius-4xl` at `× 2.6`), and no arbitrary radius appears outside that scale. +prevent. **Component geometry belongs to the component**: a control's height, padding and +internal gaps are set once in `src/components/`, not per screen, and this document does not +restate them. **Page and layout rhythm is the consumer's own composition**, on Tailwind's +ordinary spacing scale: `p-6`, `gap-4`, `space-y-8`. Dense where the content is plural — data +surfaces show more of the thing the reader came for; generous where prose and forms are read one +thing at a time. + +Radius derives from `--radius`, per *Tokens*. ## Icons -`lucide-react` is the set, and the only one. No package declares a second icon library, and -`gates/tokens.test.mjs` refuses one that reappears in a manifest or an import. -The rule is *one* set rather than one particular set — what costs a reader is two of them on a +`lucide-react` is the set, and the only one. No manifest declares a second icon library and no +source imports one; `src/gates/tokens.test.ts` refuses one that reappears in either place. The +rule is *one* set rather than one particular set — what costs a reader is two of them on a screen, where the same idea arrives at two weights and two grids. Changing which one is a -decision to make here, not a dependency to add. Icon size comes from the primitive that contains +decision to make here, not a dependency to add. Icon size comes from the component that contains the icon; a call site does not resize an icon to fit a control it did not measure. ## Motion @@ -289,19 +297,14 @@ Motion orients or confirms, and never stands between somebody and their next act **enter** animation is free to play: the surface it introduces did not exist a frame ago, so nothing is waiting on it. An **exit** animation is not, and the difference is not taste. -**A menu leaves on the frame it is dismissed.** While Radix runs an exit animation the content -stays mounted and the dismissable layer with it, so a press meant to open the next menu is read -twice — as the open, and as an interaction outside the closing surface — and the two cancel. At a -100ms exit that window covers the gap between an `Escape` and the click after it, which a fast -hand meets routinely. Canonical `dropdown-menu.tsx` animates out like any other floating -surface and the snapshot gate holds it there, so the rule is a **call-site constant**, not a -primitive edit: every `DropdownMenuContent` applies `menuSurface` -([`src/lib/menu.ts`](src/lib/menu.ts), `data-closed:animate-none! w-auto`). Both halves are one -rule — a menu that behaves like a menu — and travel together, `w-auto` freeing the surface from -canonical's trigger-width pin, which behind an icon-sized button leaves a 128px floor every -longer item wraps against. A surface whose trigger cannot be pressed again straight away — a -dialog's, a tooltip's — keeps its exit animation and never takes the constant. `TooltipProvider` -defaults `delayDuration` to `0`; a screen wanting Radix's debounce sets it on the provider. +**A menu leaves on the frame it is dismissed.** While an exit animation runs the content stays +mounted and the dismissable layer with it, so a press meant to open the next menu is read twice — +as the open, and as an interaction outside the closing surface — and the two cancel. At a 100ms +exit that window covers the gap between an `Escape` and the click after it, which a fast hand +meets routinely. `DropdownMenuContent` therefore has no exit animation, in its own base classes, +as its default. A surface whose trigger cannot be pressed again straight away — a dialog's, a +tooltip's — keeps its exit animation. `TooltipProvider` defaults `delayDuration` to `0`; a screen +wanting the debounce sets it on the provider. `prefers-reduced-motion` sits above all of this: the base layer collapses every animation and transition to a single frame under that query, so no component opts in. @@ -315,87 +318,125 @@ transition to a single frame under that query, so no component opts in. - **Focus is always visible**, per *Borders and focus*: never removed, never colour-only. - **No colour-only communication.** Status, selection, validity and provenance each carry a redundant channel. +- **A component announces its own state.** `Progress` forwards its `value` to the underlying + Radix root, so `aria-valuenow` is emitted from `` with nothing asked of + the caller. The general form of that rule is in *Why the Patch Layer Is Gone*, and it is the + sharpest argument in this document. - **Field anatomy carries the wiring.** `Field`, `FieldLabel`, `FieldDescription`, `FieldError`, `FieldGroup`, `FieldSet`, `FieldLegend`, `FieldContent`, `FieldTitle`, `FieldSeparator`: a description and an error are subcomponents, so `aria-describedby` and `aria-invalid` come from - the primitive rather than from a screen remembering. -- **Dialog and Sheet keep their official anatomy** — trigger, overlay, content, header, title, + the component rather than from a screen remembering. +- **Dialog and Sheet keep their full anatomy** — trigger, overlay, content, header, title, description, footer, close. Focus trap and return, `Escape`, and the labelled-by relationships - are Radix's guarantees, which is why no primitive here is hand-rolled from a `
`. + are Radix's guarantees, which is why nothing here is hand-rolled from a `
`. + +## Adding a Component + +A component is a file in `src/components/`, written here. There is no generator to run and +nothing to reconcile against: the work is designing the API and then holding it to the rules +above. + +1. **Take behaviour from Radix or Base UI**, and wrap it. Focus trap and return, `Escape`, arrow + movement, the `aria-*` relationships and the `data-state` attributes the variant layer keys + off are theirs to guarantee. A floating surface, a menu or a composite widget hand-rolled from + a `
` will be wrong in ways that only show up on a keyboard or a screen reader. +2. **Spell colour only through the tokens** — `bg-primary`, `text-muted-foreground`. Never a + literal in a class string, never a second name for something the vocabulary already covers. +3. **Put the geometry in the component.** Height, padding, gaps and radius are decided once, + here, on the radius scale — not passed in by a screen. +4. **Draw from `lucide-react`,** and size the icon from the component that contains it. +5. **Give it `data-slot`,** the hook a parent uses to reach a child's layout without knowing its + internals, and a `className` that merges last through `cn` so a consumer can adjust without + fighting specificity. +6. **Export it from [`src/index.ts`](src/index.ts).** The surface is listed name by name rather + than re-exported wholesale, which is what keeps the promise auditable — and is also how a + finished component can sit in the directory reaching nobody. `src/gates/design.test.ts` + compares the two and fails naming what it cannot import. +7. **Test the behaviour a screen would silently lose**, in + [`src/components/components.test.tsx`](src/components/components.test.tsx): the `className` + merge, `asChild`, the announced error, the state a consumer reads. Appearance is not what + these tests are for; the behaviour a caller depends on is. + +**Where a decision goes.** If a caller wants something the component does not offer, the answer is +a prop or a variant *in the component* — never a class string spread onto it from the call site. +The reasoning is in *Why the Patch Layer Is Gone*, and it is the same either way: if the addition +reads as a decision the caller is making, it is a prop; if it reads as a limitation, it is a +defect, and the component is right here to fix. + +## Why the Patch Layer Is Gone + +Four helper modules used to live in `src/lib/`, and they are worth a section because they are the +clearest evidence for everything above. + +| Helper | What a caller spread onto a component | Why it existed | +| --- | --- | --- | +| `menuSurface` | class overrides on every `DropdownMenuContent` | the component's base classes were not editable | +| `twoLineTrigger` | class overrides on a `SelectTrigger` | a variant could not be added to `SelectTrigger` | +| `inlineLink` | class overrides on a `Button` | a size could not be added to `Button` | +| `progressAria` | `aria-*` attributes on a `Progress` | `Progress` could not be fixed | + +**None of the four described a decision the caller was making. All four described a limitation.** +That is the tell, and it is a general one: a helper whose name answers "what did the author +decide?" is a real abstraction, and a helper whose name answers "what could the author not reach?" +is a workaround wearing an abstraction's clothes. The second kind spreads — every call site has to +know about it, no call site is reminded to, and the module's existence looks like design rather +than like a bill coming due. + +Once ownership was real, all four evaporated into the components they had been patching from +outside: + +- `menuSurface` became `DropdownMenuContent`'s base classes. A menu sizes to its items rather + than to its trigger, and does not animate on the way out. Both are now the default, so there is + nothing to remember and nothing to forget. +- `twoLineTrigger` became ``. A boolean prop, orthogonal to size, + because "let the value wrap" is a decision the caller genuinely makes. +- `inlineLink` became `); + const button = screen.getByRole("button", { name: "Read the docs" }); + expect(button.getAttribute("data-size")).toBe("inline"); + expect(button.className).toContain("h-auto"); + expect(button.className).toContain("p-0"); +}); +``` + +- [ ] **Step 2: Correr el test para verificar que falla** + +```bash +pnpm vitest run src/primitives/primitives.test.tsx -t "inline size" +``` + +Esperado: FALLA. TypeScript rechaza `size="inline"` y las clases no aparecen. + +- [ ] **Step 3: Añadir la variante de tamaño** + +En `src/primitives/button.tsx`, dentro del bloque `size` de `buttonVariants` (tras `"icon-lg"`, línea 34), añadir: + +```ts + inline: "h-auto p-0", +``` + +El comentario que justifica la variante va sobre la línea: + +```ts + // A link button is inline prose, not a boxed control: no height of its + // own and no padding, so it sits inside a sentence or a table cell. + inline: "h-auto p-0", +``` + +- [ ] **Step 4: Correr el test para verificar que pasa** + +```bash +pnpm vitest run src/primitives/primitives.test.tsx -t "inline size" +``` + +Esperado: PASA. + +- [ ] **Step 5: Migrar el test existente que usaba el helper** + +`src/primitives/primitives.test.tsx:100` renderiza hoy ``), [ - `a.tsx:1: `), [`b.tsx:1: `), - [`g.tsx:1: ` does not end the tag before the real attribute. - assert.deepEqual( - legacyVocabularyIn("g2.tsx", ``), - [`g2.tsx:1: `), []); - // Nor does a `>` quoted inside a string inside braces — the tag still - // extends to its real close, past the attribute that follows the string. - assert.deepEqual( - legacyVocabularyIn("g5.tsx", ` b"} title="Heads up">Body`), - [`g5.tsx:1: b"} title="Heads up">`], - ); - assert.deepEqual(legacyVocabularyIn("h.tsx", `Optional`), [ - `h.tsx:1: Optional`, - ]); - assert.deepEqual(legacyVocabularyIn("i.tsx", `No rows`), [ - `i.tsx:1: No rows`, - ]); - assert.deepEqual(legacyVocabularyIn("j.ts", `import { Badge } from "../primitives/Badge.js";`), [ - `j.ts:1: import { Badge } from "../primitives/Badge.js";`, - ]); - assert.deepEqual(legacyVocabularyIn("k.tsx", `import { useTheme } from "next-themes";`), [ - `k.tsx:1: import { useTheme } from "next-themes";`, - ]); - - // The shadcn contract itself must pass clean. - assert.deepEqual(legacyVocabularyIn("l.tsx", ``), []); - assert.deepEqual(legacyVocabularyIn("m.tsx", `x`), []); - assert.deepEqual(legacyVocabularyIn("n.tsx", `Draft`), []); - // The `quiet` variant is the shape itself, not the hand-rolled shape. - assert.deepEqual(legacyVocabularyIn("n2.tsx", `Chip`), []); - // Only one of the two attributes is not yet the forbidden shape. - assert.deepEqual(legacyVocabularyIn("n3.tsx", `Chip`), []); - assert.deepEqual(legacyVocabularyIn("n4.tsx", `Chip`), []); - assert.deepEqual(legacyVocabularyIn("o.ts", `import { Badge } from "../primitives/badge.js";`), []); - // A comment recalling the retired shape states history, not a usage. - assert.deepEqual(legacyVocabularyIn("p.tsx", ` // `), []); - assert.deepEqual(legacyVocabularyIn("q.tsx", `// reads next-themes for the mounted flag`), []); - - // The same vocabulary as a test reads it back, the wrapped call included — - // the shape TAG_ATTRIBUTE_RULES cannot see. - assert.deepEqual( - legacyVocabularyIn("r.ts", `await expect(go).toHaveAttribute(\n "data-variant",\n "primary",\n);`), - [`r.ts:2: "data-variant", "primary"`], - ); - assert.deepEqual(legacyVocabularyIn("r2.ts", `page.locator('[data-variant="primary"]')`), [ - `r2.ts:1: [data-variant="primary"]`, - ]); - assert.deepEqual( - legacyVocabularyIn("r3.tsx", `expect(el.getAttribute("data-variant")).toBe("accent");`), - [`r3.tsx:1: getAttribute("data-variant")).toBe("accent"`], - ); - assert.deepEqual(legacyVocabularyIn("r4.tsx", `expect(el.dataset.size).toBe("md");`), [ - `r4.tsx:1: dataset.size).toBe("md"`, - ]); - // A variant that still exists is a question about the call site, not the vocabulary. - assert.deepEqual(legacyVocabularyIn("s.ts", `toHaveAttribute("data-variant", "outline")`), []); - assert.deepEqual( - legacyVocabularyIn("s2.tsx", `expect(b.getAttribute("data-variant")).toBe("success");`), - [], - ); -}); - -test("no package source reaches for a name the extension contract retired", () => { - const tracked = packageSources(); - assert.ok(tracked.length > 0, "the scan found no package sources, so it proves nothing"); - - const offenders = tracked.flatMap((file) => legacyVocabularyIn(file, readFileSync(path.join(REPO, file), "utf8"))); - assert.deepEqual( - offenders, - [], - "a source still reaches for a name the extension contract retired:\n" + offenders.join("\n"), - ); -}); - -test("statusPaletteIn finds the emerald/amber/sky family, and not a token or a comment", () => { - assert.deepEqual(statusPaletteIn("a.tsx", ` className="bg-emerald-500/10"`), [ - `a.tsx:1: className="bg-emerald-500/10"`, - ]); - assert.deepEqual(statusPaletteIn("b.tsx", ` className="border-amber-400 dark:border-amber-300"`), [ - `b.tsx:1: className="border-amber-400 dark:border-amber-300"`, - ]); - assert.deepEqual(statusPaletteIn("c.tsx", ` className="ring-sky-500"`), [`c.tsx:1: className="ring-sky-500"`]); - assert.deepEqual(statusPaletteIn("d.tsx", ` className="shadow-emerald-500/20"`), [ - `d.tsx:1: className="shadow-emerald-500/20"`, - ]); - // A token, not the palette. - assert.deepEqual(statusPaletteIn("e.tsx", ` className="bg-primary text-primary-foreground"`), []); - // The family name without a shade is not yet a colour. - assert.deepEqual(statusPaletteIn("f.tsx", ` className="bg-emerald"`), []); - // A comment recalling the palette states history, not a usage. - assert.deepEqual(statusPaletteIn("g.tsx", ` // never bg-emerald-500 outside statusTone`), []); -}); - -test("competingStatusPaletteIn finds a rival colour family, and not the status palette itself", () => { - assert.deepEqual(competingStatusPaletteIn("a.tsx", ` className="bg-green-500"`), [ - `a.tsx:1: className="bg-green-500"`, - ]); - assert.deepEqual(competingStatusPaletteIn("b.tsx", ` className="text-yellow-700"`), [ - `b.tsx:1: className="text-yellow-700"`, - ]); - assert.deepEqual(competingStatusPaletteIn("c.tsx", ` className="border-blue-400"`), [ - `c.tsx:1: className="border-blue-400"`, - ]); - assert.deepEqual(competingStatusPaletteIn("d.tsx", ` className="bg-emerald-500"`), []); - assert.deepEqual(competingStatusPaletteIn("e.tsx", ` // never bg-red-500 for a destructive state`), []); -}); - -test("the status palette lives in exactly Badge and statusTone, nowhere else", () => { - const ALLOWED_PALETTE_FILES = [ - "src/primitives/badge.tsx", - "src/statusTone.ts", - "src/statusTone.test.ts", - ]; - const tracked = packageSources({ includeSnapshots: true }); - assert.ok(tracked.length > 0, "the scan found no package sources, so it proves nothing"); - - const offenders = tracked - .filter((file) => !ALLOWED_PALETTE_FILES.includes(file)) - .flatMap((file) => statusPaletteIn(file, readFileSync(path.join(REPO, file), "utf8"))); - assert.deepEqual( - offenders, - [], - "the status palette has exactly one home outside Badge and statusTone — read the tone from " + - `src/statusTone.ts instead:\n${offenders.join("\n")}`, - ); -}); - -test("no competing colour family stands in for the status palette anywhere in the package", () => { - const tracked = packageSources({ includeSnapshots: true }); - assert.ok(tracked.length > 0, "the scan found no package sources, so it proves nothing"); - - const offenders = tracked.flatMap((file) => - competingStatusPaletteIn(file, readFileSync(path.join(REPO, file), "utf8")), - ); - assert.deepEqual( - offenders, - [], - `a competing colour family stands in for the status palette:\n${offenders.join("\n")}`, - ); -}); - -test("statusTokenUtilitiesIn finds the retired success/warning utility, and not the emerald/amber tokens that replaced it", () => { - assert.deepEqual(statusTokenUtilitiesIn("a.tsx", ` className="bg-success"`), [`a.tsx:1: className="bg-success"`]); - assert.deepEqual(statusTokenUtilitiesIn("b.tsx", ` className="text-warning-foreground"`), [ - `b.tsx:1: className="text-warning-foreground"`, - ]); - assert.deepEqual(statusTokenUtilitiesIn("c.tsx", ` className="border-success"`), [ - `c.tsx:1: className="border-success"`, - ]); - assert.deepEqual(statusTokenUtilitiesIn("d.tsx", ` className="ring-warning"`), [ - `d.tsx:1: className="ring-warning"`, - ]); - assert.deepEqual(statusTokenUtilitiesIn("e.tsx", ` className="bg-emerald-500 text-emerald-700"`), []); - assert.deepEqual(statusTokenUtilitiesIn("f.tsx", ` className="bg-destructive"`), []); - assert.deepEqual(statusTokenUtilitiesIn("g.tsx", ` // bg-success no longer exists`), []); -}); - -test("no package source reaches for the retired success/warning token utility", () => { - const tracked = packageSources({ includeSnapshots: true }); - assert.ok(tracked.length > 0, "the scan found no package sources, so it proves nothing"); - - const offenders = tracked.flatMap((file) => - statusTokenUtilitiesIn(file, readFileSync(path.join(REPO, file), "utf8")), - ); - assert.deepEqual( - offenders, - [], - `a source reaches for the retired success/warning token utility:\n${offenders.join("\n")}`, - ); -}); - -/** - * The public surface `index.ts` promises: every canonical primitive - * re-exported, the retired pattern-layer `Combobox` gone now that it is a - * primitive, and no `*Variants` beyond the three shadcn's own `cva` calls - * produce. - */ -const INDEX_PATH = "src/index.ts"; -const PUBLIC_PRIMITIVES = [ - "badge", - "button", - "alert", - "field", - "dialog", - "sheet", - "combobox", - "input-group", - "select", - "dropdown-menu", - "tooltip", - "progress", - "skeleton", - "sonner", - "table", - "tabs", - "card", - "input", - "textarea", - "label", -]; - -test("index.ts exports every canonical primitive, drops the retired pattern Combobox, and adds no *Variants beyond shadcn's own three", () => { - const source = read(INDEX_PATH); - - for (const name of PUBLIC_PRIMITIVES) { - const exported = new RegExp(String.raw`export\s*\{[^;]*\}\s*from\s*"\./primitives/${name}\.js"`).test(source); - assert.ok(exported, `${INDEX_PATH} does not export from ./primitives/${name}.js`); - } - - assert.ok( - !source.includes('from "./patterns/Combobox.js"'), - `${INDEX_PATH} must not export the retired ./patterns/Combobox.js — Combobox is a primitive now`, - ); - - const variantsExports = [...new Set([...source.matchAll(/\b[a-zA-Z]*Variants\b/g)].map((m) => m[0]))].sort(); - assert.deepEqual( - variantsExports, - ["badgeVariants", "buttonVariants", "tabsListVariants"], - `${INDEX_PATH} exports a *Variants beyond shadcn's own three:\n${variantsExports.join(", ")}`, - ); -}); - -test("menuSurfaceGapsIn flags a DropdownMenuContent missing menuSurface, and stays silent when it carries it", () => { - assert.deepEqual( - menuSurfaceGapsIn("a.tsx", `x`), - [`a.tsx:1: `], - ); - assert.deepEqual( - menuSurfaceGapsIn( - "b.tsx", - `x`, - ), - [], - ); - assert.deepEqual( - menuSurfaceGapsIn( - "c.tsx", - `x`, - ), - [], - ); -}); - -test("every DropdownMenuContent call site outside the shadcn snapshot carries menuSurface", () => { - const tracked = packageSources(); - assert.ok(tracked.length > 0, "the scan found no package sources, so it proves nothing"); - - const offenders = tracked.flatMap((file) => - menuSurfaceGapsIn(file, readFileSync(path.join(REPO, file), "utf8")), - ); - assert.deepEqual( - offenders, - [], - `a DropdownMenuContent call site is missing lib/menu.ts's menuSurface:\n${offenders.join("\n")}`, - ); -}); diff --git a/gates/index.d.mts b/gates/index.d.mts deleted file mode 100644 index c41bed1..0000000 --- a/gates/index.d.mts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Hand-written declarations for `@robomous/ui-core/gates` — the module is - * plain ESM JavaScript (it runs under `node --test` with no build step), so - * its types are maintained here beside it. - */ - -// ---- canonical ---- -export function additiveOnly( - snapshot: string, - actual: string, -): { ok: true } | { ok: false; missing: string }; -export const FRAMEWORK_ADAPTERS: string[]; -export const ADAPTER_REMOVED_LINES: string[]; -export function withoutLines(text: string, removed: readonly string[]): string; -export function checkAdapter( - file: string, - actualText: string, -): { ok: true; isAdapter: boolean } | { ok: false; reason: string }; - -// ---- rosters and vocabulary ---- -export function variantKeys(source: string, block?: string): string[]; -export function variantClasses(source: string, key: string): string; -export const OFFICIAL_BADGE: string[]; -export const FOUNDATION_BADGE: string[]; -export const BUTTON_VARIANTS: string[]; -export const BUTTON_SIZES: string[]; -export function openTagsIn(text: string, tagName: string): { at: number; tag: string }[]; -export function legacyVocabularyIn(file: string, text: string): string[]; -export function statusPaletteIn(file: string, text: string): string[]; -export function competingStatusPaletteIn(file: string, text: string): string[]; -export function statusTokenUtilitiesIn(file: string, text: string): string[]; -export function menuSurfaceGapsIn(file: string, text: string): string[]; - -// ---- colour discipline ---- -export function colouredClassesIn(file: string, text: string): string[]; -export function brandUsagesIn( - file: string, - text: string, -): { file: string; at: number; text: string }[]; -export function retiredDeclarationsIn(text: string): string[]; - -// ---- stylesheet parsing ---- -export function normalize(value: string): string; -export function blockBody(css: string, header: string): string; -export function rawDeclarations(block: string): Map; -export function declarations(block: string): Map; -export const SEMANTIC_NAMES: string[]; - -// ---- foundation facts ---- -export function snapshotsDir(): string; -export function foundationTokenNames(): string[]; diff --git a/gates/index.mjs b/gates/index.mjs deleted file mode 100644 index 1f4c280..0000000 --- a/gates/index.mjs +++ /dev/null @@ -1,514 +0,0 @@ -/** - * @robomous/ui-core/gates — the pure helpers and foundation facts that keep - * the design system honest, published so every consumer repo can run the - * same gates over its own sources with its own extensions registry. - * Lifted from VisionSet's original repo-root gate tests. - */ -import assert from "node:assert/strict"; -import { readFileSync } from "node:fs"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -const PKG = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); - -const COMMENT = /^\s*(?:\/\/|\/\*|\*|#)/; - -// ---- canonical (from tests/scripts/shadcn_canonical.test.mjs) ---- - -const lines = (text) => text.split(/\r?\n/).map((l) => l.trimEnd()); - -// Every snapshot line must appear in the primitive, in order. Added lines are -// the only permitted difference — that is the whole of the "do not modify -// shadcn's code" rule, in a form a machine can check. -export function additiveOnly(snapshot, actual) { - const want = lines(snapshot); - const have = lines(actual); - let cursor = 0; - for (const line of want) { - const at = have.indexOf(line, cursor); - if (at === -1) return { ok: false, missing: line }; - cursor = at + 1; - } - return { ok: true }; -} - -// A primitive may replace a framework-specific hook (shadcn's Next.js -// integrations) with a thin adapter that reads the one theme source -// instead — never a shortcut for divergence in general. `FRAMEWORK_ADAPTERS` -// is the explicit allow-list; the marker comment `SHADCN FRAMEWORK ADAPTER` -// is how a primitive claims the exemption, and it must be on the list to -// claim it. `ADAPTER_REMOVED_LINES` are exactly the snapshot lines the -// adapter is permitted to drop — every other snapshot line must still appear, -// in order, same as any other primitive. -export const FRAMEWORK_ADAPTERS = ["sonner.tsx"]; -export const ADAPTER_REMOVED_LINES = ['import { useTheme } from "next-themes"', ' const { theme = "system" } = useTheme()']; - -export function withoutLines(text, removed) { - const removedTrimmed = new Set(removed.map((l) => l.trimEnd())); - return lines(text) - .filter((line) => !removedTrimmed.has(line)) - .join("\n"); -} - -// Decides which comparison a primitive gets. Returns { ok: false, reason } -// when the marker is present on a file that isn't allow-listed; otherwise -// { ok: true, isAdapter } says whether the adapter-adjusted snapshot applies. -export function checkAdapter(file, actualText) { - if (!actualText.includes("SHADCN FRAMEWORK ADAPTER")) return { ok: true, isAdapter: false }; - if (!FRAMEWORK_ADAPTERS.includes(file)) { - return { ok: false, reason: `${file} carries the SHADCN FRAMEWORK ADAPTER marker but is not in FRAMEWORK_ADAPTERS` }; - } - return { ok: true, isAdapter: true }; -} - -// ---- rosters and vocabulary (from tests/scripts/shadcn_extensions.test.mjs) ---- - -/** The variant keys of the first `variant: { … }` block in a cva source. */ -export function variantKeys(source, block = "variant") { - const start = source.indexOf(`${block}: {`); - assert.notEqual(start, -1, `no "${block}" block`); - let depth = 0, i = start + block.length + 3; - const begin = i; - for (; i < source.length; i++) { - if (source[i] === "{") depth++; - else if (source[i] === "}") { if (depth === 0) break; depth--; } - } - const body = source.slice(begin, i); - return [...body.matchAll(/(?:^|,)\s*"?([a-z][a-z0-9-]*)"?:\s/g)].map((m) => m[1]); -} - -/** The class string of one variant line in a cva source. */ -export function variantClasses(source, key) { - const m = source.match(new RegExp(String.raw`^\s*"?${key}"?:\s*\n?\s*"([^"]*)"`, "m")); - assert.ok(m, `no variant ${key}`); - return m[1]; -} - -export const OFFICIAL_BADGE = ["default", "secondary", "destructive", "outline", "ghost", "link"]; -export const FOUNDATION_BADGE = ["success", "warning", "info", "quiet"]; -export const BUTTON_VARIANTS = ["default", "outline", "secondary", "ghost", "destructive", "link"]; -export const BUTTON_SIZES = ["default", "xs", "sm", "lg", "icon", "icon-xs", "icon-sm", "icon-lg"]; - -/** - * v1's vocabulary the extension contract retired: prop names and shapes no - * shadcn primitive carries, and the PascalCase import paths and framework - * package v1 read them from. - * - * Every forbidden token is assembled from fragments — the trick - * `RETIRED_DECLARATIONS` below uses too — so none of them sits contiguously - * anywhere in this file's own source. - */ -const NEXT_THEMES = ["next", "themes"].join("-"); -const FIELD_HINT = ["Field", "Hint"].join(""); -const TABLE_EMPTY = ["Table", "Empty"].join(""); -const LEGACY_PRIMITIVES = [ - "Badge", - "Button", - "Card", - "Combobox", - "Dialog", - "Feedback", - "Input", - "Menu", - "Select", - "Table", - "Tabs", -]; - -/** - * `Badge`'s `quiet` variant is the shape for a soft, borderless status chip — - * an `outline` Badge hand-rounded with a `rounded-*` utility on `className` - * is that same shape built by hand, so it is forbidden wherever both - * attributes land on the one tag (in either order). - */ -const BADGE_OUTLINE_ROUNDED = /(?=[\s\S]*\bvariant="outline")(?=[\s\S]*\bclassName="[^"]*\brounded-)/; - -const TAG_ATTRIBUTE_RULES = [ - { tag: "Button", attribute: /variant="(?:primary|success)"|size="(?:md)"/ }, - { tag: "Badge", attribute: /variant="(?:neutral|accent)"/ }, - { tag: "Badge", attribute: BADGE_OUTLINE_ROUNDED }, - { tag: "Progress", attribute: /\bvariant=/ }, - { tag: "SelectItem", attribute: /\bmeta=/ }, - { tag: "Alert", attribute: /\btitle=/ }, -]; -const LEGACY_IMPORT = new RegExp( - String.raw`from\s+["'][^"']*/primitives/(?:${LEGACY_PRIMITIVES.join("|")})(?:\.js)?["']`, -); -const NEXT_THEMES_IMPORT = new RegExp(String.raw`from\s+["']${NEXT_THEMES}["']`); -const BARE_LEGACY_NAME = new RegExp(String.raw`\b(?:${FIELD_HINT}|${TABLE_EMPTY})\b`); - -/** - * The same retired names, written the way a test reads them back off the DOM - * rather than the way a component spells them. `TAG_ATTRIBUTE_RULES` walks JSX - * opening tags only, so a spec asserting v1's vocabulary sails straight past - * it — which is how `cycle.spec.ts` kept `"primary"` through the whole - * realignment, with CI the only thing that ever caught it. - * - * Listed here are the values **no** primitive carries any more, so naming one - * is wrong wherever it lands: `primary` belongs to no variant block at all, - * `Badge` dropped `neutral` and `accent`, and `Button`'s `md` is the official - * `default`. `secondary` and `success` are deliberately absent — both are - * still real variants, so an assertion naming one asks a question about its - * own call site that no vocabulary sweep can answer. - */ -const RETIRED_ATTRIBUTE_VALUES = { variant: ["primary", "neutral", "accent"], size: ["md"] }; - -/** - * Three shapes per attribute, because a spec has three ways to say the one - * thing: `toHaveAttribute("data-variant", "primary")`, a - * `[data-variant="primary"]` selector, and `getAttribute("data-variant")` or - * `dataset.variant` compared with `toBe`/`toEqual`. The first crosses newlines - * on purpose — prettier puts the two arguments on their own lines once the - * call runs long, and that wrapped form is exactly the one that got through. - */ -const RETIRED_ATTRIBUTE_ASSERTIONS = Object.entries(RETIRED_ATTRIBUTE_VALUES).flatMap( - ([name, values]) => { - const value = `(?:${values.join("|")})`; - return [ - new RegExp(String.raw`"data-${name}"\s*,\s*"${value}"`, "g"), - new RegExp(String.raw`\[data-${name}="${value}"\]`, "g"), - new RegExp( - String.raw`(?:getAttribute\("data-${name}"\)|dataset\.${name})[^\n]*?\.to(?:Be|Equal)\(\s*"${value}"`, - "g", - ), - ]; - }, -); - -/** The 1-based line of `text` that character offset `index` falls on. */ -function lineAt(text, index) { - return text.slice(0, index).split("\n").length; -} - -/** - * The index just past a JSX opening tag's real closing `>`, starting the scan - * at `start` (the tag's own `<`). A `[^>]*?` regex is fooled by any literal - * `>` — an arrow function, a `count > 0` comparison, a `>` inside a quoted - * string — so this instead walks the text tracking `{}` depth (a `>` only - * ends the tag at depth zero) and skips over `"…"`/`'…'`/`` `…` `` bodies - * wholesale, wherever they appear, so a `>` quoted inside one is never read - * as the tag's own. - */ -function openTagEnd(text, start) { - let depth = 0; - for (let i = start; i < text.length; i++) { - const ch = text[i]; - if (ch === '"' || ch === "'" || ch === "`") { - const quote = ch; - i++; - while (i < text.length && text[i] !== quote) { - if (text[i] === "\\") i++; - i++; - } - continue; - } - if (ch === "{") depth++; - else if (ch === "}") depth--; - else if (ch === ">" && depth === 0) return i + 1; - } - return text.length; -} - -/** Every `` opening tag in `text`, as `{ at, tag }` in source order. */ -export function openTagsIn(text, tagName) { - const starts = new RegExp(String.raw`<${tagName}\b`, "g"); - return [...text.matchAll(starts)].map((m) => ({ - at: m.index, - tag: text.slice(m.index, openTagEnd(text, m.index)), - })); -} - -/** Every `file:line` in `text` reaching for a name the extension contract retired. */ -export function legacyVocabularyIn(file, text) { - const scrubbed = text - .split("\n") - .map((line) => (COMMENT.test(line) ? "" : line)) - .join("\n"); - const hits = []; - - for (const { tag, attribute } of TAG_ATTRIBUTE_RULES) { - for (const { at, tag: tagText } of openTagsIn(scrubbed, tag)) { - if (attribute.test(tagText)) hits.push({ at: lineAt(scrubbed, at), text: tagText.split("\n")[0].trim() }); - } - } - - for (const matcher of RETIRED_ATTRIBUTE_ASSERTIONS) { - for (const { 0: found, index } of scrubbed.matchAll(matcher)) { - hits.push({ at: lineAt(scrubbed, index), text: found.replace(/\s+/g, " ").trim() }); - } - } - - scrubbed.split("\n").forEach((line, index) => { - if (BARE_LEGACY_NAME.test(line) || LEGACY_IMPORT.test(line) || NEXT_THEMES_IMPORT.test(line)) { - hits.push({ at: index + 1, text: line.trim() }); - } - }); - - return hits - .sort((a, b) => a.at - b.at) - .map(({ at, text: t }) => `${file}:${at}: ${t}`); -} - -/** - * The status palette's one Tailwind family: emerald/amber/sky, across every - * prefix that can carry a colour. `Badge` and `statusTone.ts` are its one - * home — see `statusTone.ts`'s own docstring — so a third place naming the - * family is a fork of the palette, not a use of it. - */ -const STATUS_PALETTE = /\b(?:bg|text|border|ring|fill|stroke|from|to|via|outline|decoration|shadow)-(?:emerald|amber|sky)-\d/; - -/** Every `file:line` in `text` painting with the status palette, outside a comment. */ -export function statusPaletteIn(file, text) { - return text - .split("\n") - .map((line, index) => ({ line, at: index + 1 })) - .filter(({ line }) => !COMMENT.test(line) && STATUS_PALETTE.test(line)) - .map(({ line, at }) => `${file}:${at}: ${line.trim()}`); -} - -/** - * A colour family that competes with the status palette for the same job — - * "a warning", "a success" — and so could stand in for it undetected. Unlike - * the palette itself these have no allowed home anywhere in a consumer. - */ -const COMPETING_PALETTE = /\b(?:bg|text|border)-(?:green|lime|teal|yellow|orange|blue|cyan|red)-\d/; - -/** Every `file:line` in `text` reaching for a colour family that competes with the status palette. */ -export function competingStatusPaletteIn(file, text) { - return text - .split("\n") - .map((line, index) => ({ line, at: index + 1 })) - .filter(({ line }) => !COMMENT.test(line) && COMPETING_PALETTE.test(line)) - .map(({ line, at }) => `${file}:${at}: ${line.trim()}`); -} - -/** - * `--success`/`--warning` and their `-foreground` companions are retired - * declarations (`retiredDeclarationsIn` guards the stylesheet itself); this is - * the other half — no consumer may reach for the *utility* either, on any of - * the four prefixes that could carry one. - */ -const STATUS_TOKEN_UTILITY = /\b(?:bg|text|border|ring)-(?:success|warning)(?:-foreground)?\b/; - -/** Every `file:line` in `text` reaching for the retired status token utility, outside a comment. */ -export function statusTokenUtilitiesIn(file, text) { - return text - .split("\n") - .map((line, index) => ({ line, at: index + 1 })) - .filter(({ line }) => !COMMENT.test(line) && STATUS_TOKEN_UTILITY.test(line)) - .map(({ line, at }) => `${file}:${at}: ${line.trim()}`); -} - -/** - * Every `DropdownMenuContent` call site owns `lib/menu.ts`'s `menuSurface` — - * the constant that carries the canonical surface classes plus the exit- - * animation and sizing fixes documented there. A call site that drops it - * silently reverts to the bare Radix surface. - */ - -/** Every `file:line` in `text` opening a `DropdownMenuContent` without `menuSurface` on it. */ -export function menuSurfaceGapsIn(file, text) { - return openTagsIn(text, "DropdownMenuContent") - .filter(({ tag }) => !tag.includes("menuSurface")) - .map(({ at, tag }) => `${file}:${lineAt(text, at)}: ${tag.split("\n")[0].trim()}`); -} - -// ---- colour discipline (from tests/scripts/design_tokens.test.mjs) ---- - -/** - * A Tailwind arbitrary value whose content is a colour. - * - * Assembled from fragments so this file does not match itself. The `-` before - * the bracket is what makes it a *utility* rather than an array index or a - * TypeScript tuple type. - */ -const HEX = ["#", "[0-9a-fA-F]{3,8}"].join(""); -const LITERAL = String.raw`(?:${HEX}|rgba?\(|hsla?\(|oklch\()`; -// A raw colour right inside the bracket… -const ARBITRARY_COLOUR = new RegExp(String.raw`-\[\s*(?:${LITERAL}|var\(\s*--)`); -// The one colour-mix the preset writes mixes tokens only: -// `color-mix(in_oklch,var(--secondary),var(--foreground)_5%)`. Any other -// argument shape — a literal, a named colour, a bare number — is a colour. -const TOKEN_MIX = String.raw`color-mix\(in_[a-z0-9-]+(?:,var\(--[a-z0-9-]+\)(?:_\d+(?:\.\d+)?%)?)+\)`; -const BRACKET_MIX = new RegExp(String.raw`-\[\s*color-mix\([^\]]*\]`); -const ALLOWED_MIX = new RegExp(String.raw`-\[\s*${TOKEN_MIX}\]`); - -/** Every `file:line` in `text` that puts a colour inside a Tailwind class. */ -export function colouredClassesIn(file, text) { - return text - .split("\n") - .map((line, index) => ({ line, at: index + 1 })) - .filter( - ({ line }) => - !COMMENT.test(line) && - (ARBITRARY_COLOUR.test(line) || (BRACKET_MIX.test(line) && !ALLOWED_MIX.test(line))), - ) - .map(({ line, at }) => `${file}:${at}: ${line.trim()}`); -} - -/** - * `DESIGN.md` "Where the brand is": coral is identity, not a functional-UI - * colour — the wordmark and the styleguide swatch that shows it off, nothing - * a person acts on. This is not a headcount: the gate does not exist to hold - * a count of sites, it exists so brand can never migrate onto a control (a - * button, a progress fill, anything with a function) instead of staying the - * one place it is allowed to just be seen. A pure function over one file's - * text, so the gate is provable with fabricated input, and `COMMENT` keeps - * the styles.css line that *states* the rule from counting as a usage of it. - */ -const BRAND_UTILITY = /\b(?:bg|text|border|ring|fill|stroke)-brand\b/; - -/** Every line in `text` that paints with the brand colour. */ -export function brandUsagesIn(file, text) { - return text - .split("\n") - .map((line, index) => ({ line, at: index + 1 })) - .filter(({ line }) => !COMMENT.test(line) && BRAND_UTILITY.test(line)) - .map(({ line, at }) => ({ file, at, text: line.trim() })); -} - -/** - * The names the original audit retired outright — no shadcn analogue, no - * extension, no idiom left to fall back to. `tokens.test.ts` already guards - * this structurally, parsed against `styles.css`'s own `:root`/`.dark` - * blocks; this is the same guard by a different method, on purpose — a - * plain-text scan that keeps working even if that vitest suite is ever - * refactored or its parser changes shape. - * - * Assembled from fragments — the trick `HEX` above already uses — so none of - * these names is a contiguous string anywhere in this file's own source, and - * a repo-wide sweep for one of them never mistakes this guard for a - * lingering usage. - */ -const dash = (...parts) => parts.join("-"); -const RETIRED_DECLARATIONS = [ - dash("--color", "primary", "hover"), - dash("--color", "disabled"), - dash("--color", "disabled", "foreground"), - dash("--color", "success", "hover"), - dash("--color", "destructive", "foreground"), - dash("--color", "sidebar", "strong"), - dash("--color", "sidebar", "muted"), - dash("--text", "meta"), - dash("--text", "body"), - dash("--text", "section"), - dash("--text", "page"), - dash("--spacing", "sidebar", "mobile"), - dash("--success"), - dash("--success", "foreground"), - dash("--warning"), - dash("--warning", "foreground"), - dash("--color", "success"), - dash("--color", "success", "foreground"), - dash("--color", "warning"), - dash("--color", "warning", "foreground"), -]; - -/** Every retired name in `text` declared as a custom property, not merely mentioned. */ -export function retiredDeclarationsIn(text) { - return text - .split("\n") - .map((line, index) => ({ line, at: index + 1 })) - .filter(({ line }) => !COMMENT.test(line)) - .flatMap(({ line, at }) => - RETIRED_DECLARATIONS.filter((name) => line.includes(`${name}:`)).map((name) => `${at}: ${name}`), - ); -} - -// ---- stylesheet parsing (from src/tokens.test.ts, translated TS→JS) ---- - -/** Whitespace is presentation; a value that wraps is the same value. */ -export function normalize(value) { - return value.replace(/\s+/g, " ").trim(); -} - -/** - * The `{ … }` body belonging to the block whose header (e.g. `:root {` or - * `.dark {`) appears first in the file, matched by brace depth rather than by - * the next `\n}` so a nested `calc(...)` or `color-mix(...)` cannot fool it. - */ -export function blockBody(css, header) { - const headerAt = css.indexOf(header); - if (headerAt === -1) throw new Error(`stylesheet has no ${JSON.stringify(header)} block`); - const braceAt = css.indexOf("{", headerAt); - let depth = 1; - let i = braceAt + 1; - while (depth > 0 && i < css.length) { - if (css[i] === "{") depth++; - else if (css[i] === "}") depth--; - i++; - } - return css.slice(braceAt + 1, i - 1); -} - -/** Every `--name: value;` declaration in a block, keyed WITH the leading `--`. */ -export function rawDeclarations(block) { - const map = new Map(); - for (const match of block.matchAll(/(--[\w-]+):\s*([^;]+);/g)) { - map.set(match[1], normalize(match[2])); - } - return map; -} - -/** The same, keyed WITHOUT the leading `--` — the shape `LIGHT_THEME` uses. */ -export function declarations(block) { - const map = new Map(); - for (const [name, value] of rawDeclarations(block)) { - map.set(name.slice(2), value); - } - return map; -} - -// The shadcn standard vocabulary, exactly as the CLI scratch emitted it — -// not derived from `tokens.ts`, so a mistake in the mirror cannot also -// erase the thing it was supposed to mirror. -export const SEMANTIC_NAMES = [ - "background", - "foreground", - "card", - "card-foreground", - "popover", - "popover-foreground", - "primary", - "primary-foreground", - "secondary", - "secondary-foreground", - "muted", - "muted-foreground", - "accent", - "accent-foreground", - "destructive", - "border", - "input", - "ring", - "chart-1", - "chart-2", - "chart-3", - "chart-4", - "chart-5", - "sidebar", - "sidebar-foreground", - "sidebar-primary", - "sidebar-primary-foreground", - "sidebar-accent", - "sidebar-accent-foreground", - "sidebar-border", - "sidebar-ring", -]; - -// ---- foundation facts ---- - -/** Absolute path to the canonical shadcn snapshots shipped in this package. */ -export function snapshotsDir() { - return path.join(PKG, "shadcn"); -} - -/** - * The foundation token names, read off the shipped stylesheet's `:root` so - * they can never drift from what actually runs. `radius` is a geometry - * setting, not a colour token, and is excluded — it matches LIGHT_THEME's keys. - */ -export function foundationTokenNames() { - const css = readFileSync(path.join(PKG, "src/styles.css"), "utf8"); - return [...declarations(blockBody(css, ":root {")).keys()].filter((n) => n !== "radius"); -} diff --git a/gates/tokens.test.mjs b/gates/tokens.test.mjs deleted file mode 100644 index 8d20344..0000000 --- a/gates/tokens.test.mjs +++ /dev/null @@ -1,276 +0,0 @@ -/** - * `DESIGN.md`'s first principle, machine-enforced: **never a colour in a class - * string.** The helpers live in `./index.mjs` so consumers run the same scans; - * this file keeps their fabricated-input self-tests and runs the repo gates - * over this package's own sources. - */ - -import assert from "node:assert/strict"; -import { readFileSync } from "node:fs"; -import path from "node:path"; -import { spawnSync } from "node:child_process"; -import { fileURLToPath } from "node:url"; -import { test } from "node:test"; - -import { brandUsagesIn, colouredClassesIn, retiredDeclarationsIn } from "./index.mjs"; - -const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); - -const SOURCE = /\.(?:ts|tsx|css)$/; - -// Fragments for fabricating violating input without this file matching itself. -const HEX = ["#", "[0-9a-fA-F]{3,8}"].join(""); -const dash = (...parts) => parts.join("-"); - -test("the scan finds a colour smuggled into a class, and nothing that merely looks like one", () => { - assert.deepEqual(colouredClassesIn("a.tsx", `
`), [ - `a.tsx:1:
`, - ]); - assert.deepEqual(colouredClassesIn("b.tsx", ` className="text-[var(--accent)]"`), [ - `b.tsx:1: className="text-[var(--accent)]"`, - ]); - assert.deepEqual(colouredClassesIn("c.tsx", ` className="ring-[rgb(0 0 0)]"`), [ - `c.tsx:1: className="ring-[rgb(0 0 0)]"`, - ]); - - // A token utility is the whole point of the rule and must pass. - assert.deepEqual(colouredClassesIn("d.tsx", ` className="bg-primary text-primary-foreground"`), []); - // The accent at 10% is a token with an opacity modifier, not a colour. - assert.deepEqual(colouredClassesIn("e.tsx", ` className="bg-primary/10 border-primary"`), []); - // An arbitrary value that is *not* a colour stays legal — the rule is about - // colour, and a one-off `top-[50%]` is not what v1 got wrong. - assert.deepEqual(colouredClassesIn("f.tsx", ` className="translate-y-[3px]"`), []); - // An inline style carrying a schema-supplied colour is the sanctioned road: - // `classColor` answers with whatever the kernel stored, and Tailwind has never - // seen it, so no utility could name it. - assert.deepEqual( - colouredClassesIn("g.tsx", ` style={{ background: classColor(declared, name) }}`), - [], - ); - // A docstring explaining the rule must pass, or the gate forbids its own - // explanation — the mistake a boundary scan makes when it matches its own prose. - assert.deepEqual(colouredClassesIn("h.tsx", ` * Never write \`bg-[${"#"}eb5a47]\`.`), []); - // And a CSS custom property *declaration* is where colours are supposed to live. - assert.deepEqual(colouredClassesIn("i.css", ` --color-primary: #eb5a47;`), []); - // A colour-mix of two tokens names no colour of its own — the preset's own - // Button hover step. - assert.deepEqual(colouredClassesIn("x.tsx", 'className="hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)]"'), []); - // A colour-mix that mixes in a literal is still a colour smuggled into a class. - assert.equal(colouredClassesIn("x.tsx", 'className="bg-[color-mix(in_srgb,#fff,var(--x))]"').length, 1); - // A named CSS colour inside color-mix is still a colour. - assert.equal(colouredClassesIn("x.tsx", 'className="bg-[color-mix(in_oklch,red,var(--x))]"').length, 1); - // Whatever order the tokens come in, and whichever colour space, two tokens - // stay two tokens. - assert.equal(colouredClassesIn("x.tsx", 'className="bg-[color-mix(in_srgb,var(--a)_40%,var(--b))]"').length, 0); -}); - -test("the brand scan counts a usage, and not the comment that states the rule", () => { - // A utility usage on any of the six colour-bearing prefixes is counted. - assert.deepEqual(brandUsagesIn("a.tsx", ` Robomous`), [ - { file: "a.tsx", at: 1, text: `Robomous` }, - ]); - assert.deepEqual(brandUsagesIn("b.tsx", ` className="h-full bg-brand transition-transform"`), [ - { file: "b.tsx", at: 1, text: `className="h-full bg-brand transition-transform"` }, - ]); - // An opacity modifier is still a usage of the brand colour. - assert.deepEqual( - brandUsagesIn("c.tsx", ` className="bg-brand/10"`).map((u) => u.at), - [1], - ); - // A comment line states the rule rather than applying it. - assert.deepEqual(brandUsagesIn("d.css", ` * a third \`bg-brand\` is a design decision`), []); - assert.deepEqual(brandUsagesIn("e.tsx", ` // never add bg-brand here`), []); - // Another token on the same prefixes is not the brand. - assert.deepEqual(brandUsagesIn("f.tsx", ` className="bg-primary text-primary-foreground"`), []); - // The token *name* without a utility prefix is not a usage — tokens.test.ts - // asserts LIGHT_THEME.brand's value and must not trip the gate. - assert.deepEqual(brandUsagesIn("g.ts", ` expect(COLOR.brand).toBe("#e85d44");`), []); -}); - -/** Every tracked source in this repo. */ -function repoSources() { - const listed = spawnSync("git", ["ls-files", "-z"], { cwd: REPO, encoding: "utf8" }); - assert.equal(listed.status, 0, `git ls-files failed: ${listed.stderr}`); - return listed.stdout.split("\0").filter((name) => SOURCE.test(name)); -} - -test("no source puts a colour inside a class name", () => { - const tracked = repoSources(); - assert.ok(tracked.length > 0, "the scan found no sources, so it proves nothing"); - - const offenders = tracked.flatMap((file) => - colouredClassesIn(file, readFileSync(path.join(REPO, file), "utf8")), - ); - assert.deepEqual( - offenders, - [], - "colour belongs to the token contract — add a token to " + - `src/styles.css and name the intent:\n${offenders.join("\n")}`, - ); -}); - -/** - * This package has zero brand sites: it declares the `--brand` token and never - * paints with it. `DESIGN.md` "Where the brand is": brand sites are a consumer - * decision — two enumerated sites per consuming app (the wordmark and the - * styleguide swatch that displays the value), gated in the consumer's repo. - */ -const BRAND_SITES = []; - -test("the brand colour paints nothing here — brand sites are a consumer decision", () => { - const tracked = repoSources(); - assert.ok(tracked.length > 0, "the scan found no sources, so it proves nothing"); - - const usages = tracked.flatMap((file) => - brandUsagesIn(file, readFileSync(path.join(REPO, file), "utf8")), - ); - assert.deepEqual( - usages.map((u) => u.file).sort(), - BRAND_SITES, - "DESIGN.md 'Where the brand is': this package declares the brand token and never uses it — " + - "a brand-coloured site belongs to a consuming app (two enumerated sites per app):\n" + - usages.map((u) => `${u.file}:${u.at}: ${u.text}`).join("\n"), - ); -}); - -test("the scan finds a retired declaration, and not a comment or a longer name that merely contains it", () => { - assert.deepEqual(retiredDeclarationsIn(` ${dash("--color", "disabled")}: oklch(0.9 0 0);`), [ - `1: ${dash("--color", "disabled")}`, - ]); - assert.deepEqual(retiredDeclarationsIn(` ${dash("--text", "meta")}: 0.75rem;`), [ - `1: ${dash("--text", "meta")}`, - ]); - // A comment recalling the retired name states history, not a declaration. - assert.deepEqual( - retiredDeclarationsIn(` /* ${dash("--color", "primary", "hover")} no longer exists */`), - [], - ); - // A name that merely starts with a retired one is a different declaration — - // disabled-foreground is its own retired entry, and its presence must not - // be double-counted as disabled's. - assert.deepEqual(retiredDeclarationsIn(` ${dash("--color", "disabled", "foreground")}: red;`), [ - `1: ${dash("--color", "disabled", "foreground")}`, - ]); - // A current, kept token is not a retired one. - assert.deepEqual(retiredDeclarationsIn(` --brand: white;`), []); - // success/warning retired alongside the forked primitives that needed them — - // no longer a kept extension, so this now reports a hit. - assert.deepEqual(retiredDeclarationsIn(` --success-foreground: white;`), [ - `1: ${dash("--success", "foreground")}`, - ]); -}); - -test("the retired foundation vocabulary is absent from the stylesheet", () => { - const STYLES_PATH = "src/styles.css"; - const stylesheet = readFileSync(path.join(REPO, STYLES_PATH), "utf8"); - const present = retiredDeclarationsIn(stylesheet); - assert.deepEqual( - present, - [], - "styles.css still declares a name the original audit retired — " + - `it has no shadcn analogue and no extension:\n${present.join("\n")}`, - ); -}); - -/** - * `components.json` carries the preset properties shadcn's own tools read, and - * only those: the fields its config schema defines. The schema is **strict** — - * `rawConfigSchema.safeParse` answers `unrecognized_keys` for anything else — so - * a decoded preset property the schema has no field for cannot be added here - * even as documentation. It would not be ignored; it would break every `shadcn` - * invocation that reads the file. - * - * `radius` is the property that keeps inviting the mistake: the preset decodes to - * `radius: medium`, and the obvious repair for "the config does not say so" is to - * write it in. The medium step's one home is `styles.css`'s `--radius: 0.625rem` - * (asserted by `tokens.test.ts`); this test is the other half, refusing the field - * that would look like a second home while doing nothing. Keys rather than a - * count, so a failure names what moved. - */ -const CONFIG_PATH = "components.json"; -const SCHEMA_SUPPORTED_KEYS = [ - "$schema", - "aliases", - "iconLibrary", - "menuAccent", - "menuColor", - "registries", - "rsc", - "rtl", - "style", - "tailwind", - "tsx", -]; - -test("components.json holds the schema-supported preset fields, and no others", () => { - const config = JSON.parse(readFileSync(path.join(REPO, CONFIG_PATH), "utf8")); - assert.deepEqual( - Object.keys(config).sort(), - SCHEMA_SUPPORTED_KEYS, - `${CONFIG_PATH} must carry exactly the fields shadcn's strict config schema defines. ` + - "A decoded preset property with no field here belongs in src/styles.css " + - "as a value — see DESIGN.md 'Source of Truth'", - ); - - // The preset's own values, where the schema does have a field for them. - assert.equal(config.style, "radix-nova"); - assert.equal(config.iconLibrary, "lucide"); - assert.equal(config.menuColor, "default"); - assert.equal(config.menuAccent, "subtle"); - assert.equal(config.tailwind.baseColor, "neutral"); - assert.equal(config.tailwind.css, "src/styles.css"); -}); - -/** - * Lucide is the icon set, and the only one. - * - * The rule is "one icon library", not "this particular library". So this - * guards whichever set is currently *not* in use, and the value below is the - * whole of what changes when that decision changes. - * - * Assembled from fragments so this file never holds the package's name as a - * contiguous string, and a repository-wide sweep for it never mistakes its own - * guard for a lingering usage. - */ -const RETIRED_ICON_PACKAGE = ["@tabler", "icons-react"].join("/"); - -test("no package declares a second icon set, and no source imports one", () => { - const listed = spawnSync("git", ["ls-files", "-z"], { cwd: REPO, encoding: "utf8" }); - assert.equal(listed.status, 0, `git ls-files failed: ${listed.stderr}`); - const tracked = listed.stdout.split("\0").filter(Boolean); - - const manifests = tracked.filter((name) => /(?:^|\/)package\.json$/.test(name)); - assert.ok(manifests.length > 0, "no manifests were read, so this proves nothing"); - const declaring = manifests.filter((name) => - readFileSync(path.join(REPO, name), "utf8").includes(`"${RETIRED_ICON_PACKAGE}"`), - ); - assert.deepEqual( - declaring, - [], - `this package draws one icon set, and ${RETIRED_ICON_PACKAGE} is not it. ` + - `A second one is a decision for DESIGN.md, not a dependency:\n${declaring.join("\n")}`, - ); - - const sources = tracked.filter((name) => SOURCE.test(name)); - assert.ok(sources.length > 0, "no sources were read, so this proves nothing"); - const importing = sources.filter((name) => - new RegExp(String.raw`(?:from|require\()\s*["']${RETIRED_ICON_PACKAGE}["']`).test( - readFileSync(path.join(REPO, name), "utf8"), - ), - ); - assert.deepEqual(importing, [], `these draw from the retired icon set:\n${importing.join("\n")}`); -}); - -test("the tokens have exactly one home, and it is the stylesheet", () => { - const listed = spawnSync("git", ["ls-files", "-z"], { cwd: REPO, encoding: "utf8" }); - assert.equal(listed.status, 0, `git ls-files failed: ${listed.stderr}`); - const configs = listed.stdout - .split("\0") - .filter((name) => /(?:^|\/)tailwind\.config\.[cm]?[jt]s$/.test(name)); - assert.deepEqual( - configs, - [], - "Tailwind v4 is CSS-first: the tokens live in src/styles.css. " + - `A config file gives them a second definition that wins for some utilities and not others:\n${configs.join("\n")}`, - ); -}); diff --git a/package.json b/package.json index d228591..14bb24c 100644 --- a/package.json +++ b/package.json @@ -1,36 +1,46 @@ { "name": "@robomous/ui-core", - "version": "0.1.1", - "description": "Robomous design system: shadcn Nova primitives under Radix behaviour, design tokens, and the gates that keep them canonical. Extracted from Robomous/VisionSet.", + "version": "0.2.0", + "description": "Robomous design system: twenty-one owned React components, the design tokens they resolve through, and the gates that hold the rules behind them.", "license": "Apache-2.0", - "repository": { "type": "git", "url": "https://github.com/Robomous/ui-core.git" }, + "repository": { + "type": "git", + "url": "https://github.com/Robomous/ui-core.git" + }, "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { - ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" }, - "./styles.css": "./src/styles.css", - "./gates": { "types": "./gates/index.d.mts", "import": "./gates/index.mjs" } + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./styles.css": "./src/theme/styles.css", + "./gates": { + "types": "./dist/gates/index.d.ts", + "import": "./dist/gates/index.js" + } }, - "files": ["dist", "src", "gates", "shadcn", "components.json"], + "files": [ + "dist", + "src" + ], "scripts": { "build": "tsc -p tsconfig.build.json", "test": "vitest run", - "test:gates": "node --test gates/*.test.mjs", "typecheck": "tsc -p tsconfig.json --noEmit", "lint": "eslint src && pnpm run typecheck", - "shadcn:add": "bash scripts/shadcn_add.sh" + "format": "prettier --write .", + "format:check": "prettier --check ." }, "dependencies": { "@base-ui/react": "^1.7.0", "@fontsource-variable/geist": "^5.3.0", "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", + "cn": "^0.2.4", "lucide-react": "^1.32.0", "radix-ui": "^1.6.7", - "shadcn": "^4.19.0", "sonner": "^2.0.8", - "tailwind-merge": "^3.6.0", "tw-animate-css": "^1.4.0" }, "peerDependencies": { @@ -50,6 +60,7 @@ "eslint": "^10.9.0", "eslint-plugin-react-hooks": "^7.1.1", "jsdom": "^30.0.1", + "prettier": "^3.9.6", "react": "^19.2.8", "react-dom": "^19.2.8", "tailwindcss": "^4.3.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bd457ef..397f622 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,24 +17,18 @@ importers: class-variance-authority: specifier: ^0.7.1 version: 0.7.1 - clsx: - specifier: ^2.1.1 - version: 2.1.1 + cn: + specifier: ^0.2.4 + version: 0.2.5 lucide-react: specifier: ^1.32.0 version: 1.37.0(react@19.2.8) radix-ui: specifier: ^1.6.7 version: 1.6.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - shadcn: - specifier: ^4.19.0 - version: 4.19.0(typescript@6.0.3) sonner: specifier: ^2.0.8 version: 2.0.8(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - tailwind-merge: - specifier: ^3.6.0 - version: 3.6.0 tw-animate-css: specifier: ^1.4.0 version: 1.4.0 @@ -72,6 +66,9 @@ importers: jsdom: specifier: ^30.0.1 version: 30.0.1 + prettier: + specifier: ^3.9.6 + version: 3.9.6 react: specifier: ^19.2.8 version: 19.2.8 @@ -117,28 +114,14 @@ packages: resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.29.7': - resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} - engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.29.7': resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.29.7': - resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-globals@7.29.7': resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} - '@babel/helper-member-expression-to-functions@7.29.7': - resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} - engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.29.7': resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} engines: {node: '>=6.9.0'} @@ -149,24 +132,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-optimise-call-expression@7.29.7': - resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} - engines: {node: '>=6.9.0'} - - '@babel/helper-plugin-utils@7.29.7': - resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-replace-supers@7.29.7': - resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-skip-transparent-expression-wrappers@7.29.7': - resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.29.7': resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} @@ -188,36 +153,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-syntax-jsx@7.29.7': - resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.29.7': - resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-modules-commonjs@7.29.7': - resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-typescript@7.29.7': - resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/preset-typescript@7.29.7': - resolution: {integrity: sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/runtime@7.29.7': resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} @@ -301,13 +236,6 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} - '@dotenvx/dotenvx@1.75.1': - resolution: {integrity: sha512-/BITOC9dmS/edY2zQwZNicQ059O6RKabtQfyEafV0nGtfYRNHYy1DIPiYVcov40+tob9hfmBnbR963dS+EQ1DQ==} - hasBin: true - - '@dotenvx/primitives@0.8.0': - resolution: {integrity: sha512-VYJy0uhFm9zTJ1TxBaW/pA8bjbOM/OttaNMwZ1RHG4JKyRG7DhSdiqD1ipQoAyoD22olUtxbP78W9xY3Wd11bg==} - '@eslint-community/eslint-utils@4.10.1': resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -374,12 +302,6 @@ packages: '@fontsource-variable/geist@5.3.0': resolution: {integrity: sha512-j0m+vLQuG5XAYoHtGCVu0spvlGreR3EzpECUVzkFmI1mTVnAO38l/NEPDCFgZ177JxzYJCLSmTQibIiYPilGrA==} - '@hono/node-server@2.1.1': - resolution: {integrity: sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==} - engines: {node: '>=20'} - peerDependencies: - hono: ^4 - '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -416,28 +338,6 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@modelcontextprotocol/sdk@1.30.0': - resolution: {integrity: sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==} - engines: {node: '>=18'} - peerDependencies: - '@cfworker/json-schema': ^4.1.1 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - '@cfworker/json-schema': - optional: true - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - '@oxc-project/types@0.147.0': resolution: {integrity: sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==} @@ -1230,13 +1130,6 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - '@sec-ant/readable-stream@0.4.1': - resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - - '@sindresorhus/merge-streams@4.0.0': - resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} - engines: {node: '>=18'} - '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -1265,9 +1158,6 @@ packages: peerDependencies: '@testing-library/dom': '>=7.21.4' - '@ts-morph/common@0.27.0': - resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} - '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -1297,9 +1187,6 @@ packages: '@types/react@19.2.18': resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} - '@types/validate-npm-package-name@4.0.2': - resolution: {integrity: sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==} - '@typescript-eslint/eslint-plugin@8.68.0': resolution: {integrity: sha512-WASHDpCm6qO5jj9g1a+8NiW5+GCkAyLReR56/4VruYmNgfUmqpxOfZ2Yfb8xGfJPWv5Qi6LSD8sXdces3vbp/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1404,10 +1291,6 @@ packages: '@vitest/utils@4.1.11': resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} - acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1418,47 +1301,17 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - ajv-formats@2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} - ajv@8.20.0: - resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} - - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.3.0: - resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==} - engines: {node: '>=12'} - ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-hidden@1.2.6: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} @@ -1470,14 +1323,6 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - ast-types@0.16.1: - resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} - engines: {node: '>=4'} - - atomically@1.7.0: - resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==} - engines: {node: '>=10.12.0'} - balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -1490,43 +1335,15 @@ packages: bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} - body-parser@2.3.0: - resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} - engines: {node: '>=18'} - brace-expansion@5.0.9: resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} engines: {node: 20 || >=22} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - browserslist@4.28.8: resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - bundle-name@4.1.0: - resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} - engines: {node: '>=18'} - - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - caniuse-lite@1.0.30001810: resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} @@ -1534,76 +1351,21 @@ packages: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} - chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} - cli-cursor@5.0.0: - resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} - engines: {node: '>=18'} - - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} - code-block-writer@13.0.3: - resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==} - - commander@11.1.0: - resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} - engines: {node: '>=16'} - - commander@14.0.3: - resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + cn@0.2.5: + resolution: {integrity: sha512-OCjZtMeQfXbI4Es1+EIjkd77gvWzaE689gD8KhfexlqjClC06qR1MQBR+Z35ZMSPNEBWyHiItW1Soy0UvwNv9w==} engines: {node: '>=20'} - - conf@10.2.0: - resolution: {integrity: sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==} - engines: {node: '>=12'} - - content-disposition@1.1.0: - resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} - engines: {node: '>=18'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - - content-type@2.1.0: - resolution: {integrity: sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==} - engines: {node: '>=18'} + hasBin: true convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} - engines: {node: '>=6.6.0'} - - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} - - cors@2.8.6: - resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} - engines: {node: '>= 0.10'} - - cosmiconfig@9.0.2: - resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1612,11 +1374,6 @@ packages: resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -1624,10 +1381,6 @@ packages: resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - debounce-fn@4.0.0: - resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==} - engines: {node: '>=10'} - debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -1640,41 +1393,9 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} - dedent@1.7.2: - resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - default-browser-id@5.0.1: - resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} - engines: {node: '>=18'} - - default-browser@5.5.1: - resolution: {integrity: sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==} - engines: {node: '>=18'} - - define-lazy-prop@2.0.0: - resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} - engines: {node: '>=8'} - - define-lazy-prop@3.0.0: - resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} - engines: {node: '>=12'} - - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1686,75 +1407,23 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - diff@8.0.4: - resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} - engines: {node: '>=0.3.1'} - dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} - dot-prop@6.0.1: - resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} - engines: {node: '>=10'} - - dotenv@17.4.2: - resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} - engines: {node: '>=12'} - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.416: resolution: {integrity: sha512-K6bvB2BjnNrugtIih6ewlbBI9DXa976jIdiIlRLHhBoEI9a4JaQjjHyF+A1IQI543aQYR4LnmOrT/K5fZj0aPA==} - emoji-regex@10.6.0: - resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} - - encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - entities@8.0.0: resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} engines: {node: '>=20.19.0'} - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - - error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} - es-object-atoms@1.1.2: - resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} - engines: {node: '>= 0.4'} - escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1791,11 +1460,6 @@ packages: resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} @@ -1815,59 +1479,19 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - - eventsource-parser@3.1.1: - resolution: {integrity: sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==} - engines: {node: '>=18.0.0'} - - eventsource@3.0.7: - resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} - engines: {node: '>=18.0.0'} - - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - execa@9.6.1: - resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} - engines: {node: ^18.19.0 || >=20.5.0} - expect-type@1.4.0: resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} - express-rate-limit@8.7.0: - resolution: {integrity: sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==} - engines: {node: '>= 16'} - peerDependencies: - express: '>= 4.11' - - express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} - engines: {node: '>= 18'} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-uri@3.1.6: - resolution: {integrity: sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==} - - fastq@1.20.3: - resolution: {integrity: sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==} - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1877,26 +1501,10 @@ packages: picomatch: optional: true - figures@6.1.0: - resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} - engines: {node: '>=18'} - file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - finalhandler@2.1.1: - resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} - engines: {node: '>= 18.0.0'} - - find-up@3.0.0: - resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} - engines: {node: '>=6'} - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1908,114 +1516,33 @@ packages: flatted@3.4.4: resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - - fresh@2.0.0: - resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} - engines: {node: '>= 0.8'} - - fs-extra@11.4.0: - resolution: {integrity: sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==} - engines: {node: '>=14.14'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - fuzzysort@3.1.0: - resolution: {integrity: sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==} - gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - get-east-asian-width@1.6.0: - resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} - engines: {node: '>=18'} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} - get-own-enumerable-keys@1.0.0: - resolution: {integrity: sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==} - engines: {node: '>=14.16'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-stream@9.0.1: - resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} - engines: {node: '>=18'} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - hasown@2.0.4: - resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} - engines: {node: '>= 0.4'} - hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} - hono@4.13.5: - resolution: {integrity: sha512-O6+/eCYRkzzzy0rPWwKLiGBR1nFuUPZynnwjxN1MBA62NNqbT0wQEzQyK2gSO5yDIDB336sXQleAhOHrzlYyKw==} - engines: {node: '>=16.9.0'} - html-encoding-sniffer@6.0.0: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - http-errors@2.0.1: - resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} - engines: {node: '>= 0.8'} - - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - - human-signals@8.0.1: - resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} - engines: {node: '>=18.18.0'} - - iconv-lite@0.7.3: - resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} - engines: {node: '>=0.10.0'} - ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -2024,38 +1551,10 @@ packages: resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} engines: {node: '>= 4'} - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ip-address@10.7.0: - resolution: {integrity: sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==} - engines: {node: '>= 12'} - - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-docker@2.2.1: - resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} - engines: {node: '>=8'} - hasBin: true - - is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2064,86 +1563,15 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-in-ssh@1.0.0: - resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==} - engines: {node: '>=20'} - - is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - - is-interactive@2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} - engines: {node: '>=12'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - - is-obj@3.0.0: - resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==} - engines: {node: '>=12'} - - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - - is-regexp@3.1.0: - resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} - engines: {node: '>=12'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-stream@4.0.1: - resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} - engines: {node: '>=18'} - - is-unicode-supported@1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} - - is-unicode-supported@2.1.0: - resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} - engines: {node: '>=18'} - - is-wsl@2.2.0: - resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} - engines: {node: '>=8'} - - is-wsl@3.1.1: - resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} - engines: {node: '>=16'} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isexe@3.1.5: - resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} - engines: {node: '>=18'} - - jose@6.2.10: - resolution: {integrity: sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g==} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.3.2: - resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} - hasBin: true - jsdom@30.0.1: resolution: {integrity: sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} @@ -2161,21 +1589,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json-schema-typed@7.0.3: - resolution: {integrity: sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==} - - json-schema-typed@8.0.2: - resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} - json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -2184,20 +1600,9 @@ packages: engines: {node: '>=6'} hasBin: true - jsonfile@6.2.1: - resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -2276,21 +1681,10 @@ packages: resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} engines: {node: '>= 12.0.0'} - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - locate-path@3.0.0: - resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} - engines: {node: '>=6'} - locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - log-symbols@6.0.0: - resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} - engines: {node: '>=18'} - lru-cache@11.5.2: resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} @@ -2310,58 +1704,12 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} - media-typer@1.1.1: - resolution: {integrity: sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==} - engines: {node: '>= 0.8'} - - merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} - engines: {node: '>=18'} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - - mime-types@3.0.2: - resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} - engines: {node: '>=18'} - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - mimic-fn@3.1.0: - resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==} - engines: {node: '>=8'} - - mimic-function@5.0.1: - resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} - engines: {node: '>=18'} - - minimatch@10.2.6: - resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} - engines: {node: 18 || 20 || >=22} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2374,115 +1722,29 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - negotiator@1.1.0: - resolution: {integrity: sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==} - engines: {node: '>=18'} - node-releases@2.0.54: resolution: {integrity: sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==} engines: {node: '>=18'} - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - - npm-run-path@6.0.0: - resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} - engines: {node: '>=18'} - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - object-treeify@1.1.33: - resolution: {integrity: sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==} - engines: {node: '>= 10'} - obug@2.1.4: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} engines: {node: '>=12.20.0'} - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - - onetime@7.0.0: - resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} - engines: {node: '>=18'} - - open@11.0.2: - resolution: {integrity: sha512-RWqF+pBSkqecEvCKOn8QYhaNdRMJDZRIrlS/7rTDdLHaPcfXGCZ/h8zb413NfvdeAV0MR7T1yJcA34/q+CSm1Q==} - engines: {node: '>=20'} - - open@8.4.2: - resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} - engines: {node: '>=12'} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - ora@8.2.0: - resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} - engines: {node: '>=18'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-locate@3.0.0: - resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} - engines: {node: '>=6'} - p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - - parse-ms@4.0.0: - resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} - engines: {node: '>=18'} - parse5@8.0.1: resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} - parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - - path-exists@3.0.0: - resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} - engines: {node: '>=4'} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2491,82 +1753,37 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - - path-to-regexp@8.4.2: - resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - picomatch@4.0.7: resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} - pkce-challenge@5.0.1: - resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} - engines: {node: '>=16.20.0'} - - pkg-up@3.1.0: - resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} - engines: {node: '>=8'} - - postcss-selector-parser@7.1.5: - resolution: {integrity: sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==} - engines: {node: '>=4'} - postcss@8.5.26: resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} engines: {node: ^10 || ^12 || >=14} - powershell-utils@0.1.0: - resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} - engines: {node: '>=20'} - - powershell-utils@0.2.1: - resolution: {integrity: sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==} - engines: {node: '>=20'} - prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier@3.9.6: + resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} + engines: {node: '>=14'} + hasBin: true + pretty-format@27.5.1: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - pretty-ms@9.3.1: - resolution: {integrity: sha512-HzMy3Geq23nVALD/M2LliU+F+M+gVNsvkQWWqeBZ8HDiCgzo6YPJ/Omrmtq24EFrIsk0a3EkQGEd7bDOo+IhGA==} - engines: {node: '>=18'} - - prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qs@6.16.0: - resolution: {integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==} - engines: {node: '>=0.6'} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - radix-ui@1.6.7: resolution: {integrity: sha512-QBdhh1arIEUvPC0dQ5+nwWAxt7+N+oP/9jPwjJkGFoSk/sqxg32gJtSXGtFh8frAIcS6oC9cx2Q+7KYCQLOAeA==} peerDependencies: @@ -2580,14 +1797,6 @@ packages: '@types/react-dom': optional: true - range-parser@1.3.0: - resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} - engines: {node: '>= 0.6'} - - raw-body@3.0.2: - resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} - engines: {node: '>= 0.10'} - react-dom@19.2.8: resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} peerDependencies: @@ -2630,10 +1839,6 @@ packages: resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} engines: {node: '>=0.10.0'} - recast@0.23.21: - resolution: {integrity: sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw==} - engines: {node: '>= 4'} - require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -2641,37 +1846,11 @@ packages: reselect@5.3.0: resolution: {integrity: sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==} - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - restore-cursor@5.1.0: - resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} - engines: {node: '>=18'} - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rolldown@1.2.6: resolution: {integrity: sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} - - run-applescript@7.1.0: - resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} - engines: {node: '>=18'} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -2688,22 +1867,6 @@ packages: engines: {node: '>=10'} hasBin: true - send@1.2.1: - resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} - engines: {node: '>= 18'} - - serve-static@2.2.1: - resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} - engines: {node: '>= 18'} - - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - - shadcn@4.19.0: - resolution: {integrity: sha512-EQF6R+CUXTsEP2BpyhxrUEAFesrtFD1POvVOf5jM+wkgtA4kG1EW1+1Wlmi9LqiprSL681JJXBcS0u8WkVVVyQ==} - engines: {node: '>=20.18.1'} - hasBin: true - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2712,43 +1875,9 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - side-channel-list@1.0.1: - resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.1: - resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} - engines: {node: '>= 0.4'} - siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - - socks@2.8.9: - resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - sonner@2.0.8: resolution: {integrity: sha512-UM/ByIoFra8yzV75n1o0Puu0bw5U/9UNnDacrJNspekBewIfsQ3D6ez1nvlWpt7aTsO6rujQtifBpycwIivqlg==} peerDependencies: @@ -2763,70 +1892,18 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - statuses@2.0.2: - resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} - engines: {node: '>= 0.8'} - std-env@4.2.0: resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} - stdin-discarder@0.2.2: - resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} - engines: {node: '>=18'} - - string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} - - stringify-object@5.0.0: - resolution: {integrity: sha512-zaJYxz2FtcMb4f+g60KsRNFOpVMUyuJgA51Zi5Z1DOTC3S59+OQiVOzE9GZt0x72uBGWKsQIuBKeF9iusmKFsg==} - engines: {node: '>=14.16'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.2.0: - resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} - engines: {node: '>=12'} - - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - strip-final-newline@4.0.0: - resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} - engines: {node: '>=18'} - symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - systeminformation@5.33.6: - resolution: {integrity: sha512-hMOQG/eRUzuopuYGGdl8ntkau0nEC7fOaRoTUg1RSr2GTQIk2VNa76DA0+ApajkGfzmcgAupgIP/vt+jtoe5EA==} - engines: {node: '>=10.0.0'} - os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] - hasBin: true - - tailwind-merge@3.6.0: - resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} - tailwindcss@4.3.3: resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} - tiny-invariant@1.3.3: - resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -2849,14 +1926,6 @@ packages: resolution: {integrity: sha512-aBiNayCfTQxuIJBm06M+xR14cYaYlDlSXZbgsnKzKNxDKUVq7KFwTjwBSsb7m9Y5xO8WfPnBc63WaYFMTGlvqw==} hasBin: true - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - tough-cookie@6.0.2: resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} engines: {node: '>=16'} @@ -2871,13 +1940,6 @@ packages: peerDependencies: typescript: '>=4.8.4' - ts-morph@26.0.0: - resolution: {integrity: sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==} - - tsconfig-paths@4.2.0: - resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} - engines: {node: '>=6'} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -2888,10 +1950,6 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-is@2.1.0: - resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} - engines: {node: '>= 18'} - typescript-eslint@8.68.0: resolution: {integrity: sha512-MHy0Y0ynqeEbx/S45+i/bBssdy3X6KNBfmJAP35GrgtNxu2TQ5K5xsFDhAnmsq1jvpdoZOPG1LGtJo0HWqYCrQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2907,26 +1965,10 @@ packages: undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} - undici@7.29.0: - resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} - engines: {node: '>=20.18.1'} - undici@8.10.0: resolution: {integrity: sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==} engines: {node: '>=22.19.0'} - unicorn-magic@0.3.0: - resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} - engines: {node: '>=18'} - - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - update-browserslist-db@1.3.2: resolution: {integrity: sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==} hasBin: true @@ -2961,17 +2003,6 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - validate-npm-package-name@7.0.2: - resolution: {integrity: sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==} - engines: {node: ^20.17.0 || >=22.9.0} - - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - vite@8.2.2: resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3081,11 +2112,6 @@ packages: engines: {node: '>= 8'} hasBin: true - which@4.0.0: - resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} - engines: {node: ^16.13.0 || >=18.0.0} - hasBin: true - why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -3095,13 +2121,6 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - wsl-utils@1.0.0: - resolution: {integrity: sha512-Hl0ZOAs672vg+06kfujwRhoS6/jehvULrlFkuF2dRu6pHgA8U06h3xqNIqNNU1LTXPcedxByAR4GS6pwQK0mgA==} - engines: {node: '>=20'} - xml-name-validator@5.0.0: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} @@ -3116,28 +2135,12 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-spinner@1.2.2: - resolution: {integrity: sha512-DODGl1wJjA/s5pnJFKau9lIYHT81lnhob1i3e1TjxZRxEhWRKl74nTbWE6H5KlkViQQTo/Z29YFdxzTZAMY3ng==} - engines: {node: '>=18.19'} - - yoctocolors@2.2.0: - resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==} - engines: {node: '>=18'} - - zod-to-json-schema@3.25.2: - resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} - peerDependencies: - zod: ^3.25.28 || ^4 - zod-validation-error@4.0.2: resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} engines: {node: '>=18.0.0'} peerDependencies: zod: ^3.25.0 || ^4.0.0 - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.5.4: resolution: {integrity: sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==} @@ -3194,10 +2197,6 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.29.7': - dependencies: - '@babel/types': 7.29.8 - '@babel/helper-compilation-targets@7.29.7': dependencies: '@babel/compat-data': 7.29.7 @@ -3206,28 +2205,8 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 - '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/traverse': 7.29.8 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/helper-globals@7.29.7': {} - '@babel/helper-member-expression-to-functions@7.29.7': - dependencies: - '@babel/traverse': 7.29.8 - '@babel/types': 7.29.8 - transitivePeerDependencies: - - supports-color - '@babel/helper-module-imports@7.29.7': dependencies: '@babel/traverse': 7.29.8 @@ -3244,28 +2223,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-optimise-call-expression@7.29.7': - dependencies: - '@babel/types': 7.29.8 - - '@babel/helper-plugin-utils@7.29.7': {} - - '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 - '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/traverse': 7.29.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-skip-transparent-expression-wrappers@7.29.7': - dependencies: - '@babel/traverse': 7.29.8 - '@babel/types': 7.29.8 - transitivePeerDependencies: - - supports-color - '@babel/helper-string-parser@7.29.7': {} '@babel/helper-validator-identifier@7.29.7': {} @@ -3281,46 +2238,6 @@ snapshots: dependencies: '@babel/types': 7.29.8 - '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - - '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - - '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) - '@babel/helper-plugin-utils': 7.29.7 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) - '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) - transitivePeerDependencies: - - supports-color - - '@babel/preset-typescript@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-validator-option': 7.29.7 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) - transitivePeerDependencies: - - supports-color - '@babel/runtime@7.29.7': {} '@babel/template@7.29.7': @@ -3397,27 +2314,6 @@ snapshots: '@csstools/css-tokenizer@4.0.0': {} - '@dotenvx/dotenvx@1.75.1': - dependencies: - '@dotenvx/primitives': 0.8.0 - commander: 11.1.0 - conf: 10.2.0 - dotenv: 17.4.2 - enquirer: 2.4.1 - env-paths: 2.2.1 - execa: 5.1.1 - fdir: 6.5.0(picomatch@4.0.7) - ignore: 5.3.2 - object-treeify: 1.1.33 - open: 8.4.2 - picomatch: 4.0.7 - systeminformation: 5.33.6 - undici: 7.29.0 - which: 4.0.0 - yocto-spinner: 1.2.2 - - '@dotenvx/primitives@0.8.0': {} - '@eslint-community/eslint-utils@4.10.1(eslint@10.9.1)': dependencies: eslint: 10.9.1 @@ -3473,10 +2369,6 @@ snapshots: '@fontsource-variable/geist@5.3.0': {} - '@hono/node-server@2.1.1(hono@4.13.5)': - dependencies: - hono: 4.13.5 - '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -3512,40 +2404,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.6.0 - '@modelcontextprotocol/sdk@1.30.0(zod@3.25.76)': - dependencies: - '@hono/node-server': 2.1.1(hono@4.13.5) - ajv: 8.20.0 - ajv-formats: 3.0.1(ajv@8.20.0) - content-type: 1.0.5 - cors: 2.8.6 - cross-spawn: 7.0.6 - eventsource: 3.0.7 - eventsource-parser: 3.1.1 - express: 5.2.1 - express-rate-limit: 8.7.0(express@5.2.1) - hono: 4.13.5 - jose: 6.2.10 - json-schema-typed: 8.0.2 - pkce-challenge: 5.0.1 - raw-body: 3.0.2 - zod: 3.25.76 - zod-to-json-schema: 3.25.2(zod@3.25.76) - transitivePeerDependencies: - - supports-color - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.3 - '@oxc-project/types@0.147.0': {} '@radix-ui/number@1.1.3': {} @@ -4342,10 +3200,6 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} - '@sec-ant/readable-stream@0.4.1': {} - - '@sindresorhus/merge-streams@4.0.0': {} - '@standard-schema/spec@1.1.0': {} '@testing-library/dom@10.4.1': @@ -4373,12 +3227,6 @@ snapshots: dependencies: '@testing-library/dom': 10.4.1 - '@ts-morph/common@0.27.0': - dependencies: - fast-glob: 3.3.3 - minimatch: 10.2.6 - path-browserify: 1.0.1 - '@types/aria-query@5.0.4': {} '@types/chai@5.2.3': @@ -4406,8 +3254,6 @@ snapshots: dependencies: csstype: 3.2.3 - '@types/validate-npm-package-name@4.0.2': {} - '@typescript-eslint/eslint-plugin@8.68.0(@typescript-eslint/parser@8.68.0(eslint@10.9.1)(typescript@6.0.3))(eslint@10.9.1)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -4545,25 +3391,12 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.1 - accepts@2.0.0: - dependencies: - mime-types: 3.0.2 - negotiator: 1.1.0 - acorn-jsx@5.3.2(acorn@8.18.0): dependencies: acorn: 8.18.0 acorn@8.18.0: {} - ajv-formats@2.1.1(ajv@8.20.0): - optionalDependencies: - ajv: 8.20.0 - - ajv-formats@3.0.1(ajv@8.20.0): - optionalDependencies: - ajv: 8.20.0 - ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 @@ -4571,23 +3404,10 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.20.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.6 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ansi-colors@4.1.3: {} - ansi-regex@5.0.1: {} - ansi-regex@6.3.0: {} - ansi-styles@5.2.0: {} - argparse@2.0.1: {} - aria-hidden@1.2.6: dependencies: tslib: 2.8.1 @@ -4598,12 +3418,6 @@ snapshots: assertion-error@2.0.1: {} - ast-types@0.16.1: - dependencies: - tslib: 2.8.1 - - atomically@1.7.0: {} - balanced-match@4.0.4: {} baseline-browser-mapping@2.11.20: {} @@ -4612,28 +3426,10 @@ snapshots: dependencies: require-from-string: 2.0.2 - body-parser@2.3.0: - dependencies: - bytes: 3.1.2 - content-type: 2.1.0 - debug: 4.4.3 - http-errors: 2.0.1 - iconv-lite: 0.7.3 - on-finished: 2.4.1 - qs: 6.16.0 - raw-body: 3.0.2 - type-is: 2.1.0 - transitivePeerDependencies: - - supports-color - brace-expansion@5.0.9: dependencies: balanced-match: 4.0.4 - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - browserslist@4.28.8: dependencies: baseline-browser-mapping: 2.11.20 @@ -4642,87 +3438,20 @@ snapshots: node-releases: 2.0.54 update-browserslist-db: 1.3.2(browserslist@4.28.8) - bundle-name@4.1.0: - dependencies: - run-applescript: 7.1.0 - - bytes@3.1.2: {} - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - callsites@3.1.0: {} - - caniuse-lite@1.0.30001810: {} + caniuse-lite@1.0.30001810: {} chai@6.2.2: {} - chalk@5.6.2: {} - class-variance-authority@0.7.1: dependencies: clsx: 2.1.1 - cli-cursor@5.0.0: - dependencies: - restore-cursor: 5.1.0 - - cli-spinners@2.9.2: {} - clsx@2.1.1: {} - code-block-writer@13.0.3: {} - - commander@11.1.0: {} - - commander@14.0.3: {} - - conf@10.2.0: - dependencies: - ajv: 8.20.0 - ajv-formats: 2.1.1(ajv@8.20.0) - atomically: 1.7.0 - debounce-fn: 4.0.0 - dot-prop: 6.0.1 - env-paths: 2.2.1 - json-schema-typed: 7.0.3 - onetime: 5.1.2 - pkg-up: 3.1.0 - semver: 7.8.5 - - content-disposition@1.1.0: {} - - content-type@1.0.5: {} - - content-type@2.1.0: {} + cn@0.2.5: {} convert-source-map@2.0.0: {} - cookie-signature@1.2.2: {} - - cookie@0.7.2: {} - - cors@2.8.6: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - - cosmiconfig@9.0.2(typescript@6.0.3): - dependencies: - env-paths: 2.2.1 - import-fresh: 3.3.1 - js-yaml: 4.3.2 - parse-json: 5.2.0 - optionalDependencies: - typescript: 6.0.3 - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -4734,8 +3463,6 @@ snapshots: mdn-data: 2.27.1 source-map-js: 1.2.1 - cssesc@3.0.0: {} - csstype@3.2.3: {} data-urls@7.0.0: @@ -4745,92 +3472,30 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' - debounce-fn@4.0.0: - dependencies: - mimic-fn: 3.1.0 - debug@4.4.3: dependencies: ms: 2.1.3 decimal.js@10.6.0: {} - dedent@1.7.2: {} - deep-is@0.1.4: {} - deepmerge@4.3.1: {} - - default-browser-id@5.0.1: {} - - default-browser@5.5.1: - dependencies: - bundle-name: 4.1.0 - default-browser-id: 5.0.1 - - define-lazy-prop@2.0.0: {} - - define-lazy-prop@3.0.0: {} - - depd@2.0.0: {} - dequal@2.0.3: {} detect-libc@2.1.2: {} detect-node-es@1.1.0: {} - diff@8.0.4: {} - dom-accessibility-api@0.5.16: {} - dot-prop@6.0.1: - dependencies: - is-obj: 2.0.0 - - dotenv@17.4.2: {} - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - ee-first@1.1.1: {} - electron-to-chromium@1.5.416: {} - emoji-regex@10.6.0: {} - - encodeurl@2.0.0: {} - - enquirer@2.4.1: - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - entities@8.0.0: {} - env-paths@2.2.1: {} - - error-ex@1.3.4: - dependencies: - is-arrayish: 0.2.1 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - es-module-lexer@2.3.2: {} - es-object-atoms@1.1.2: - dependencies: - es-errors: 1.3.0 - escalade@3.2.0: {} - escape-html@1.0.3: {} - escape-string-regexp@4.0.0: {} eslint-plugin-react-hooks@7.1.1(eslint@10.9.1): @@ -4896,8 +3561,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.18.0) eslint-visitor-keys: 5.0.1 - esprima@4.0.1: {} - esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -4914,135 +3577,22 @@ snapshots: esutils@2.0.3: {} - etag@1.8.1: {} - - eventsource-parser@3.1.1: {} - - eventsource@3.0.7: - dependencies: - eventsource-parser: 3.1.1 - - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - execa@9.6.1: - dependencies: - '@sindresorhus/merge-streams': 4.0.0 - cross-spawn: 7.0.6 - figures: 6.1.0 - get-stream: 9.0.1 - human-signals: 8.0.1 - is-plain-obj: 4.1.0 - is-stream: 4.0.1 - npm-run-path: 6.0.0 - pretty-ms: 9.3.1 - signal-exit: 4.1.0 - strip-final-newline: 4.0.0 - yoctocolors: 2.2.0 - expect-type@1.4.0: {} - express-rate-limit@8.7.0(express@5.2.1): - dependencies: - debug: 4.4.3 - express: 5.2.1 - ip-address: 10.7.0 - transitivePeerDependencies: - - supports-color - - express@5.2.1: - dependencies: - accepts: 2.0.0 - body-parser: 2.3.0 - content-disposition: 1.1.0 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.2.2 - debug: 4.4.3 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 2.1.1 - fresh: 2.0.0 - http-errors: 2.0.1 - merge-descriptors: 2.0.0 - mime-types: 3.0.2 - on-finished: 2.4.1 - once: 1.4.0 - parseurl: 1.3.3 - proxy-addr: 2.0.7 - qs: 6.16.0 - range-parser: 1.3.0 - router: 2.2.0 - send: 1.2.1 - serve-static: 2.2.1 - statuses: 2.0.2 - type-is: 2.1.0 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - fast-deep-equal@3.1.3: {} - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} - fast-uri@3.1.6: {} - - fastq@1.20.3: - dependencies: - reusify: 1.1.0 - fdir@6.5.0(picomatch@4.0.7): optionalDependencies: picomatch: 4.0.7 - figures@6.1.0: - dependencies: - is-unicode-supported: 2.1.0 - file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - finalhandler@2.1.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - find-up@3.0.0: - dependencies: - locate-path: 3.0.0 - find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -5055,183 +3605,47 @@ snapshots: flatted@3.4.4: {} - forwarded@0.2.0: {} - - fresh@2.0.0: {} - - fs-extra@11.4.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.1 - universalify: 2.0.1 - fsevents@2.3.3: optional: true - function-bind@1.1.2: {} - - fuzzysort@3.1.0: {} - gensync@1.0.0-beta.2: {} - get-east-asian-width@1.6.0: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.2 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.4 - math-intrinsics: 1.1.0 - get-nonce@1.0.1: {} - get-own-enumerable-keys@1.0.0: {} - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.2 - - get-stream@6.0.1: {} - - get-stream@9.0.1: - dependencies: - '@sec-ant/readable-stream': 0.4.1 - is-stream: 4.0.1 - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 - gopd@1.2.0: {} - - graceful-fs@4.2.11: {} - - has-symbols@1.1.0: {} - - hasown@2.0.4: - dependencies: - function-bind: 1.1.2 - hermes-estree@0.25.1: {} hermes-parser@0.25.1: dependencies: hermes-estree: 0.25.1 - hono@4.13.5: {} - html-encoding-sniffer@6.0.0: dependencies: '@exodus/bytes': 1.15.1 transitivePeerDependencies: - '@noble/hashes' - http-errors@2.0.1: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.2 - toidentifier: 1.0.1 - - human-signals@2.1.0: {} - - human-signals@8.0.1: {} - - iconv-lite@0.7.3: - dependencies: - safer-buffer: 2.1.2 - ignore@5.3.2: {} ignore@7.0.6: {} - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - imurmurhash@0.1.4: {} - inherits@2.0.4: {} - - ip-address@10.7.0: {} - - ipaddr.js@1.9.1: {} - - is-arrayish@0.2.1: {} - - is-docker@2.2.1: {} - - is-docker@3.0.0: {} - is-extglob@2.1.1: {} is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-in-ssh@1.0.0: {} - - is-inside-container@1.0.0: - dependencies: - is-docker: 3.0.0 - - is-interactive@2.0.0: {} - - is-number@7.0.0: {} - - is-obj@2.0.0: {} - - is-obj@3.0.0: {} - - is-plain-obj@4.1.0: {} - is-potential-custom-element-name@1.0.1: {} - is-promise@4.0.0: {} - - is-regexp@3.1.0: {} - - is-stream@2.0.1: {} - - is-stream@4.0.1: {} - - is-unicode-supported@1.3.0: {} - - is-unicode-supported@2.1.0: {} - - is-wsl@2.2.0: - dependencies: - is-docker: 2.2.1 - - is-wsl@3.1.1: - dependencies: - is-inside-container: 1.0.0 - isexe@2.0.0: {} - isexe@3.1.5: {} - - jose@6.2.10: {} - js-tokens@4.0.0: {} - js-yaml@4.3.2: - dependencies: - argparse: 2.0.1 - jsdom@30.0.1: dependencies: '@asamuzakjp/css-color': 6.0.7 @@ -5262,34 +3676,16 @@ snapshots: json-buffer@3.0.1: {} - json-parse-even-better-errors@2.3.1: {} - json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: {} - - json-schema-typed@7.0.3: {} - - json-schema-typed@8.0.2: {} - json-stable-stringify-without-jsonify@1.0.1: {} json5@2.2.3: {} - jsonfile@6.2.1: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - keyv@4.5.4: dependencies: json-buffer: 3.0.1 - kleur@3.0.3: {} - - kleur@4.1.5: {} - levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -5344,22 +3740,10 @@ snapshots: lightningcss-win32-arm64-msvc: 1.33.0 lightningcss-win32-x64-msvc: 1.33.0 - lines-and-columns@1.2.4: {} - - locate-path@3.0.0: - dependencies: - p-locate: 3.0.0 - path-exists: 3.0.0 - locate-path@6.0.0: dependencies: p-locate: 5.0.0 - log-symbols@6.0.0: - dependencies: - chalk: 5.6.2 - is-unicode-supported: 1.3.0 - lru-cache@11.5.2: {} lru-cache@5.1.1: @@ -5376,101 +3760,22 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 - math-intrinsics@1.1.0: {} - mdn-data@2.27.1: {} - media-typer@1.1.1: {} - - merge-descriptors@2.0.0: {} - - merge-stream@2.0.0: {} - - merge2@1.4.1: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.2 - - mime-db@1.54.0: {} - - mime-types@3.0.2: - dependencies: - mime-db: 1.54.0 - - mimic-fn@2.1.0: {} - - mimic-fn@3.1.0: {} - - mimic-function@5.0.1: {} - minimatch@10.2.6: dependencies: brace-expansion: 5.0.9 - minimist@1.2.8: {} - ms@2.1.3: {} nanoid@3.3.18: {} natural-compare@1.4.0: {} - negotiator@1.1.0: - dependencies: - content-type: 2.1.0 - node-releases@2.0.54: {} - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - - npm-run-path@6.0.0: - dependencies: - path-key: 4.0.0 - unicorn-magic: 0.3.0 - - object-assign@4.1.1: {} - - object-inspect@1.13.4: {} - - object-treeify@1.1.33: {} - obug@2.1.4: {} - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - - onetime@7.0.0: - dependencies: - mimic-function: 5.0.1 - - open@11.0.2: - dependencies: - default-browser: 5.5.1 - define-lazy-prop: 3.0.0 - is-in-ssh: 1.0.0 - is-inside-container: 1.0.0 - powershell-utils: 0.2.1 - wsl-utils: 1.0.0 - - open@8.4.2: - dependencies: - define-lazy-prop: 2.0.0 - is-docker: 2.2.1 - is-wsl: 2.2.0 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -5480,127 +3785,46 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - ora@8.2.0: - dependencies: - chalk: 5.6.2 - cli-cursor: 5.0.0 - cli-spinners: 2.9.2 - is-interactive: 2.0.0 - is-unicode-supported: 2.1.0 - log-symbols: 6.0.0 - stdin-discarder: 0.2.2 - string-width: 7.2.0 - strip-ansi: 7.2.0 - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - p-locate@3.0.0: - dependencies: - p-limit: 2.3.0 - p-locate@5.0.0: dependencies: p-limit: 3.1.0 - p-try@2.2.0: {} - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.29.7 - error-ex: 1.3.4 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - - parse-ms@4.0.0: {} - parse5@8.0.1: dependencies: entities: 8.0.0 - parseurl@1.3.3: {} - - path-browserify@1.0.1: {} - - path-exists@3.0.0: {} - path-exists@4.0.0: {} path-key@3.1.1: {} - path-key@4.0.0: {} - - path-to-regexp@8.4.2: {} - pathe@2.0.3: {} picocolors@1.1.1: {} - picomatch@2.3.2: {} - picomatch@4.0.7: {} - pkce-challenge@5.0.1: {} - - pkg-up@3.1.0: - dependencies: - find-up: 3.0.0 - - postcss-selector-parser@7.1.5: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - postcss@8.5.26: dependencies: nanoid: 3.3.18 picocolors: 1.1.1 source-map-js: 1.2.1 - powershell-utils@0.1.0: {} - - powershell-utils@0.2.1: {} - prelude-ls@1.2.1: {} + prettier@3.9.6: {} + pretty-format@27.5.1: dependencies: ansi-regex: 5.0.1 ansi-styles: 5.2.0 react-is: 17.0.2 - pretty-ms@9.3.1: - dependencies: - parse-ms: 4.0.0 - - prompts@2.4.2: - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - - proxy-addr@2.0.7: - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - punycode@2.3.1: {} - qs@6.16.0: - dependencies: - es-define-property: 1.0.1 - side-channel: 1.1.1 - - queue-microtask@1.2.3: {} - radix-ui@1.6.7(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: '@radix-ui/primitive': 1.1.7 @@ -5664,15 +3888,6 @@ snapshots: '@types/react': 19.2.18 '@types/react-dom': 19.2.5(@types/react@19.2.18) - range-parser@1.3.0: {} - - raw-body@3.0.2: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.1 - iconv-lite: 0.7.3 - unpipe: 1.0.0 - react-dom@19.2.8(react@19.2.8): dependencies: react: 19.2.8 @@ -5709,27 +3924,10 @@ snapshots: react@19.2.8: {} - recast@0.23.21: - dependencies: - ast-types: 0.16.1 - esprima: 4.0.1 - source-map: 0.6.1 - tiny-invariant: 1.3.3 - tslib: 2.8.1 - require-from-string@2.0.2: {} reselect@5.3.0: {} - resolve-from@4.0.0: {} - - restore-cursor@5.1.0: - dependencies: - onetime: 7.0.0 - signal-exit: 4.1.0 - - reusify@1.1.0: {} - rolldown@1.2.6: dependencies: '@oxc-project/types': 0.147.0 @@ -5751,24 +3949,6 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.6 '@rolldown/binding-win32-x64-msvc': 1.2.6 - router@2.2.0: - dependencies: - debug: 4.4.3 - depd: 2.0.0 - is-promise: 4.0.0 - parseurl: 1.3.3 - path-to-regexp: 8.4.2 - transitivePeerDependencies: - - supports-color - - run-applescript@7.1.0: {} - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - safer-buffer@2.1.2: {} - saxes@6.0.0: dependencies: xmlchars: 2.2.0 @@ -5779,123 +3959,14 @@ snapshots: semver@7.8.5: {} - send@1.2.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 2.0.0 - http-errors: 2.0.1 - mime-types: 3.0.2 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.3.0 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - serve-static@2.2.1: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 1.2.1 - transitivePeerDependencies: - - supports-color - - setprototypeof@1.2.0: {} - - shadcn@4.19.0(typescript@6.0.3): - dependencies: - '@babel/core': 7.29.7 - '@babel/parser': 7.29.8 - '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) - '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) - '@dotenvx/dotenvx': 1.75.1 - '@modelcontextprotocol/sdk': 1.30.0(zod@3.25.76) - '@types/validate-npm-package-name': 4.0.2 - browserslist: 4.28.8 - commander: 14.0.3 - cosmiconfig: 9.0.2(typescript@6.0.3) - dedent: 1.7.2 - deepmerge: 4.3.1 - diff: 8.0.4 - execa: 9.6.1 - fast-glob: 3.3.3 - fs-extra: 11.4.0 - fuzzysort: 3.1.0 - kleur: 4.1.5 - open: 11.0.2 - ora: 8.2.0 - postcss: 8.5.26 - postcss-selector-parser: 7.1.5 - prompts: 2.4.2 - recast: 0.23.21 - socks: 2.8.9 - stringify-object: 5.0.0 - tailwind-merge: 3.6.0 - ts-morph: 26.0.0 - tsconfig-paths: 4.2.0 - undici: 7.29.0 - validate-npm-package-name: 7.0.2 - zod: 3.25.76 - zod-to-json-schema: 3.25.2(zod@3.25.76) - transitivePeerDependencies: - - '@cfworker/json-schema' - - babel-plugin-macros - - supports-color - - typescript - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} - side-channel-list@1.0.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.1 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - siginfo@2.0.0: {} - signal-exit@3.0.7: {} - - signal-exit@4.1.0: {} - - sisteransi@1.0.5: {} - - smart-buffer@4.2.0: {} - - socks@2.8.9: - dependencies: - ip-address: 10.7.0 - smart-buffer: 4.2.0 - sonner@2.0.8(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: react: 19.2.8 @@ -5905,52 +3976,14 @@ snapshots: source-map-js@1.2.1: {} - source-map@0.6.1: {} - stackback@0.0.2: {} - statuses@2.0.2: {} - std-env@4.2.0: {} - stdin-discarder@0.2.2: {} - - string-width@7.2.0: - dependencies: - emoji-regex: 10.6.0 - get-east-asian-width: 1.6.0 - strip-ansi: 7.2.0 - - stringify-object@5.0.0: - dependencies: - get-own-enumerable-keys: 1.0.0 - is-obj: 3.0.0 - is-regexp: 3.1.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.2.0: - dependencies: - ansi-regex: 6.3.0 - - strip-bom@3.0.0: {} - - strip-final-newline@2.0.0: {} - - strip-final-newline@4.0.0: {} - symbol-tree@3.2.4: {} - systeminformation@5.33.6: {} - - tailwind-merge@3.6.0: {} - tailwindcss@4.3.3: {} - tiny-invariant@1.3.3: {} - tinybench@2.9.0: {} tinyexec@1.3.0: {} @@ -5968,12 +4001,6 @@ snapshots: dependencies: tldts-core: 7.4.11 - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - toidentifier@1.0.1: {} - tough-cookie@6.0.2: dependencies: tldts: 7.4.11 @@ -5986,17 +4013,6 @@ snapshots: dependencies: typescript: 6.0.3 - ts-morph@26.0.0: - dependencies: - '@ts-morph/common': 0.27.0 - code-block-writer: 13.0.3 - - tsconfig-paths@4.2.0: - dependencies: - json5: 2.2.3 - minimist: 1.2.8 - strip-bom: 3.0.0 - tslib@2.8.1: {} tw-animate-css@1.4.0: {} @@ -6005,12 +4021,6 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-is@2.1.0: - dependencies: - content-type: 2.1.0 - media-typer: 1.1.1 - mime-types: 3.0.2 - typescript-eslint@8.68.0(eslint@10.9.1)(typescript@6.0.3): dependencies: '@typescript-eslint/eslint-plugin': 8.68.0(@typescript-eslint/parser@8.68.0(eslint@10.9.1)(typescript@6.0.3))(eslint@10.9.1)(typescript@6.0.3) @@ -6026,16 +4036,8 @@ snapshots: undici-types@8.3.0: {} - undici@7.29.0: {} - undici@8.10.0: {} - unicorn-magic@0.3.0: {} - - universalify@2.0.1: {} - - unpipe@1.0.0: {} - update-browserslist-db@1.3.2(browserslist@4.28.8): dependencies: browserslist: 4.28.8 @@ -6065,12 +4067,6 @@ snapshots: dependencies: react: 19.2.8 - util-deprecate@1.0.2: {} - - validate-npm-package-name@7.0.2: {} - - vary@1.1.2: {} - vite@8.2.2(@types/node@26.4.0): dependencies: lightningcss: 1.33.0 @@ -6138,10 +4134,6 @@ snapshots: dependencies: isexe: 2.0.0 - which@4.0.0: - dependencies: - isexe: 3.1.5 - why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -6149,13 +4141,6 @@ snapshots: word-wrap@1.2.5: {} - wrappy@1.0.2: {} - - wsl-utils@1.0.0: - dependencies: - is-wsl: 3.1.1 - powershell-utils: 0.1.0 - xml-name-validator@5.0.0: {} xmlchars@2.2.0: {} @@ -6164,20 +4149,8 @@ snapshots: yocto-queue@0.1.0: {} - yocto-spinner@1.2.2: - dependencies: - yoctocolors: 2.2.0 - - yoctocolors@2.2.0: {} - - zod-to-json-schema@3.25.2(zod@3.25.76): - dependencies: - zod: 3.25.76 - zod-validation-error@4.0.2(zod@4.5.4): dependencies: zod: 4.5.4 - zod@3.25.76: {} - zod@4.5.4: {} diff --git a/prettier.config.mjs b/prettier.config.mjs new file mode 100644 index 0000000..f0bb00e --- /dev/null +++ b/prettier.config.mjs @@ -0,0 +1,27 @@ +/** + * Prettier is the half of the formatting story that gets *checked*. + * + * `.editorconfig` tells an editor what to do and `.gitattributes` holds the + * line endings, but neither can fail a build — and a rule nothing checks is a + * preference (DESIGN.md says so about the design rules; it is no less true + * here). `pnpm format:check` is what makes this one a rule. + * + * Only two options depart from Prettier's defaults, and both match what the + * repository already does rather than imposing a new taste: + * + * - `printWidth: 100` is the prose convention in `DESIGN.md` and `README.md`, + * where the 95th-percentile line is 99 characters. Leaving it at 80 would + * rewrap most of the package for no reason. + * - `semi: true` is Prettier's default, and it is also what the hand-written + * modules here already do — `src/index.ts`, `src/theme/` and `src/gates/`. + * The component files are the ones that will change, in the one direction + * that converges the two halves of the package on a single answer. + * + * eslint carries no stylistic rules (see `eslint.config.js`), so there is + * nothing for the two tools to disagree about and no need for a compatibility + * plugin between them. + */ +export default { + printWidth: 100, + semi: true, +}; diff --git a/scripts/shadcn_add.sh b/scripts/shadcn_add.sh deleted file mode 100644 index 48d1f0b..0000000 --- a/scripts/shadcn_add.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -# Runs from the repository root (pnpm script cwd). Writes the CLI's -# pristine output to shadcn/.tsx, then relativises src/primitives/.tsx. -set -euo pipefail -[ "$#" -gt 0 ] || { echo "usage: pnpm shadcn:add ..." >&2; exit 2; } -# `pnpm dlx`, not `npx`: pnpm is the only package manager here, and a fetch-and-run -# of an uninstalled package is exactly what the cool-down governs (cooldown.test.mjs). -pnpm dlx shadcn@4.19.0 add "$@" --overwrite --yes -mkdir -p shadcn -for name in "$@"; do - cp "src/primitives/$name.tsx" "shadcn/$name.tsx" -done -node scripts/shadcn_relativize.mjs src/primitives/*.tsx diff --git a/scripts/shadcn_relativize.mjs b/scripts/shadcn_relativize.mjs deleted file mode 100644 index 43a2500..0000000 --- a/scripts/shadcn_relativize.mjs +++ /dev/null @@ -1,19 +0,0 @@ -import { readFileSync, writeFileSync } from "node:fs"; - -// The `.js` extensions are not optional: the package ships `dist/` as ESM, -// and Node's resolver — which consumers' test runners use for node_modules — -// requires explicit extensions on relative imports. -const RULES = [ - [/from "@\/lib\/cn"/g, 'from "../lib/cn.js"'], - [/from "@\/primitives\/([a-z-]+)"/g, 'from "./$1.js"'], -]; - -export function relativize(source) { - return RULES.reduce((text, [pattern, replacement]) => text.replace(pattern, replacement), source); -} - -if (process.argv[1] === new URL(import.meta.url).pathname || process.argv[1].endsWith("shadcn_relativize.mjs")) { - for (const file of process.argv.slice(2)) { - writeFileSync(file, relativize(readFileSync(file, "utf8"))); - } -} diff --git a/shadcn/README.md b/shadcn/README.md deleted file mode 100644 index 30ebcd4..0000000 --- a/shadcn/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# shadcn canonical snapshots - -Each file here is exactly what `shadcn@4.19.0 add ` wrote for this -package's `components.json` (radix-nova, preset b2iH), before the import -relativisation `scripts/shadcn_relativize.mjs` applies. `tests/scripts/shadcn_canonical.test.mjs` -holds every `src/primitives/.tsx` to its snapshot plus added lines only. -Regenerate with `pnpm --filter @visionset/ui-core shadcn:add `; never edit by hand. - -The gate matches the snapshot as an **ordered subsequence**, so a green run is a -strong signal rather than a proof: an edited line whose original text also appears -further down — including on a line that was added — still satisfies the match, and -the edit goes unreported. Read the diff when a primitive changes; the gate does not -stand in for that. diff --git a/shadcn/badge.tsx b/shadcn/badge.tsx deleted file mode 100644 index fe85556..0000000 --- a/shadcn/badge.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" -import { Slot } from "radix-ui" - -import { cn } from "@/lib/cn" - -const badgeVariants = cva( - "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", - { - variants: { - variant: { - default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", - secondary: - "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80", - destructive: - "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20", - outline: - "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground", - ghost: - "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50", - link: "text-primary underline-offset-4 hover:underline", - }, - }, - defaultVariants: { - variant: "default", - }, - } -) - -function Badge({ - className, - variant = "default", - asChild = false, - ...props -}: React.ComponentProps<"span"> & - VariantProps & { asChild?: boolean }) { - const Comp = asChild ? Slot.Root : "span" - - return ( - - ) -} - -export { Badge, badgeVariants } diff --git a/shadcn/button.tsx b/shadcn/button.tsx deleted file mode 100644 index 239fbb6..0000000 --- a/shadcn/button.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" -import { Slot } from "radix-ui" - -import { cn } from "@/lib/cn" - -const buttonVariants = cva( - "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", - { - variants: { - variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/80", - outline: - "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", - secondary: - "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", - ghost: - "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50", - destructive: - "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40", - link: "text-primary underline-offset-4 hover:underline", - }, - size: { - default: - "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", - xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", - sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", - lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", - icon: "size-8", - "icon-xs": - "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3", - "icon-sm": - "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg", - "icon-lg": "size-9", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } -) - -function Button({ - className, - variant = "default", - size = "default", - asChild = false, - ...props -}: React.ComponentProps<"button"> & - VariantProps & { - asChild?: boolean - }) { - const Comp = asChild ? Slot.Root : "button" - - return ( - - ) -} - -export { Button, buttonVariants } diff --git a/shadcn/combobox.tsx b/shadcn/combobox.tsx deleted file mode 100644 index 81afea6..0000000 --- a/shadcn/combobox.tsx +++ /dev/null @@ -1,299 +0,0 @@ -"use client" - -import * as React from "react" -import { Combobox as ComboboxPrimitive } from "@base-ui/react" - -import { cn } from "@/lib/cn" -import { Button } from "@/primitives/button" -import { - InputGroup, - InputGroupAddon, - InputGroupButton, - InputGroupInput, -} from "@/primitives/input-group" -import { ChevronDownIcon, XIcon, CheckIcon } from "lucide-react" - -const Combobox = ComboboxPrimitive.Root - -function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) { - return -} - -function ComboboxTrigger({ - className, - children, - ...props -}: ComboboxPrimitive.Trigger.Props) { - return ( - - {children} - - - ) -} - -function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) { - return ( - } - className={cn(className)} - {...props} - > - - - ) -} - -function ComboboxInput({ - className, - children, - disabled = false, - showTrigger = true, - showClear = false, - ...props -}: ComboboxPrimitive.Input.Props & { - showTrigger?: boolean - showClear?: boolean -}) { - return ( - - } - {...props} - /> - - {showTrigger && ( - - - - )} - {showClear && } - - {children} - - ) -} - -function ComboboxContent({ - className, - side = "bottom", - sideOffset = 6, - align = "start", - alignOffset = 0, - anchor, - ...props -}: ComboboxPrimitive.Popup.Props & - Pick< - ComboboxPrimitive.Positioner.Props, - "side" | "align" | "sideOffset" | "alignOffset" | "anchor" - >) { - return ( - - - - - - ) -} - -function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) { - return ( - - ) -} - -function ComboboxItem({ - className, - children, - ...props -}: ComboboxPrimitive.Item.Props) { - return ( - - {children} - - } - > - - - - ) -} - -function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) { - return ( - - ) -} - -function ComboboxLabel({ - className, - ...props -}: ComboboxPrimitive.GroupLabel.Props) { - return ( - - ) -} - -function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) { - return ( - - ) -} - -function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) { - return ( - - ) -} - -function ComboboxSeparator({ - className, - ...props -}: ComboboxPrimitive.Separator.Props) { - return ( - - ) -} - -function ComboboxChips({ - className, - ...props -}: React.ComponentPropsWithRef & - ComboboxPrimitive.Chips.Props) { - return ( - - ) -} - -function ComboboxChip({ - className, - children, - showRemove = true, - ...props -}: ComboboxPrimitive.Chip.Props & { - showRemove?: boolean -}) { - return ( - - {children} - {showRemove && ( - } - className="-ml-1 opacity-50 hover:opacity-100" - data-slot="combobox-chip-remove" - > - - - )} - - ) -} - -function ComboboxChipsInput({ - className, - ...props -}: ComboboxPrimitive.Input.Props) { - return ( - - ) -} - -function useComboboxAnchor() { - return React.useRef(null) -} - -export { - Combobox, - ComboboxInput, - ComboboxContent, - ComboboxList, - ComboboxItem, - ComboboxGroup, - ComboboxLabel, - ComboboxCollection, - ComboboxEmpty, - ComboboxSeparator, - ComboboxChips, - ComboboxChip, - ComboboxChipsInput, - ComboboxTrigger, - ComboboxValue, - useComboboxAnchor, -} diff --git a/shadcn/dialog.tsx b/shadcn/dialog.tsx deleted file mode 100644 index f1b8ae6..0000000 --- a/shadcn/dialog.tsx +++ /dev/null @@ -1,166 +0,0 @@ -import * as React from "react" -import { Dialog as DialogPrimitive } from "radix-ui" - -import { cn } from "@/lib/cn" -import { Button } from "@/primitives/button" -import { XIcon } from "lucide-react" - -function Dialog({ - ...props -}: React.ComponentProps) { - return -} - -function DialogTrigger({ - ...props -}: React.ComponentProps) { - return -} - -function DialogPortal({ - ...props -}: React.ComponentProps) { - return -} - -function DialogClose({ - ...props -}: React.ComponentProps) { - return -} - -function DialogOverlay({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function DialogContent({ - className, - children, - showCloseButton = true, - ...props -}: React.ComponentProps & { - showCloseButton?: boolean -}) { - return ( - - - - {children} - {showCloseButton && ( - - - - )} - - - ) -} - -function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) -} - -function DialogFooter({ - className, - showCloseButton = false, - children, - ...props -}: React.ComponentProps<"div"> & { - showCloseButton?: boolean -}) { - return ( -
- {children} - {showCloseButton && ( - - - - )} -
- ) -} - -function DialogTitle({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function DialogDescription({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -export { - Dialog, - DialogClose, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogOverlay, - DialogPortal, - DialogTitle, - DialogTrigger, -} diff --git a/shadcn/dropdown-menu.tsx b/shadcn/dropdown-menu.tsx deleted file mode 100644 index 006e74f..0000000 --- a/shadcn/dropdown-menu.tsx +++ /dev/null @@ -1,267 +0,0 @@ -import * as React from "react" -import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui" - -import { cn } from "@/lib/cn" -import { CheckIcon, ChevronRightIcon } from "lucide-react" - -function DropdownMenu({ - ...props -}: React.ComponentProps) { - return -} - -function DropdownMenuPortal({ - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function DropdownMenuTrigger({ - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function DropdownMenuContent({ - className, - align = "start", - sideOffset = 4, - ...props -}: React.ComponentProps) { - return ( - - - - ) -} - -function DropdownMenuGroup({ - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function DropdownMenuItem({ - className, - inset, - variant = "default", - ...props -}: React.ComponentProps & { - inset?: boolean - variant?: "default" | "destructive" -}) { - return ( - - ) -} - -function DropdownMenuCheckboxItem({ - className, - children, - checked, - inset, - ...props -}: React.ComponentProps & { - inset?: boolean -}) { - return ( - - - - - - - {children} - - ) -} - -function DropdownMenuRadioGroup({ - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function DropdownMenuRadioItem({ - className, - children, - inset, - ...props -}: React.ComponentProps & { - inset?: boolean -}) { - return ( - - - - - - - {children} - - ) -} - -function DropdownMenuLabel({ - className, - inset, - ...props -}: React.ComponentProps & { - inset?: boolean -}) { - return ( - - ) -} - -function DropdownMenuSeparator({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function DropdownMenuShortcut({ - className, - ...props -}: React.ComponentProps<"span">) { - return ( - - ) -} - -function DropdownMenuSub({ - ...props -}: React.ComponentProps) { - return -} - -function DropdownMenuSubTrigger({ - className, - inset, - children, - ...props -}: React.ComponentProps & { - inset?: boolean -}) { - return ( - - {children} - - - ) -} - -function DropdownMenuSubContent({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -export { - DropdownMenu, - DropdownMenuPortal, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuLabel, - DropdownMenuItem, - DropdownMenuCheckboxItem, - DropdownMenuRadioGroup, - DropdownMenuRadioItem, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuSub, - DropdownMenuSubTrigger, - DropdownMenuSubContent, -} diff --git a/shadcn/field.tsx b/shadcn/field.tsx deleted file mode 100644 index af7e444..0000000 --- a/shadcn/field.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import { useMemo } from "react" -import { cva, type VariantProps } from "class-variance-authority" - -import { cn } from "@/lib/cn" -import { Label } from "@/primitives/label" -import { Separator } from "@/primitives/separator" - -function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) { - return ( -
[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3", - className - )} - {...props} - /> - ) -} - -function FieldLegend({ - className, - variant = "legend", - ...props -}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) { - return ( - - ) -} - -function FieldGroup({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) -} - -const fieldVariants = cva( - "group/field flex w-full gap-2 data-[invalid=true]:text-destructive", - { - variants: { - orientation: { - vertical: "flex-col *:w-full [&>.sr-only]:w-auto", - horizontal: - "flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px", - responsive: - "flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px", - }, - }, - defaultVariants: { - orientation: "vertical", - }, - } -) - -function Field({ - className, - orientation = "vertical", - ...props -}: React.ComponentProps<"div"> & VariantProps) { - return ( -
- ) -} - -function FieldContent({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) -} - -function FieldLabel({ - className, - ...props -}: React.ComponentProps) { - return ( -