From a18c63bfc575158fce4dabe999986b73c6cdcfac Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 04:49:10 +0000 Subject: [PATCH 1/2] Add blog post series for Dev.to/Medium publication Four-part series on Whenny date library: 1. Introduction - high level overview, AI-first design 2. shadcn-style imports - own your code, Tailwind-like config 3. AI Agents guide - the markdown file that teaches AI conventions 4. The Timestamp Trap - server/client timezone pitfalls solved Ready for publication via Dev.to API or manual posting. https://claude.ai/code/session_01EV8v8HqsiGqW8gyDM2xc1j --- blog/01-introducing-whenny.md | 161 ++++++++++++++++ blog/02-shadcn-style-dates.md | 269 ++++++++++++++++++++++++++ blog/03-ai-agents-dates-guide.md | 284 ++++++++++++++++++++++++++++ blog/04-the-timestamp-trap.md | 314 +++++++++++++++++++++++++++++++ 4 files changed, 1028 insertions(+) create mode 100644 blog/01-introducing-whenny.md create mode 100644 blog/02-shadcn-style-dates.md create mode 100644 blog/03-ai-agents-dates-guide.md create mode 100644 blog/04-the-timestamp-trap.md diff --git a/blog/01-introducing-whenny.md b/blog/01-introducing-whenny.md new file mode 100644 index 0000000..6f4d3fd --- /dev/null +++ b/blog/01-introducing-whenny.md @@ -0,0 +1,161 @@ +# Introducing Whenny: The TypeScript Date Library That AI Actually Understands + +*A modern date library built for the AI era. Moment.js's fun cousin.* + +--- + +If you've ever asked an AI assistant to help you with date formatting in JavaScript, you've probably received a mix of `moment()`, `dayjs()`, `date-fns`, and vanilla `Intl.DateTimeFormat` suggestions. The AI doesn't know which library you're using, what your project conventions are, or how you want dates displayed. + +**Whenny changes that.** + +## The Problem with Date Libraries in 2024 + +Every project I've worked on has the same pattern: + +```typescript +// File 1: UserProfile.tsx +const joinDate = moment(user.createdAt).format('MMM D, YYYY') + +// File 2: Comment.tsx +const timeAgo = formatDistanceToNow(comment.createdAt, { addSuffix: true }) + +// File 3: EventCard.tsx +const eventDate = dayjs(event.startTime).format('dddd, MMMM D') + +// File 4: Invoice.tsx +const dueDate = new Intl.DateTimeFormat('en-US', { + dateStyle: 'long' +}).format(invoice.dueAt) +``` + +Four different approaches. No consistency. And when you ask Claude or GPT to add a new date display somewhere? It guesses. Sometimes it picks moment. Sometimes date-fns. Sometimes it writes 15 lines of vanilla JS. + +## Enter Whenny + +Whenny is a TypeScript date library designed with three principles: + +1. **AI-First API**: Clean, predictable functions that AI can reason about +2. **Own Your Code**: shadcn-style installation - code lives in YOUR repo +3. **Configure Once**: Tailwind-like sizing system for consistent output + +### The Tailwind Approach to Dates + +Instead of remembering format tokens, Whenny uses a size-based system: + +```typescript +import { whenny } from './lib/whenny' + +// T-shirt sizes - configure once, use everywhere +whenny(date).xs // "2/6" +whenny(date).sm // "Feb 6" +whenny(date).md // "Feb 6, 2024" +whenny(date).lg // "February 6th, 2024" +whenny(date).xl // "Thursday, February 6th, 2024" + +// Utility formats +whenny(date).clock // "3:45 PM" +whenny(date).sortable // "2024-02-06" +``` + +No more `'YYYY-MM-DD'` vs `'yyyy-MM-dd'` confusion. No more checking docs for every format string. + +### shadcn-Style Installation + +Whenny doesn't live in `node_modules`. It lives in YOUR codebase: + +```bash +npx create-whenny # Initialize +npx create-whenny add all # Add all modules +``` + +This creates a `lib/whenny/` folder in your project with the actual source code. You own it. You can modify it. When AI generates code using whenny, it can read your configuration and match your conventions. + +### Why This Matters for AI + +When you include a `Whenny-Dates-Agents.md` file in your project (auto-generated), AI assistants can: + +1. **See your date conventions** - "This project uses `.md` for card displays and `.lg` for headers" +2. **Understand your timezone handling** - "Dates come from API in UTC, displayed in user's local time" +3. **Follow your patterns** - Consistent output across every file they touch + +## Quick Start + +```bash +# Install into your project +npx create-whenny + +# Add the modules you need +npx create-whenny add relative smart calendar + +# Or grab everything +npx create-whenny add all +``` + +Then use it: + +```typescript +import { whenny, relative, smart, duration } from './lib/whenny' + +// Smart formatting - picks the best representation automatically +whenny(comment.createdAt).smart() +// "just now" | "5 minutes ago" | "Yesterday at 3pm" | "Feb 6" + +// Relative time +relative(notification.timestamp) // "2 hours ago" + +// Duration formatting +duration(videoLength).compact() // "1h 23m" +duration(videoLength).timer() // "01:23:45" + +// React hooks that auto-update +import { useRelativeTime, useCountdown } from './lib/whenny/react' + +function Comment({ createdAt }) { + const timeAgo = useRelativeTime(createdAt) // Auto-updates! + return {timeAgo} +} +``` + +## What's Included + +Whenny is modular. Pick what you need: + +| Module | Description | +|--------|-------------| +| `core` | The `whenny()` function with size-based formatting | +| `relative` | "5 minutes ago", "in 3 days" | +| `smart` | Context-aware formatting that picks the best display | +| `duration` | Format time spans: "1h 30m", "01:30:00" | +| `calendar` | Business days, isToday, isWeekend, date math | +| `timezone` | Timezone conversions and utilities | +| `transfer` | Server/client sync protocol (more on this in a future post) | +| `natural` | Parse "next tuesday at 3pm" into Date objects | +| `react` | Hooks: useRelativeTime, useCountdown | +| `constants` | 400+ IANA timezones, format patterns | + +## Coming Up + +This is the first post in a series about building date handling that works with AI: + +1. **This post** - Introduction to Whenny +2. **shadcn-style imports** - Why owning your code matters for AI collaboration +3. **AI Agents guide** - The markdown file that makes AI date-aware +4. **The timestamp trap** - Why your calendar component is showing the wrong date (and how to fix it) + +## Try It + +```bash +npx create-whenny +``` + +GitHub: [github.com/ZVN-DEV/whenny](https://github.com/ZVN-DEV/whenny) +Docs: [whenny.dev](https://whenny.dev) +npm: [npmjs.com/package/whenny](https://npmjs.com/package/whenny) + +--- + +*Whenny is MIT licensed and open source. Star the repo if you find it useful!* + +--- + +**Tags:** #typescript #javascript #webdev #ai diff --git a/blog/02-shadcn-style-dates.md b/blog/02-shadcn-style-dates.md new file mode 100644 index 0000000..17515da --- /dev/null +++ b/blog/02-shadcn-style-dates.md @@ -0,0 +1,269 @@ +# Why Your Date Library Should Live in Your Codebase, Not node_modules + +*The shadcn approach to date formatting - own your code, configure like Tailwind* + +--- + +When Shadcn/ui came out, it challenged a fundamental assumption: **why do UI components need to be npm packages?** + +The answer was: they don't. By copying components directly into your codebase, you get: +- Full customization without forking +- No version lock-in +- Code that AI assistants can read and understand +- Consistency that matches YOUR project, not some library author's opinion + +**Whenny applies the same philosophy to date formatting.** + +## The node_modules Problem + +Here's what happens when you use a traditional date library: + +```typescript +// Your code +import { format } from 'date-fns' +const display = format(date, 'MMM d, yyyy') +``` + +Looks simple. But: + +1. **AI can't see the implementation** - When Claude helps you, it can't read inside `node_modules` +2. **Format strings are tribal knowledge** - Is it `YYYY` or `yyyy`? Depends on the library +3. **Customization requires workarounds** - Want to change "5 minutes ago" to "5m ago"? Good luck +4. **No project-wide conventions** - Every developer picks their own format strings + +## The shadcn Approach + +Whenny installs code directly into your project: + +```bash +npx create-whenny +npx create-whenny add relative smart duration +``` + +This creates: + +``` +your-project/ +├── lib/ +│ └── whenny/ +│ ├── core.ts # Main whenny() function +│ ├── relative.ts # "5 minutes ago" +│ ├── smart.ts # Context-aware formatting +│ ├── duration.ts # "1h 30m" +│ ├── config.ts # YOUR configuration +│ └── index.ts # Exports +``` + +**You own this code.** It's checked into your repo. AI can read it. You can modify it. + +## Tailwind-Style Configuration + +Remember the first time you used Tailwind? Instead of: + +```css +.card-title { + font-size: 1.25rem; + font-weight: 600; + line-height: 1.75rem; +} +``` + +You wrote: + +```html +

+``` + +Whenny does the same for dates. Instead of: + +```typescript +// Memorizing format tokens +format(date, 'MMM d, yyyy') // date-fns +moment(date).format('MMM D, YYYY') // moment +dayjs(date).format('MMM D, YYYY') // dayjs +``` + +You write: + +```typescript +whenny(date).md // "Feb 6, 2024" +``` + +### Configure Once, Use Everywhere + +In your `lib/whenny/config.ts`: + +```typescript +import { configure } from './core' + +configure({ + styles: { + xs: 'M/D', // "2/6" + sm: 'MMM D', // "Feb 6" + md: 'MMM D, YYYY', // "Feb 6, 2024" + lg: 'MMMM Do, YYYY', // "February 6th, 2024" + xl: 'dddd, MMMM Do, YYYY', // "Thursday, February 6th, 2024" + clock: 'h:mm A', // "3:45 PM" + sortable: 'YYYY-MM-DD', // "2024-02-06" + }, + relative: { + justNow: 'just now', + minutesAgo: (n) => `${n}m ago`, + hoursAgo: (n) => `${n}h ago`, + daysAgo: (n) => `${n}d ago`, + } +}) +``` + +Now EVERYWHERE in your app: + +```typescript +// Component 1 +whenny(post.createdAt).sm // "Feb 6" + +// Component 2 +whenny(event.startTime).lg // "February 6th, 2024" + +// Component 3 +whenny(comment.timestamp).relative() // "5m ago" +``` + +**One configuration. Consistent output. No format strings to remember.** + +## Why This Matters for AI Collaboration + +When you ask Claude to add a date display to a new component, here's what happens: + +### With Traditional Libraries + +``` +You: "Add a timestamp to the comment component" + +Claude: *checks if project uses moment, dayjs, date-fns, or Intl* +Claude: *guesses based on package.json* +Claude: *picks a format string that might not match your conventions* +``` + +Result: Inconsistent formatting across your codebase. + +### With Whenny + +``` +You: "Add a timestamp to the comment component" + +Claude: *reads lib/whenny/config.ts* +Claude: *sees your relative time config uses "5m ago" format* +Claude: *matches your existing patterns* +``` + +```typescript +// Claude generates: +import { whenny } from '@/lib/whenny' + +function Comment({ comment }) { + return ( +
+ {whenny(comment.createdAt).relative()} + {/* Uses YOUR config: "5m ago" not "5 minutes ago" */} +
+ ) +} +``` + +## Real-World Configuration Examples + +### Slack-Style + +```typescript +configure({ + styles: { + xs: 'M/D', + sm: 'MMM D', + md: 'MMM D, YYYY', + }, + relative: { + justNow: 'just now', + minutesAgo: (n) => `${n} min`, + hoursAgo: (n) => `${n} hr`, + }, + smart: { + todayFormat: 'h:mm A', // "3:45 PM" + yesterdayFormat: '[Yesterday at] h:mm A', + thisWeekFormat: 'dddd [at] h:mm A', // "Monday at 3:45 PM" + } +}) +``` + +### Twitter-Style (Ultra Compact) + +```typescript +configure({ + relative: { + justNow: 'now', + secondsAgo: (n) => `${n}s`, + minutesAgo: (n) => `${n}m`, + hoursAgo: (n) => `${n}h`, + daysAgo: (n) => `${n}d`, + } +}) +``` + +### Formal/Business + +```typescript +configure({ + styles: { + sm: 'D MMMM', // "6 February" + md: 'D MMMM YYYY', // "6 February 2024" + lg: 'Do of MMMM, YYYY', // "6th of February, 2024" + }, + relative: { + justNow: 'a moment ago', + minutesAgo: (n) => `${n} minutes ago`, + } +}) +``` + +## The Module System + +Don't need everything? Only install what you use: + +```bash +# Just core formatting +npx create-whenny add core + +# Add relative time later +npx create-whenny add relative + +# Need business day calculations? +npx create-whenny add calendar +``` + +Each module is independent. Your bundle only includes what you actually use. + +## Migration from Other Libraries + +Already using moment or date-fns? Whenny's format strings are compatible: + +```typescript +// These all work: +whenny(date).format('YYYY-MM-DD') // moment-style +whenny(date).format('MMM Do, YYYY') // moment-style +whenny(date).md // Or just use sizes! +``` + +## Try It + +```bash +npx create-whenny +``` + +Then open `lib/whenny/config.ts` and make it yours. + +--- + +**Next up:** How to create an AI-friendly markdown file that teaches agents your date conventions. + +--- + +**Tags:** #typescript #javascript #webdev #shadcn diff --git a/blog/03-ai-agents-dates-guide.md b/blog/03-ai-agents-dates-guide.md new file mode 100644 index 0000000..95fa7be --- /dev/null +++ b/blog/03-ai-agents-dates-guide.md @@ -0,0 +1,284 @@ +# The Markdown File That Teaches AI How to Handle Dates in Your Project + +*Stop getting inconsistent date formatting from Claude, Copilot, and ChatGPT* + +--- + +Every time you ask an AI assistant to write code that displays a date, you're playing format roulette: + +```typescript +// What you get from AI attempt #1: +const date = new Date(timestamp).toLocaleDateString() + +// Attempt #2: +const date = format(timestamp, 'yyyy-MM-dd') + +// Attempt #3: +const date = moment(timestamp).format('MMM D, YYYY') + +// Attempt #4: +const date = dayjs(timestamp).format('MMMM D, YYYY') +``` + +The AI is guessing. It doesn't know your project conventions, your preferred library, or how you want dates displayed in different contexts. + +**There's a simple fix: tell it.** + +## The Whenny-Dates-Agents.md File + +When you initialize Whenny in your project, it can generate a markdown file specifically designed for AI consumption: + +```bash +npx create-whenny +# Generates lib/whenny/ AND Whenny-Dates-Agents.md +``` + +This file lives in your project root and contains everything an AI needs to know about handling dates in YOUR codebase. + +## What Goes in the File + +Here's an example `Whenny-Dates-Agents.md`: + +```markdown +# Date Handling Guide for AI Agents + +This project uses **Whenny** for all date formatting and manipulation. +Import from `@/lib/whenny` (NOT moment, dayjs, or date-fns). + +## Quick Reference + +| Use Case | Code | Output | +|----------|------|--------| +| Card/list dates | `whenny(date).sm` | "Feb 6" | +| Full dates | `whenny(date).lg` | "February 6th, 2024" | +| Timestamps | `whenny(date).clock` | "3:45 PM" | +| Relative time | `whenny(date).relative()` | "5m ago" | +| Smart (auto) | `whenny(date).smart()` | Context-aware | + +## Import Statement + +Always use: +```typescript +import { whenny, relative, smart, duration } from '@/lib/whenny' +``` + +## Formatting Rules + +1. **User-facing dates**: Use `.sm` or `.md` sizes +2. **Timestamps on comments/posts**: Use `.relative()` or `.smart()` +3. **Headers/titles**: Use `.lg` or `.xl` sizes +4. **API responses**: Use `.sortable` (ISO format) +5. **Logs**: Use `.log` format + +## DO NOT + +- Import from `moment`, `dayjs`, or `date-fns` +- Use `new Date().toLocaleDateString()` +- Write custom format strings (use size properties instead) +- Forget timezone handling (see Transfer Protocol section) + +## Timezone Handling + +This project stores dates in UTC. When displaying: + +```typescript +// From API (UTC) -> Display (local) +whenny(apiDate).smart() // Automatically converts + +// When SENDING to API from user input: +import { createTransfer } from '@/lib/whenny' +const payload = createTransfer(userSelectedDate, { + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone +}) +``` + +## React Components + +Use the hooks for auto-updating displays: + +```typescript +import { useRelativeTime, useCountdown } from '@/lib/whenny/react' + +// Auto-updates every minute +const timeAgo = useRelativeTime(comment.createdAt) + +// Countdown timer +const { days, hours, minutes, seconds } = useCountdown(deadline) +``` + +## Installed Modules + +- [x] core - Base formatting +- [x] relative - "5 minutes ago" +- [x] smart - Context-aware formatting +- [x] duration - "1h 30m" +- [x] calendar - Business days, date math +- [x] timezone - Timezone utilities +- [x] transfer - Server/client sync +- [x] react - Hooks +``` + +## Why This Works + +AI assistants like Claude, GPT, and Copilot read your project files for context. When they encounter a task involving dates, they'll: + +1. **Find the markdown file** - It's in the project root, easy to discover +2. **Read your conventions** - Import paths, formatting rules, DO NOTs +3. **Generate matching code** - Consistent with your existing codebase + +### Before (Without the Guide) + +``` +You: "Add a created date to the BlogPost component" + +AI: *generates* +import { format } from 'date-fns' +{format(post.createdAt, 'MMMM d, yyyy')} +``` + +### After (With the Guide) + +``` +You: "Add a created date to the BlogPost component" + +AI: *reads Whenny-Dates-Agents.md* +AI: *generates* +import { whenny } from '@/lib/whenny' +{whenny(post.createdAt).md} +``` + +## Auto-Generation + +The guide updates automatically when you add modules: + +```bash +npx create-whenny add duration calendar +# Whenny-Dates-Agents.md is regenerated with new modules documented +``` + +## Customizing the Guide + +The file is yours to edit. Add project-specific rules: + +```markdown +## Project-Specific Rules + +### Event Dates +Events always show day of week: +```typescript +whenny(event.startTime).xl // "Thursday, February 6th, 2024" +``` + +### Invoice Due Dates +Use formal format for financial documents: +```typescript +whenny(invoice.dueDate).format('MMMM Do, YYYY') +``` + +### User Birthdays +Never show year for privacy: +```typescript +whenny(user.birthday).format('MMMM Do') // "February 6th" +``` +``` + +## MCP Server Integration + +If you're using Claude with MCP (Model Context Protocol), Whenny includes a built-in server: + +```json +{ + "mcpServers": { + "whenny": { + "command": "npx", + "args": ["create-whenny", "mcp"] + } + } +} +``` + +This exposes all Whenny functions directly to Claude, so it can: +- Format dates in real-time while helping you +- Understand your configuration +- Suggest the right function for each use case + +## The Full Template + +Here's a complete `Whenny-Dates-Agents.md` you can customize: + +```markdown +# Date Handling - AI Agent Guide + +## Overview +This project uses Whenny for date handling. All date code should use +imports from `@/lib/whenny`. + +## Installed Modules + +- core, relative, smart, duration, calendar, react + +## Quick Reference + +### Formatting Sizes +| Size | Example | Use For | +|------|---------|---------| +| `.xs` | "2/6" | Compact spaces | +| `.sm` | "Feb 6" | Cards, lists | +| `.md` | "Feb 6, 2024" | Default display | +| `.lg` | "February 6th, 2024" | Headers | +| `.xl` | "Thursday, February 6th, 2024" | Full context | + +### Special Formats +| Property | Example | Use For | +|----------|---------|---------| +| `.clock` | "3:45 PM" | Time only | +| `.sortable` | "2024-02-06" | APIs, sorting | +| `.log` | "2024-02-06 15:45:00" | Logging | + +### Methods +| Method | Example | Use For | +|--------|---------|---------| +| `.relative()` | "5m ago" | Timestamps | +| `.smart()` | varies | Auto-pick best | +| `.format('...')` | custom | Specific needs | + +## Rules + +1. ALWAYS import from `@/lib/whenny` +2. PREFER size properties over format strings +3. USE `.smart()` when unsure +4. USE hooks in React components for auto-updating + +## Don't Do This + +```typescript +// BAD +import moment from 'moment' +import { format } from 'date-fns' +new Date().toLocaleDateString() +``` + +## Do This Instead + +```typescript +// GOOD +import { whenny } from '@/lib/whenny' +whenny(date).md +``` +``` + +## Get Started + +```bash +npx create-whenny +``` + +The AI-friendly guide is generated automatically. Edit it to match your project's specific needs. + +--- + +**Next up:** The timestamp trap - why your calendar component shows the wrong date and how Whenny's Transfer Protocol fixes it. + +--- + +**Tags:** #ai #typescript #javascript #webdev diff --git a/blog/04-the-timestamp-trap.md b/blog/04-the-timestamp-trap.md new file mode 100644 index 0000000..a42e053 --- /dev/null +++ b/blog/04-the-timestamp-trap.md @@ -0,0 +1,314 @@ +# The Timestamp Trap: Why Your Calendar Component Shows the Wrong Date + +*And why AI assistants keep generating code that makes it worse* + +--- + +You've seen this bug. Maybe you've shipped it. + +A user in New York selects **December 5th** on a date picker. They submit the form. The confirmation page shows **December 4th**. + +What happened? + +## The Silent Timezone Disaster + +Here's the flow that breaks everything: + +### Step 1: User Picks a Date + +User is in New York (EST, UTC-5). They click December 5th on a calendar component. + +The calendar doesn't care about time - it's a DATE picker. But JavaScript `Date` objects always have a time. So the component creates: + +```javascript +// Calendar component output +new Date('2024-12-05') // December 5th, 2024 +``` + +But wait - what time? When you create a date from a date-only string, JavaScript interprets it as **midnight UTC**: + +```javascript +new Date('2024-12-05').toISOString() +// "2024-12-05T00:00:00.000Z" <- UTC midnight +``` + +### Step 2: Display Looks Fine (Locally) + +On the user's machine in New York: + +```javascript +new Date('2024-12-05').toLocaleDateString() +// "12/4/2024" <- WAIT WHAT?! +``` + +**December 5th UTC midnight is December 4th at 7pm in New York.** + +But most calendar components compensate for this by using `toDateString()` or extracting just the date parts. So the user sees "December 5th" and thinks everything is fine. + +### Step 3: Send to Server + +The frontend sends the date to the API: + +```javascript +// Frontend code +fetch('/api/events', { + method: 'POST', + body: JSON.stringify({ + date: selectedDate.toISOString() + // "2024-12-05T00:00:00.000Z" + }) +}) +``` + +### Step 4: Server Stores It + +The server receives the ISO string and stores it in the database. No conversion, just storage. Seems safe. + +```javascript +// Server code +await db.events.create({ + date: new Date(body.date) // Stored as UTC +}) +``` + +### Step 5: Send Back to User + +Later, the server sends this event back: + +```javascript +// API Response +{ + "date": "2024-12-05T00:00:00.000Z" +} +``` + +### Step 6: User Sees Wrong Date + +The frontend displays it: + +```javascript +// Frontend +const eventDate = new Date(event.date) +eventDate.toLocaleDateString('en-US') +// "12/4/2024" <- December 4th, NOT December 5th +``` + +**The user selected December 5th. They now see December 4th.** + +## Why AI Assistants Make This Worse + +Ask Claude or GPT to help with a date picker form, and you'll get code like: + +```typescript +// AI-generated code (broken) +const handleSubmit = async (selectedDate: Date) => { + await fetch('/api/events', { + method: 'POST', + body: JSON.stringify({ + date: selectedDate.toISOString() + }) + }) +} +``` + +The AI doesn't know about your timezone issues. It generates "correct" code that follows best practices (use ISO strings, store in UTC) but produces wrong results. + +Ask for a fix, and you might get: + +```typescript +// AI "fix" attempt #1 (still broken) +const handleSubmit = async (selectedDate: Date) => { + const dateOnly = selectedDate.toISOString().split('T')[0] + await fetch('/api/events', { + method: 'POST', + body: JSON.stringify({ date: dateOnly }) + }) +} +``` + +Now you're sending `"2024-12-05"` as a string. But when the server parses it back to a Date, you're back to the same problem. + +```typescript +// AI "fix" attempt #2 (hacky, fragile) +const adjustedDate = new Date( + selectedDate.getTime() + + selectedDate.getTimezoneOffset() * 60 * 1000 +) +``` + +This works sometimes but fails for DST transitions and doesn't communicate intent. + +## The Real Problem + +The issue is a **mismatch of concepts**: + +1. **Calendar dates** are time-agnostic: "December 5th" means that day, everywhere +2. **JavaScript Dates** are instants in time: a specific millisecond, with timezone context +3. **UTC storage** loses the user's timezone context + +When a user picks "December 5th" on a calendar, they mean **December 5th in their local context**. Not "midnight UTC" or "midnight server time." + +## The Solution: Whenny's Transfer Protocol + +Whenny solves this by preserving timezone context across the wire: + +```typescript +import { createTransfer, fromTransfer } from '@/lib/whenny' + +// FRONTEND: User picks December 5th +const handleSubmit = async (selectedDate: Date) => { + const payload = createTransfer(selectedDate, { + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone + }) + + // payload = { + // iso: "2024-12-05T05:00:00.000Z", // Actual UTC instant + // originZone: "America/New_York", // Where user is + // originOffset: -300 // UTC-5 in minutes + // } + + await fetch('/api/events', { + method: 'POST', + body: JSON.stringify({ date: payload }) + }) +} +``` + +### On the Server + +```typescript +// SERVER: Receive and store +import { fromTransfer } from 'whenny' + +const received = fromTransfer(body.date) + +await db.events.create({ + datetime: received.date, // Store UTC instant + timezone: received.originZone, // Remember where it came from +}) +``` + +### When Displaying + +```typescript +// FRONTEND: Display correctly +import { whenny } from '@/lib/whenny' + +// Option 1: Show in user's local time +whenny(event.datetime).md +// "Dec 5, 2024" <- Correct! + +// Option 2: Show in the ORIGINAL timezone (where event was created) +whenny(event.datetime).inZone(event.timezone).md +// "Dec 5, 2024" <- Also correct! +``` + +## The Key Insight + +There's only ONE instant in time - the moment December 5th begins in New York. + +- In UTC, that's `2024-12-05T05:00:00Z` +- In New York, that's `2024-12-05T00:00:00-05:00` +- In London, that's `2024-12-05T05:00:00+00:00` + +**Same instant, different representations.** + +Whenny's Transfer Protocol keeps all this information together: + +```typescript +{ + iso: "2024-12-05T05:00:00.000Z", // The actual instant + originZone: "America/New_York", // Context for interpretation + originOffset: -300 // Offset at that moment +} +``` + +## Handling Date-Only Selections + +For calendar pickers where time doesn't matter, Whenny provides a cleaner approach: + +```typescript +import { createTransfer } from '@/lib/whenny' + +// When user picks December 5th (no time): +const dateOnlyPayload = createTransfer(selectedDate, { + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, + dateOnly: true // Treats as start-of-day in user's timezone +}) +``` + +The `dateOnly` flag tells the receiving end: "This represents a calendar date, not a specific instant." + +## Quick Fix for Existing Code + +If you're already using a calendar component that gives you broken dates, here's how to retrofit Whenny: + +```typescript +// BEFORE (broken) +const handleDateSelect = (date: Date) => { + onSubmit({ eventDate: date.toISOString() }) +} + +// AFTER (fixed) +import { createTransfer } from '@/lib/whenny' + +const handleDateSelect = (date: Date) => { + const transfer = createTransfer(date, { + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone + }) + onSubmit({ eventDate: transfer }) +} +``` + +On the receiving end: + +```typescript +// BEFORE (broken) +const displayDate = new Date(event.eventDate).toLocaleDateString() + +// AFTER (fixed) +import { whenny, fromTransfer } from '@/lib/whenny' + +const { date } = fromTransfer(event.eventDate) +const displayDate = whenny(date).md +``` + +## Why This Matters for AI + +When you have Whenny's `Whenny-Dates-Agents.md` in your project, AI assistants learn: + +1. **Always use createTransfer when sending dates to the server** +2. **Always use fromTransfer when receiving dates** +3. **Always use whenny() for display** + +No more AI-generated code that silently breaks timezone handling. + +## Try It + +```bash +npx create-whenny +npx create-whenny add transfer timezone +``` + +Then use the Transfer Protocol for all client-server date communication. + +--- + +## Summary + +| Problem | Traditional Approach | Whenny Solution | +|---------|---------------------|-----------------| +| Date-only picks | Midnight UTC (wrong day) | Transfer with timezone | +| Lost timezone | Store UTC only | Store UTC + origin zone | +| Display errors | Manual offset math | `whenny(date).md` | +| AI assistance | Generates broken code | Reads your conventions | + +Stop fighting timezones. Stop shipping the December 4th bug. + +```bash +npx create-whenny +``` + +--- + +**Tags:** #typescript #javascript #webdev #timezone From c568128d24cf163e76c002526bc45dd918fea846 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 05:52:27 +0000 Subject: [PATCH 2/2] Add blog section with scheduled publishing Blog infrastructure: - Add /blog to navigation across all pages - Create blog listing page with published posts only - Create dynamic [slug] pages for individual posts - Add react-markdown for content rendering - Include social sharing links (Twitter, LinkedIn) Scheduled publishing: - Post 1: "Introducing Whenny" - Feb 6 (today) - Post 2: "shadcn-Style Dates" - Feb 7 - Post 3: "AI Agents Guide" - Feb 10 - Post 4: "The Timestamp Trap" - Feb 11 Posts are filtered by publishDate <= today, so future posts won't appear until their scheduled date. https://claude.ai/code/session_01EV8v8HqsiGqW8gyDM2xc1j --- apps/example/app/blog/[slug]/page.tsx | 147 +++ apps/example/app/blog/layout.tsx | 27 + apps/example/app/blog/page.tsx | 82 ++ apps/example/app/blog/posts.ts | 324 +++++++ apps/example/app/docs/page.tsx | 2 +- apps/example/app/page.tsx | 2 +- apps/example/package.json | 19 +- package-lock.json | 1292 +++++++++++++++++++++++-- 8 files changed, 1819 insertions(+), 76 deletions(-) create mode 100644 apps/example/app/blog/[slug]/page.tsx create mode 100644 apps/example/app/blog/layout.tsx create mode 100644 apps/example/app/blog/page.tsx create mode 100644 apps/example/app/blog/posts.ts diff --git a/apps/example/app/blog/[slug]/page.tsx b/apps/example/app/blog/[slug]/page.tsx new file mode 100644 index 0000000..1ae0079 --- /dev/null +++ b/apps/example/app/blog/[slug]/page.tsx @@ -0,0 +1,147 @@ +'use client' + +import Link from 'next/link' +import { useParams } from 'next/navigation' +import { getPostBySlug, getAllPosts } from '../posts' +import { whenny } from 'whenny' +import ReactMarkdown from 'react-markdown' + +export default function BlogPostPage() { + const params = useParams() + const slug = params.slug as string + const post = getPostBySlug(slug) + + if (!post) { + return ( +
+
+
+ Whenny +
+ Blog + Docs +
+
+
+
+

Post Not Found

+

This post doesn't exist or hasn't been published yet.

+ + ← Back to Blog + +
+
+ ) + } + + return ( +
+ {/* Header */} +
+
+
+ Whenny + Blog +
+
+ Demo + Docs + GitHub +
+
+
+ +
+ {/* Back link */} + + ← Back to Blog + + + {/* Header */} +
+
+ + · + {post.readTime} +
+

+ {post.title} +

+

{post.subtitle}

+
+ {post.tags.map((tag) => ( + + #{tag} + + ))} +
+
+ + {/* Content */} +
+ { + const isInline = !className + if (isInline) { + return ( + + {children} + + ) + } + return ( + + {children} + + ) + }, + pre: ({ children }) => ( +
+                  {children}
+                
+ ), + }} + > + {post.content} +
+
+ + {/* Footer */} + +
+
+ ) +} diff --git a/apps/example/app/blog/layout.tsx b/apps/example/app/blog/layout.tsx new file mode 100644 index 0000000..4e554aa --- /dev/null +++ b/apps/example/app/blog/layout.tsx @@ -0,0 +1,27 @@ +import type { Metadata } from 'next' + +export const metadata: Metadata = { + title: 'Blog - Whenny Date Library', + description: 'Thoughts on dates, timezones, AI-friendly APIs, and building developer tools. Learn about TypeScript date handling, the timestamp trap, and more.', + keywords: [ + 'typescript blog', + 'javascript dates', + 'timezone handling', + 'ai coding', + 'developer tools', + 'date formatting', + ], + openGraph: { + title: 'Blog - Whenny Date Library', + description: 'Thoughts on dates, timezones, AI-friendly APIs, and building developer tools.', + url: 'https://whenny.dev/blog', + }, +} + +export default function BlogLayout({ + children, +}: { + children: React.ReactNode +}) { + return children +} diff --git a/apps/example/app/blog/page.tsx b/apps/example/app/blog/page.tsx new file mode 100644 index 0000000..befdcb4 --- /dev/null +++ b/apps/example/app/blog/page.tsx @@ -0,0 +1,82 @@ +'use client' + +import Link from 'next/link' +import { getPublishedPosts } from './posts' +import { whenny } from 'whenny' + +export default function BlogPage() { + const posts = getPublishedPosts() + + return ( +
+ {/* Header */} +
+
+
+ Whenny + Blog +
+
+ Demo + Docs + GitHub +
+
+
+ +
+

Blog

+

+ Thoughts on dates, timezones, AI, and building developer tools. +

+ + {posts.length === 0 ? ( +
+

No posts published yet. Check back soon!

+
+ ) : ( +
+ {posts.map((post) => ( +
+ +
+ + · + {post.readTime} +
+

+ {post.title} +

+

{post.subtitle}

+
+ {post.tags.map((tag) => ( + + #{tag} + + ))} +
+ +
+ ))} +
+ )} + + {/* Coming Soon Section */} +
+

Coming Soon

+
+

+ + More posts on TypeScript date handling and AI integration +

+
+
+
+
+ ) +} diff --git a/apps/example/app/blog/posts.ts b/apps/example/app/blog/posts.ts new file mode 100644 index 0000000..5b52bbd --- /dev/null +++ b/apps/example/app/blog/posts.ts @@ -0,0 +1,324 @@ +// Blog posts with scheduled publish dates +// Posts will only show if publishDate <= current date + +export interface BlogPost { + slug: string + title: string + subtitle: string + publishDate: string // ISO date string + readTime: string + tags: string[] + content: string +} + +export const posts: BlogPost[] = [ + { + slug: 'introducing-whenny', + title: 'Introducing Whenny', + subtitle: 'The TypeScript Date Library That AI Actually Understands', + publishDate: '2026-02-06', // Thursday + readTime: '5 min read', + tags: ['typescript', 'javascript', 'ai'], + content: `If you've ever asked an AI assistant to help you with date formatting in JavaScript, you've probably received a mix of \`moment()\`, \`dayjs()\`, \`date-fns\`, and vanilla \`Intl.DateTimeFormat\` suggestions. The AI doesn't know which library you're using, what your project conventions are, or how you want dates displayed. + +**Whenny changes that.** + +## The Problem with Date Libraries Today + +Every project I've worked on has the same pattern: + +\`\`\`typescript +// File 1: UserProfile.tsx +const joinDate = moment(user.createdAt).format('MMM D, YYYY') + +// File 2: Comment.tsx +const timeAgo = formatDistanceToNow(comment.createdAt, { addSuffix: true }) + +// File 3: EventCard.tsx +const eventDate = dayjs(event.startTime).format('dddd, MMMM D') + +// File 4: Invoice.tsx +const dueDate = new Intl.DateTimeFormat('en-US', { + dateStyle: 'long' +}).format(invoice.dueAt) +\`\`\` + +Four different approaches. No consistency. And when you ask Claude or GPT to add a new date display somewhere? It guesses. + +## Enter Whenny + +Whenny is a TypeScript date library designed with three principles: + +1. **AI-First API**: Clean, predictable functions that AI can reason about +2. **Own Your Code**: shadcn-style installation - code lives in YOUR repo +3. **Configure Once**: Tailwind-like sizing system for consistent output + +### The Tailwind Approach to Dates + +Instead of remembering format tokens, Whenny uses a size-based system: + +\`\`\`typescript +import { whenny } from './lib/whenny' + +// T-shirt sizes - configure once, use everywhere +whenny(date).xs // "2/6" +whenny(date).sm // "Feb 6" +whenny(date).md // "Feb 6, 2024" +whenny(date).lg // "February 6th, 2024" +whenny(date).xl // "Thursday, February 6th, 2024" +\`\`\` + +No more \`'YYYY-MM-DD'\` vs \`'yyyy-MM-dd'\` confusion. + +## Quick Start + +\`\`\`bash +npx create-whenny +npx create-whenny add all +\`\`\` + +This creates a \`lib/whenny/\` folder in your project with the actual source code. You own it. You can modify it. + +--- + +*This is the first post in a series about building date handling that works with AI. Next up: Why owning your code matters for AI collaboration.*` + }, + { + slug: 'shadcn-style-dates', + title: 'Why Your Date Library Should Live in Your Codebase', + subtitle: 'The shadcn approach to date formatting', + publishDate: '2026-02-07', // Friday + readTime: '6 min read', + tags: ['typescript', 'shadcn', 'architecture'], + content: `When Shadcn/ui came out, it challenged a fundamental assumption: **why do UI components need to be npm packages?** + +The answer was: they don't. By copying components directly into your codebase, you get full customization without forking, no version lock-in, and code that AI assistants can read and understand. + +**Whenny applies the same philosophy to date formatting.** + +## The node_modules Problem + +Here's what happens when you use a traditional date library: + +\`\`\`typescript +import { format } from 'date-fns' +const display = format(date, 'yyyy-MM-dd') +\`\`\` + +Looks simple. But: + +1. **AI can't see the implementation** - When Claude helps you, it can't read inside node_modules +2. **Format strings are tribal knowledge** - Is it \`YYYY\` or \`yyyy\`? Depends on the library +3. **Customization requires workarounds** - Want to change "5 minutes ago" to "5m ago"? Good luck + +## The shadcn Approach + +Whenny installs code directly into your project: + +\`\`\`bash +npx create-whenny +npx create-whenny add relative smart duration +\`\`\` + +This creates: + +\`\`\` +your-project/ +├── lib/ +│ └── whenny/ +│ ├── core.ts # Main whenny() function +│ ├── relative.ts # "5 minutes ago" +│ ├── config.ts # YOUR configuration +│ └── index.ts # Exports +\`\`\` + +**You own this code.** It's checked into your repo. AI can read it. You can modify it. + +## Tailwind-Style Configuration + +In your \`lib/whenny/config.ts\`: + +\`\`\`typescript +configure({ + styles: { + xs: 'M/D', + sm: 'MMM D', + md: 'MMM D, YYYY', + lg: 'MMMM Do, YYYY', + }, + relative: { + justNow: 'just now', + minutesAgo: (n) => \`\${n}m ago\`, + } +}) +\`\`\` + +Now EVERYWHERE in your app, \`whenny(date).sm\` gives you consistent output. + +--- + +*Next up: The markdown file that teaches AI your date conventions.*` + }, + { + slug: 'ai-agents-dates-guide', + title: 'The Markdown File That Teaches AI How to Handle Dates', + subtitle: 'Stop getting inconsistent date formatting from Claude and GPT', + publishDate: '2026-02-10', // Monday + readTime: '7 min read', + tags: ['ai', 'typescript', 'developer-experience'], + content: `Every time you ask an AI assistant to write code that displays a date, you're playing format roulette: + +\`\`\`typescript +// Attempt #1 +const date = new Date(timestamp).toLocaleDateString() + +// Attempt #2 +const date = format(timestamp, 'yyyy-MM-dd') + +// Attempt #3 +const date = moment(timestamp).format('MMM D, YYYY') +\`\`\` + +The AI is guessing. It doesn't know your project conventions. + +**There's a simple fix: tell it.** + +## The Whenny-Dates-Agents.md File + +When you initialize Whenny, it generates a markdown file specifically designed for AI consumption: + +\`\`\`markdown +# Date Handling Guide for AI Agents + +This project uses **Whenny** for all date formatting. +Import from \`@/lib/whenny\` (NOT moment, dayjs, or date-fns). + +## Quick Reference + +| Use Case | Code | Output | +|----------|------|--------| +| Card dates | \`whenny(date).sm\` | "Feb 6" | +| Full dates | \`whenny(date).lg\` | "February 6th, 2024" | +| Timestamps | \`whenny(date).relative()\` | "5m ago" | + +## DO NOT +- Import from moment, dayjs, or date-fns +- Use new Date().toLocaleDateString() +- Write custom format strings +\`\`\` + +## Why This Works + +AI assistants read your project files for context. When they encounter a task involving dates, they'll find this file, read your conventions, and generate matching code. + +### Before (Without the Guide) +Claude generates: \`format(post.createdAt, 'MMMM d, yyyy')\` + +### After (With the Guide) +Claude generates: \`whenny(post.createdAt).md\` + +Consistent with your existing codebase. Every time. + +--- + +*Next up: The timestamp trap - why your calendar component shows the wrong date.*` + }, + { + slug: 'the-timestamp-trap', + title: 'The Timestamp Trap: Why Your Calendar Shows the Wrong Date', + subtitle: 'And why AI assistants keep generating code that makes it worse', + publishDate: '2026-02-11', // Tuesday + readTime: '8 min read', + tags: ['typescript', 'timezone', 'bugs'], + content: `You've seen this bug. Maybe you've shipped it. + +A user in New York selects **December 5th** on a date picker. They submit the form. The confirmation page shows **December 4th**. + +What happened? + +## The Silent Timezone Disaster + +User clicks December 5th. The calendar creates: + +\`\`\`javascript +new Date('2024-12-05') +// JavaScript interprets as UTC midnight +\`\`\` + +On the user's machine in New York: + +\`\`\`javascript +new Date('2024-12-05').toLocaleDateString() +// "12/4/2024" - WRONG! +\`\`\` + +**December 5th UTC midnight is December 4th at 7pm in New York.** + +The frontend sends it to the server, the server stores it, and when it comes back... the user sees the wrong date. + +## Why AI Makes It Worse + +Ask Claude to help with a date picker form: + +\`\`\`typescript +// AI generates this (broken) +const handleSubmit = async (selectedDate: Date) => { + await fetch('/api/events', { + body: JSON.stringify({ + date: selectedDate.toISOString() // Bug waiting to happen + }) + }) +} +\`\`\` + +The AI follows "best practices" (use ISO strings, store UTC) but produces wrong results. + +## The Solution: Whenny's Transfer Protocol + +Whenny preserves timezone context across the wire: + +\`\`\`typescript +import { createTransfer, fromTransfer } from '@/lib/whenny' + +// FRONTEND: User picks December 5th +const payload = createTransfer(selectedDate, { + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone +}) +// { iso: "...", originZone: "America/New_York", originOffset: -300 } + +// SERVER: Receive and store +const received = fromTransfer(body.date) +await db.events.create({ + datetime: received.date, + timezone: received.originZone, +}) + +// FRONTEND: Display correctly +whenny(event.datetime).md // "Dec 5, 2024" ✓ +\`\`\` + +Stop shipping the December 4th bug. + +\`\`\`bash +npx create-whenny +npx create-whenny add transfer timezone +\`\`\`` + } +] + +// Get only published posts (publishDate <= today) +export function getPublishedPosts(): BlogPost[] { + const today = new Date().toISOString().split('T')[0] + return posts.filter(post => post.publishDate <= today) +} + +// Get a single post by slug (only if published) +export function getPostBySlug(slug: string): BlogPost | undefined { + const published = getPublishedPosts() + return published.find(post => post.slug === slug) +} + +// Get all posts (for static generation) +export function getAllPosts(): BlogPost[] { + return posts +} diff --git a/apps/example/app/docs/page.tsx b/apps/example/app/docs/page.tsx index b0479dc..c3c228e 100644 --- a/apps/example/app/docs/page.tsx +++ b/apps/example/app/docs/page.tsx @@ -78,7 +78,7 @@ export default function DocsPage() {
Demo - Server + Blog GitHub
diff --git a/apps/example/app/page.tsx b/apps/example/app/page.tsx index a36a844..8ae1f5d 100644 --- a/apps/example/app/page.tsx +++ b/apps/example/app/page.tsx @@ -101,7 +101,7 @@ export default function HomePage() {
Demo Docs - Test ✓ + Blog GitHub
diff --git a/apps/example/package.json b/apps/example/package.json index 5437999..5874080 100644 --- a/apps/example/package.json +++ b/apps/example/package.json @@ -9,19 +9,20 @@ "lint": "next lint" }, "dependencies": { - "whenny": "*", - "whenny-react": "*", - "next": "^14.1.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-day-picker": "^8.10.0", - "date-fns": "^3.3.0", - "lucide-react": "^0.316.0", "@radix-ui/react-popover": "^1.0.7", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", + "date-fns": "^3.3.0", + "lucide-react": "^0.316.0", + "next": "^14.1.0", + "react": "^18.2.0", + "react-day-picker": "^8.10.0", + "react-dom": "^18.2.0", + "react-markdown": "^10.1.0", "tailwind-merge": "^2.2.1", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "whenny": "*", + "whenny-react": "*" }, "devDependencies": { "@types/node": "^20.11.0", diff --git a/package-lock.json b/package-lock.json index c407119..e547487 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "react": "^18.2.0", "react-day-picker": "^8.10.0", "react-dom": "^18.2.0", + "react-markdown": "^10.1.0", "tailwind-merge": "^2.2.1", "tailwindcss-animate": "^1.0.7", "whenny": "*", @@ -2139,13 +2140,30 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/fs-extra": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", @@ -2157,6 +2175,15 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/jsonfile": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", @@ -2167,6 +2194,21 @@ "@types/node": "*" } }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.19.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", @@ -2192,14 +2234,12 @@ "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.27", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", - "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -2216,11 +2256,16 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, "license": "ISC" }, "node_modules/@vitest/coverage-v8": { @@ -2542,6 +2587,16 @@ "postcss": "^8.1.0" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2717,6 +2772,16 @@ ], "license": "CC-BY-4.0" }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chai": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", @@ -2748,6 +2813,46 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chardet": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", @@ -2874,6 +2979,16 @@ "dev": true, "license": "MIT" }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", @@ -2942,7 +3057,6 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, "license": "MIT" }, "node_modules/date-fns": { @@ -2959,7 +3073,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2973,6 +3086,19 @@ } } }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/deep-eql": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", @@ -2993,6 +3119,15 @@ "dev": true, "license": "MIT" }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-indent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", @@ -3009,6 +3144,19 @@ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "license": "MIT" }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -3428,6 +3576,16 @@ "node": ">=4.0" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -3488,6 +3646,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/extendable-error": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/extendable-error/-/extendable-error-0.1.7.tgz", @@ -3823,6 +3987,46 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -3830,6 +4034,16 @@ "dev": true, "license": "MIT" }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/human-id": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.3.tgz", @@ -3933,6 +4147,36 @@ "dev": true, "license": "ISC" }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3960,6 +4204,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3981,6 +4235,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-interactive": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", @@ -4012,6 +4276,18 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", @@ -4324,6 +4600,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4336,84 +4622,679 @@ "loose-envify": "cli.js" } }, - "node_modules/loose-envify/node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "node_modules/loose-envify/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lucide-react": { + "version": "0.316.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.316.0.tgz", + "integrity": "sha512-dTmYX1H4IXsRfVcj/KUxworV6814ApTl7iXaS21AimK2RUEl4j4AfOmqD3VR8phe5V91m4vEJ8tCK4uT1jE5nA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT" }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "get-func-name": "^2.0.1" - } - }, - "node_modules/lucide-react": { - "version": "0.316.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.316.0.tgz", - "integrity": "sha512-dTmYX1H4IXsRfVcj/KUxworV6814ApTl7iXaS21AimK2RUEl4j4AfOmqD3VR8phe5V91m4vEJ8tCK4uT1jE5nA==", - "license": "ISC", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "micromark-util-types": "^2.0.0" } }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "dev": true, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT" }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, "node_modules/micromatch": { "version": "4.0.8", @@ -4493,7 +5374,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/mz": { @@ -4858,6 +5738,31 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5180,6 +6085,16 @@ "node": ">= 6" } }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5273,6 +6188,33 @@ "dev": true, "license": "MIT" }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-remove-scroll": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", @@ -5414,6 +6356,39 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -5661,6 +6636,16 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spawndamnit": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-3.0.1.tgz", @@ -5757,6 +6742,20 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -5819,6 +6818,24 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, "node_modules/styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", @@ -6171,6 +7188,26 @@ "tree-kill": "cli.js" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -6402,6 +7439,93 @@ "dev": true, "license": "MIT" }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -6502,6 +7626,34 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "5.4.21", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", @@ -7170,6 +8322,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "packages/create-whenny": { "version": "1.0.8", "license": "MIT",