diff --git a/README.md b/README.md index 63bef7d..7d64f73 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ A Docker image is available to transform a specified directory into a CRUD API. To run the Docker image: ```bash -docker run -p 8080:8080 -v /path/to/your/data:/app/data marka-crud-api +docker run -p 8080:8080 -v /path/to/your/data:/app/data max/marka-server ``` *(Replace `/path/to/your/data` with the absolute path to the directory you want to expose.)* diff --git a/examples/Article/A dress a day.md b/examples/Article/A dress a day.md new file mode 100644 index 0000000..6ad95d1 --- /dev/null +++ b/examples/Article/A dress a day.md @@ -0,0 +1,29 @@ +--- +_type: Article +author.name: Erin Mckean +url: https://dressaday.com/2006/10/20/you-dont-have-to-be-pretty/ +rating: 5 +date: December 7, 2023 +image: https://i0.wp.com/old-dressaday-images.s3-website-us-west-2.amazonaws.com/6a0133ed1b1479970b0134809d9f8b970c.jpg +reviewRating.ratingValue: 5 +--- + +# You Dont Have to Be Pretty – A Dress A Day + +#positivity + +![Vreeland](https://i0.wp.com/old-dressaday-images.s3-website-us-west-2.amazonaws.com/6a0133ed1b1479970b0134809d9f8b970c.jpg) + +*image is by [Andy Warhol](http://www.warholfoundation.org/) © 2015 The Andy Warhol Foundation for the Visual Arts, Inc. / Artists Rights Society (ARS), New York* + +So the other day, folks in the comments were [talking about leggings](http://www.dressaday.com/2006/10/open-letter-to-mr-mizrahi.html). I’m pretty agnostic about leggings, but the whole discussion (which centered on the fact that it can be \*really\* hard to look good in leggings) got me thinking about the pervasive idea that women owe it to onlookers to maintain a certain standard of decorativeness. + +Now, this may seem strange from someone who writes about pretty dresses (mostly) every day, but: You Don’t Have to Be Pretty. You don’t _owe_ prettiness to anyone. Not to your boyfriend/spouse/partner, not to your co-workers, especially not to random men on the street. You don’t owe it to your mother, you don’t owe it to your children, you don’t owe it to civilization in general. Prettiness is not a rent you pay for occupying a space marked “female”. + +I’m not saying that you SHOULDN’T be pretty if you want to. (You don’t owe UN-prettiness to feminism, in other words.) Pretty is pleasant, and fun, and satisfying, and makes people smile, often even at you. But in the hierarchy of importance, _pretty_ stands several rungs down from _happy_, is way below _healthy_, and if done as a penance, or an obligation, can be so far away from _independent_ that you may have to squint really hard to see it in the haze. + +But what does you-don’t-have-to-be-pretty mean in practical, everyday terms? It means that you don’t have to apologize for wearing things that are held to be “unflattering” or “unfashionable” — especially if, in fact, they make you happy on some level deeper than just being pretty does. So what if your favorite color isn’t a “good” color on you? So what if you are “too fat” (by some arbitrary measure) for a sleeveless top? If you are clean, are covered enough to avoid a citation for public indecency, and have bandaged any open wounds, you can wear any color or style you please, if it makes you happy. + +I was going to make a handy prettiness decision tree, but pretty much the end of every branch was a bubble that said “tell complainers to go to hell” so it wasn’t much of a tool. + +Pretty, it’s sad to say, can have a shelf life. It’s so tied up with youth that, at some point (if you’re lucky), you’re going to have to graduate from pretty. Sometimes (as in the case with Diana Vreeland, above, you can go so far past pretty that you end up in _stylish_, or even _striking_ (or the fashion-y term _jolie laide_) before you know it. But you won’t get there if you think you have to follow all the signs that say “this way to _Pretty_.” You get there by traveling the route you find most interesting. (And to hell with the naysayers who say “But that’s not PRETTY”!) diff --git a/examples/Books/Minimal.md b/examples/Books/Minimal.md new file mode 100644 index 0000000..80be6dd --- /dev/null +++ b/examples/Books/Minimal.md @@ -0,0 +1,18 @@ +--- +_type: Review +author: + _type: Person + name: Alice +datePublished: 2025-08-01 +itemReviewed: + _type: Book + name: Untitled + author: + _type: Person + name: Unknown +--- + +# Untitled + +## Review +A mysterious book that leaves everything to the imagination. diff --git a/examples/Books/NoAuthor.md b/examples/Books/NoAuthor.md new file mode 100644 index 0000000..be07361 --- /dev/null +++ b/examples/Books/NoAuthor.md @@ -0,0 +1,17 @@ +--- +_type: Review +author: + _type: Person + name: Eve +datePublished: 2025-08-15 +itemReviewed: + _type: Book + name: Anonymous Poems +reviewBody: "Short, haunting, and powerful verses." +reviewRating.ratingValue: 4 +--- + +# Anonymous Poems + +## Review +Short, haunting, and powerful verses. diff --git a/examples/Books/NoBody.md b/examples/Books/NoBody.md new file mode 100644 index 0000000..42cfad4 --- /dev/null +++ b/examples/Books/NoBody.md @@ -0,0 +1,18 @@ +--- +_type: Review +author: + _type: Person + name: Clara +datePublished: 2025-08-10 +itemReviewed: + _type: Book + name: 1984 + author: + _type: Person + name: George Orwell +reviewRating: 5 +--- + +# 1984 + +## Review diff --git a/examples/Books/NoRating.md b/examples/Books/NoRating.md new file mode 100644 index 0000000..33cb9ca --- /dev/null +++ b/examples/Books/NoRating.md @@ -0,0 +1,27 @@ +--- +_type: Review +author: + _type: Person + name: Bob +datePublished: 2025-08-05 +itemReviewed: + _type: Book + name: War and Peace + author: + _type: Person + name: Leo Tolstoy +reviewAspect: + - Length + - Complexity + - Characters +positiveNotes: + - Rich storytelling + - Deep philosophical themes +negativeNotes: + - Overwhelmingly long +--- + +# War and Peace + +## Review +An epic novel that rewards patient readers but can feel exhausting at times. diff --git a/examples/Books/TheGreatGatsby.md b/examples/Books/TheGreatGatsby.md new file mode 100644 index 0000000..bbb476d --- /dev/null +++ b/examples/Books/TheGreatGatsby.md @@ -0,0 +1,17 @@ +--- +_type: Review +author: + name: Max Richter +itemReviewed: + _type: Book + author: + name: F. Scott Fitzgerald +reviewRating: 5 +--- + +# The Great Gatsby + +## Review +A brilliant novel that captures the glamour and disillusionment of the Jazz Age. +The writing is lyrical and immersive, and the themes of wealth, love, and loss remain relevant today. +While some characters come across as emotionally flat, the overall impact is unforgettable. diff --git a/examples/Movies/Minimal.md b/examples/Movies/Minimal.md new file mode 100644 index 0000000..fa55f06 --- /dev/null +++ b/examples/Movies/Minimal.md @@ -0,0 +1,15 @@ +--- +_type: Review +author: + _type: Person + name: Frank +datePublished: 2025-08-01 +itemReviewed: + _type: Movie + name: Untitled Film +--- + +# Untitled Film + +## Review +Unclear, experimental, and strange. diff --git a/examples/Movies/Negative.md b/examples/Movies/Negative.md new file mode 100644 index 0000000..84dd303 --- /dev/null +++ b/examples/Movies/Negative.md @@ -0,0 +1,22 @@ +--- +_type: Review +author: + _type: Person + name: Grace +datePublished: 2025-08-02 +itemReviewed: + _type: Movie + name: The Room + author: + _type: Person + name: Tommy Wiseau +negativeNotes: + - Awkward dialogue + - Strange pacing + - Inconsistent acting +--- + +# The Room + +## Review +So bad it’s fascinating. diff --git a/examples/Movies/NoBody.md b/examples/Movies/NoBody.md new file mode 100644 index 0000000..4040c5a --- /dev/null +++ b/examples/Movies/NoBody.md @@ -0,0 +1,21 @@ +--- +_type: Review +author: + _type: Person + name: Jack +datePublished: 2025-08-06 +itemReviewed: + _type: Movie + name: Blade Runner + author: + _type: Person + name: Ridley Scott +reviewRating: 4.5/5 +reviewAspect: + - Atmosphere + - Visual style +--- + +# Blade Runner + +## Review diff --git a/examples/Movies/Structured.md b/examples/Movies/Structured.md new file mode 100644 index 0000000..7a37859 --- /dev/null +++ b/examples/Movies/Structured.md @@ -0,0 +1,23 @@ +--- +_type: Review +author: + _type: Person + name: Henry +datePublished: 2025-08-03 +itemReviewed: + _type: Movie + name: Inception + author: + _type: Person + name: Christopher Nolan +reviewAspect: + - Story + - Visuals + - Soundtrack +reviewRating: 9/10 +--- + +# Inception + +## Review +A mind-bending thriller that keeps you hooked. diff --git a/examples/Recipes/baguette.md b/examples/Recipes/baguette.md new file mode 100644 index 0000000..b1fb4bd --- /dev/null +++ b/examples/Recipes/baguette.md @@ -0,0 +1,17 @@ +--- +_type: Recipe +author.name: Max Richter +--- + +# Baguette + +My favourite baguette recipe + +## Ingredients +- Flour +- Water +- Salt + +## Steps +1. Mix Flour Water and Salt +2. Bake the bread diff --git a/examples/Recipes/cake.md b/examples/Recipes/cake.md new file mode 100644 index 0000000..bd961ff --- /dev/null +++ b/examples/Recipes/cake.md @@ -0,0 +1,21 @@ +--- +author.name: Max Richter +--- + +# Chocolate Cake + +A moist, rich cake for any occasion. + +## Ingredients +- 200 g flour +- 200 g sugar +- 100 g cocoa powder +- 3 eggs +- 200 ml milk + +## Steps +1. Preheat oven to 180°C. +2. Mix dry ingredients in a bowl. +3. Add eggs and milk, stir until smooth. +4. Pour into a cake tin. +5. Bake for 35 minutes. diff --git a/examples/Recipes/caprese-salad.md b/examples/Recipes/caprese-salad.md new file mode 100644 index 0000000..57b1a60 --- /dev/null +++ b/examples/Recipes/caprese-salad.md @@ -0,0 +1,20 @@ +--- +author.name: Max Richter +--- + +# Caprese Salad + +A simple Italian starter with tomato, mozzarella, and basil. + +## Ingredients +- 2 tomatoes +- 125 g mozzarella +- Fresh basil +- Olive oil +- Balsamic vinegar + +## Steps +1. Slice tomatoes and mozzarella. +2. Arrange on a plate with basil leaves. +3. Drizzle with olive oil and balsamic vinegar. +4. Serve immediately. diff --git a/examples/Recipes/cheese-sandwich.md b/examples/Recipes/cheese-sandwich.md new file mode 100644 index 0000000..3be4fab --- /dev/null +++ b/examples/Recipes/cheese-sandwich.md @@ -0,0 +1,18 @@ +--- +author.name: Max Richter +--- + +# Grilled Cheese Sandwich + +Crispy, golden bread filled with melted cheese. + +## Ingredients +- 2 slices bread +- 2 slices cheddar +- Butter + +## Steps +1. Butter the bread slices. +2. Place cheese between bread. +3. Grill in a pan until golden on both sides. +4. Serve hot. diff --git a/examples/Recipes/complex.md b/examples/Recipes/complex.md new file mode 100644 index 0000000..49178d1 --- /dev/null +++ b/examples/Recipes/complex.md @@ -0,0 +1,20 @@ +--- +author.name: Jane Doe +author._type: Organization +recipeCategory: ["Dessert", "Vegan", "Quick"] +--- + +# Vegan Brownies + +Rich chocolate brownies with no animal products. + +## Ingredients +- 200 g flour +- 100 g cocoa powder +- 150 g sugar +- 250 ml almond milk + +## Steps +1. Mix all dry ingredients. +2. Add almond milk and stir into batter. +3. Bake at 180°C for 30 minutes. diff --git a/examples/Recipes/lentil-curry.md b/examples/Recipes/lentil-curry.md new file mode 100644 index 0000000..51a3391 --- /dev/null +++ b/examples/Recipes/lentil-curry.md @@ -0,0 +1,21 @@ +--- +author.name: Max Richter +--- + +# Lentil Curry + +A hearty Indian-inspired curry with red lentils. + +## Ingredients +- 200 g red lentils +- 1 onion +- 2 cloves garlic +- 1 tbsp curry powder +- 400 ml coconut milk + +## Steps +1. Sauté onion and garlic with curry powder. +2. Add lentils and coconut milk. +3. Simmer for 20 minutes. +4. Stir occasionally until thick. +5. Serve with rice. diff --git a/examples/Recipes/multiline.md b/examples/Recipes/multiline.md new file mode 100644 index 0000000..39977c0 --- /dev/null +++ b/examples/Recipes/multiline.md @@ -0,0 +1,23 @@ +--- +author.name: Max Richter +--- + +# Ramen Broth + +A rich broth requiring multiple steps in one ingredient description. + +## Ingredients +- Chicken bones, roasted until golden +- Water, enough to cover bones +- Aromatics: + - Onion + - Ginger + - Garlic +- Soy sauce +- Mirin + +## Steps +1. Roast bones until golden brown. +2. Simmer bones with aromatics and water for 4–6 hours. +3. Strain broth. +4. Add soy sauce and mirin before serving. diff --git a/examples/Recipes/optional-fields.md b/examples/Recipes/optional-fields.md new file mode 100644 index 0000000..5513842 --- /dev/null +++ b/examples/Recipes/optional-fields.md @@ -0,0 +1,17 @@ +--- +author.name: Max Richter +prepTime: +cookTime: +recipeYield: +cookingMethod: +--- + +# Mystery Dish + +A recipe with missing optional metadata. + +## Ingredients +- ??? + +## Steps +1. ??? diff --git a/examples/Recipes/pancakes.md b/examples/Recipes/pancakes.md new file mode 100644 index 0000000..ce0f13d --- /dev/null +++ b/examples/Recipes/pancakes.md @@ -0,0 +1,21 @@ +--- +author.name: Max Richter +--- + +# Pancakes + +Fluffy breakfast pancakes perfect with maple syrup. + +## Ingredients +- 200 g flour +- 2 eggs +- 300 ml milk +- 1 tsp baking powder +- Butter for frying + +## Steps +1. Mix flour, baking powder, eggs, and milk into a smooth batter. +2. Heat butter in a pan. +3. Pour in batter and cook until bubbles form. +4. Flip and cook the other side. +5. Serve with syrup or fruit. diff --git a/examples/Recipes/salad.md b/examples/Recipes/salad.md new file mode 100644 index 0000000..182b38a --- /dev/null +++ b/examples/Recipes/salad.md @@ -0,0 +1,22 @@ +--- +author.name: Max Richter +--- + +# Greek Salad + +A fresh salad with feta, olives, and crisp vegetables. + +## Ingredients +- 2 tomatoes +- 1 cucumber +- 1 red onion +- 100 g feta cheese +- Olives +- Olive oil and oregano + +## Steps +1. Chop tomatoes, cucumber, and onion. +2. Mix with olives in a bowl. +3. Crumble feta on top. +4. Drizzle with olive oil and sprinkle oregano. +5. Serve chilled. diff --git a/examples/Recipes/spaghetti-carbonara.md b/examples/Recipes/spaghetti-carbonara.md new file mode 100644 index 0000000..cf5e206 --- /dev/null +++ b/examples/Recipes/spaghetti-carbonara.md @@ -0,0 +1,21 @@ +--- +author.name: Max Richter +--- + +# Spaghetti Carbonara + +A creamy Roman pasta classic with pancetta, eggs, and cheese. + +## Ingredients +- 200 g spaghetti +- 100 g pancetta +- 2 eggs +- 50 g pecorino romano +- Black pepper + +## Steps +1. Boil the pasta until al dente. +2. Fry pancetta until crisp. +3. Whisk eggs and cheese in a bowl. +4. Combine hot pasta, pancetta, and egg mixture. +5. Season generously with pepper and serve. diff --git a/examples/Recipes/stir-fry.md b/examples/Recipes/stir-fry.md new file mode 100644 index 0000000..0e1986d --- /dev/null +++ b/examples/Recipes/stir-fry.md @@ -0,0 +1,21 @@ +--- +author.name: Max Richter +--- + +# Chicken Stir-Fry + +Quick stir-fried chicken with vegetables and soy sauce. + +## Ingredients +- 200 g chicken breast +- 1 bell pepper +- 1 carrot +- 2 tbsp soy sauce +- 1 tbsp sesame oil + +## Steps +1. Slice chicken and vegetables. +2. Heat oil in a wok. +3. Stir-fry chicken until browned. +4. Add vegetables and soy sauce. +5. Cook until tender-crisp. diff --git a/examples/Recipes/tomato-soup.md b/examples/Recipes/tomato-soup.md new file mode 100644 index 0000000..7ac890e --- /dev/null +++ b/examples/Recipes/tomato-soup.md @@ -0,0 +1,21 @@ +--- +author.name: Max Richter +--- + +# Tomato Soup + +A simple, comforting soup made from fresh tomatoes. + +## Ingredients +- 800 g tomatoes +- 1 onion +- 2 cloves garlic +- Olive oil +- Salt and pepper + +## Steps +1. Sauté onion and garlic in olive oil. +2. Add chopped tomatoes and cook until softened. +3. Blend until smooth. +4. Simmer for 10 minutes. +5. Season and serve warm. diff --git a/examples/Recipes/unicode.md b/examples/Recipes/unicode.md new file mode 100644 index 0000000..f5cdfcc --- /dev/null +++ b/examples/Recipes/unicode.md @@ -0,0 +1,21 @@ +--- +author.name: Max Richter +datePublished: 2025-08-17 +--- + +# Crème brûlée + +A French dessert with tricky accents and formatting. + +## Ingredients +- 500 ml cream +- 100 g sugar +- 4 egg yolks +- 1 vanilla pod + +## Steps +1. Heat cream & vanilla until steaming. +2. Whisk yolks + sugar until pale. +3. Combine gently → do not curdle! +4. Bake in water bath at 150 °C. +5. Chill, then caramelize sugar with a torch 🔥. diff --git a/examples/Recipes/water.md b/examples/Recipes/water.md new file mode 100644 index 0000000..84ac2bf --- /dev/null +++ b/examples/Recipes/water.md @@ -0,0 +1,11 @@ +--- +author.name: Max +--- + +# Water + +## Ingredients +- Water + +## Steps +1. Drink it diff --git a/go.work b/go.work index 13c90e5..3fa1c33 100644 --- a/go.work +++ b/go.work @@ -1,9 +1,11 @@ -go 1.24.5 +go 1.24.7 use ( ./parser + ./playground/wasm ./registry ./renderer + ./server ./template ./testdata ./validator diff --git a/go.work.sum b/go.work.sum index 213f195..e2e1c0a 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,4 +1,8 @@ -github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +git.max-richter.dev/max/marka/parser v0.0.0-20250819170608-69c2550f448e/go.mod h1:xQK6tsgr9BOoeFw8JxjBwDkVENlOqapmcRkYyf/L+SQ= +git.max-richter.dev/max/marka/registry v0.0.0-20250819170608-69c2550f448e/go.mod h1:n793S7TENIfgHpZLz0lm0qorM7eCx3zBLby3Fb++hZA= +git.max-richter.dev/max/marka/template v0.0.0-20250819170608-69c2550f448e/go.mod h1:Uxi5xcxtnjopsIZjjMlFaWJGuglB9JNL++FuaSbOf6U= +git.max-richter.dev/max/marka/testdata v0.0.0-20250819195334-b3c01bb43d9a/go.mod h1:88SkY5pTONkgfBy1FT10LoqRC8rt36iF1fk/rupjuJY= +git.max-richter.dev/max/marka/validator v0.0.0-20250819170608-69c2550f448e/go.mod h1:qdGfCFRzsGedmnd77vb7pu/EMx0W0DcQBMEfvNxMYsw= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..d076463 --- /dev/null +++ b/mise.toml @@ -0,0 +1,2 @@ +[tools] +go = "1.24.7" diff --git a/parser/decoders/decoder.go b/parser/decoders/decoder.go index 73a4b6b..2526608 100644 --- a/parser/decoders/decoder.go +++ b/parser/decoders/decoder.go @@ -20,21 +20,27 @@ func ParseBlock(input string, block template.Block) (any, error) { case template.CodecHashtags: return Keywords(input, block) } - return nil, fmt.Errorf("unknown codec: %s", block.Codec) + fmt.Printf("%#v\n", block) + return nil, fmt.Errorf("unknown codec '%s'", block.Codec) } func Parse(matches []matcher.Block) (any, error) { var result any - for _, m := range matches { + for i, m := range matches { if m.Block.Path == "@index" { continue } input := m.GetContent() value, err := ParseBlock(input, m.Block) + var blockIdentifier any + blockIdentifier = m.Block.Path + if blockIdentifier == "" { + blockIdentifier = fmt.Sprintf("#%d", i) + } if err != nil { - return nil, fmt.Errorf("failed to parse block(%s): %w", m.Block.Path, err) + return nil, fmt.Errorf("failed to parse block(%s) -> %w", blockIdentifier, err) } result = utils.SetPathValue(m.Block.Path, value, result) } diff --git a/parser/decoders/hashtags.go b/parser/decoders/hashtags.go index d9e7f94..e0e63fa 100644 --- a/parser/decoders/hashtags.go +++ b/parser/decoders/hashtags.go @@ -15,5 +15,10 @@ func Keywords(input string, block template.Block) (value any, error error) { tags = append(tags, tag) } } + + if len(tags) == 0 { + return nil, nil + } + return tags, nil } diff --git a/parser/go.mod b/parser/go.mod index 8dba3bf..fe9bbaf 100644 --- a/parser/go.mod +++ b/parser/go.mod @@ -1,6 +1,6 @@ module git.max-richter.dev/max/marka/parser -go 1.24.3 +go 1.24.7 require ( git.max-richter.dev/max/marka/registry v0.0.0-20250817132016-6db87db32567 @@ -14,6 +14,8 @@ require ( ) require ( + git.max-richter.dev/max/marka/renderer v0.0.0-20250819170608-69c2550f448e + git.max-richter.dev/max/marka/testdata v0.0.0-20250819195334-b3c01bb43d9a github.com/google/go-cmp v0.7.0 go.yaml.in/yaml/v4 v4.0.0-rc.1 ) diff --git a/parser/go.sum b/parser/go.sum index f65191b..7468956 100644 --- a/parser/go.sum +++ b/parser/go.sum @@ -1,7 +1,11 @@ git.max-richter.dev/max/marka/registry v0.0.0-20250817132016-6db87db32567 h1:oe7Xb8dE43S8mRla5hfEqagMnvhvEVHsvRlzl2v540w= git.max-richter.dev/max/marka/registry v0.0.0-20250817132016-6db87db32567/go.mod h1:qGWl42P8mgEktfor/IjQp0aS9SqmpeIlhSuVTlUOXLQ= +git.max-richter.dev/max/marka/renderer v0.0.0-20250819170608-69c2550f448e h1:9Eg81l8YMTXWZC3xlZ5L/NJRuK26bksrVtEHyCTV4sM= +git.max-richter.dev/max/marka/renderer v0.0.0-20250819170608-69c2550f448e/go.mod h1:mjJqEqALg4YJoiebk3V21yJVUVEs3K2RiLO/IW6DGCM= git.max-richter.dev/max/marka/template v0.0.0-20250817132016-6db87db32567 h1:XIx89KqTgd/h14oe5mLvT9E8+jGEAjWgudqiMtQdcec= git.max-richter.dev/max/marka/template v0.0.0-20250817132016-6db87db32567/go.mod h1:Uxi5xcxtnjopsIZjjMlFaWJGuglB9JNL++FuaSbOf6U= +git.max-richter.dev/max/marka/testdata v0.0.0-20250819195334-b3c01bb43d9a h1:LAU2LlLZ96s8hcg1OEGD5HBshDspWVwWTa7YG5+A70w= +git.max-richter.dev/max/marka/testdata v0.0.0-20250819195334-b3c01bb43d9a/go.mod h1:88SkY5pTONkgfBy1FT10LoqRC8rt36iF1fk/rupjuJY= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= diff --git a/parser/matcher/matcher.go b/parser/matcher/matcher.go index 55e506d..b6d6f37 100644 --- a/parser/matcher/matcher.go +++ b/parser/matcher/matcher.go @@ -50,7 +50,6 @@ func MatchBlocksFuzzy(markdown string, templateBlocks []template.Block, maxDist } } - // Handle the last block if len(templateBlocks) > 0 { lastBlock := templateBlocks[len(templateBlocks)-1] if lastBlock.Type == template.DataBlock { diff --git a/parser/matcher/matcher_test.go b/parser/matcher/matcher_test.go index cae1b6c..eb2ba4e 100644 --- a/parser/matcher/matcher_test.go +++ b/parser/matcher/matcher_test.go @@ -10,21 +10,21 @@ import ( "git.max-richter.dev/max/marka/testdata" ) -func TestFuzzyFindAll(t *testing.T) { +func TestMatch_FuzzyFindAll(t *testing.T) { recipeMd := testdata.Read(t, "baguette/input.md") tests := []struct { Needle string Start, End, StartIndex int }{ - {StartIndex: 0, Needle: "# Ingredients\n", Start: 77, End: 91}, - {StartIndex: 0, Needle: "# Ingrdients\n", Start: 77, End: 91}, - {StartIndex: 0, Needle: "# Inrdients\n", Start: 77, End: 91}, + {StartIndex: 0, Needle: "## Ingredients\n", Start: 90, End: 105}, + {StartIndex: 0, Needle: "## Ingrdients\n", Start: 90, End: 105}, + {StartIndex: 0, Needle: "## Inrdients\n", Start: 90, End: 105}, {StartIndex: 0, Needle: "---\n", Start: 0, End: 4}, - {StartIndex: 4, Needle: "---\n", Start: 29, End: 33}, - {StartIndex: 0, Needle: "# Steps\n", Start: 116, End: 124}, - {StartIndex: 0, Needle: "# Stps\n", Start: 116, End: 124}, - {StartIndex: 0, Needle: "# Step\n", Start: 116, End: 124}, + {StartIndex: 4, Needle: "---\n", Start: 43, End: 47}, + {StartIndex: 0, Needle: "## Steps\n", Start: 129, End: 138}, + {StartIndex: 0, Needle: "## Stps\n", Start: 129, End: 138}, + {StartIndex: 0, Needle: "## Step\n", Start: 129, End: 138}, } for _, test := range tests { @@ -36,13 +36,14 @@ func TestFuzzyFindAll(t *testing.T) { } } -func TestFuzzyBlockMatch(t *testing.T) { +func TestMatch_FuzzyBlockBaguette(t *testing.T) { recipeMd := testdata.Read(t, "baguette/input.md") schemaMd, err := registry.GetTemplate("Recipe") if err != nil { t.Errorf("Failed to load template: %s", err.Error()) t.FailNow() } + blocks, err := template.CompileTemplate(schemaMd) if err != nil { t.Errorf("Failed to compile template: %s", err.Error()) @@ -51,20 +52,21 @@ func TestFuzzyBlockMatch(t *testing.T) { matches := matcher.MatchBlocksFuzzy(string(recipeMd), blocks, 0.3) + for _, m := range matches { + fmt.Printf("Content: '%s'->'%q'\n\n", m.Block.Path, m.GetContent()) + } + expected := []struct { value string }{ { - value: "@type: Recipe\nauthor.name: Max Richter", + value: "_type: Recipe\nauthor.name: Max Richter", }, { value: "Baguette", }, { - value: "\nMy favourite baguette recipe", - }, - { - value: "", + value: "My favourite baguette recipe", }, { value: "- Flour\n- Water\n- Salt", @@ -80,13 +82,12 @@ func TestFuzzyBlockMatch(t *testing.T) { t.FailNow() } if expected[i].value != m.GetContent() { - t.Errorf("Match %d did not match expected: %q", i, m.GetContent()) + t.Errorf("Match %d did not match expected: %q", i, expected[i].value) } - fmt.Printf("match: %s->%q\n", m.Block.Path, m.GetContent()) } } -func TestFuzzyBlockMatchSalad(t *testing.T) { +func TestMatch_FuzzyBlockSalad(t *testing.T) { recipeMd := testdata.Read(t, "recipe_salad/input.md") schemaMd, err := registry.GetTemplate("Recipe") if err != nil { @@ -95,7 +96,7 @@ func TestFuzzyBlockMatchSalad(t *testing.T) { } blocks, err := template.CompileTemplate(schemaMd) if err != nil { - t.Errorf("Failed to compile template: %s", err.Error()) + t.Errorf("failed to compile template: %s", err.Error()) t.FailNow() } @@ -105,14 +106,11 @@ func TestFuzzyBlockMatchSalad(t *testing.T) { value string }{ { - value: "@type: Recipe\nauthor.name: Alex Chef\ncookTime: PT0M\nimage: https://example.com/salad.jpg\nprepTime: PT10M\nrecipeYield: 2 servings", + value: "_type: Recipe\nauthor.name: Alex Chef\ncookTime: PT0M\nimage: https://example.com/salad.jpg\nprepTime: PT10M\nrecipeYield: 2 servings", }, { value: "Simple Salad", }, - { - value: "#healthy #salad", - }, { value: "A quick green salad.", }, diff --git a/parser/parser.go b/parser/parser.go index 74150a7..2bf5c01 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -15,62 +15,69 @@ import ( func DetectType(markdownContent string) (string, error) { defaultSchemaContent, err := registry.GetTemplate("_default") if err != nil { - return "", fmt.Errorf("could not get schema: %w", err) + return "", fmt.Errorf("could not get schema -> %w", err) } defaultSchema, err := template.CompileTemplate(defaultSchemaContent) if err != nil { - return "", fmt.Errorf("failed to compile template: %w", err) + return "", fmt.Errorf("failed to compile template -> %w", err) } blocks := matcher.MatchBlocksFuzzy(markdownContent, defaultSchema, 0.3) result, err := decoders.Parse(blocks) if err != nil { - return "", fmt.Errorf("failed to parse blocks: %w", err) + return "", fmt.Errorf("failed to parse blocks -> %w", err) } if result, ok := result.(map[string]any); ok { - if contentType, ok := result["@type"]; ok { + if contentType, ok := result["_type"]; ok { return contentType.(string), nil - } else { - return "", fmt.Errorf("frontmatter did not contain '@type'") } - } else { - return "", fmt.Errorf("could not parse frontmatter") + return "", fmt.Errorf("frontmatter did not contain '_type'") } + + return "", fmt.Errorf("could not parse frontmatter") } -func prepareMarkdown(input string) string { - input = strings.TrimSuffix(input, "\n") - input = strings.ReplaceAll(input, "@type:", `"@type":`) - input = strings.ReplaceAll(input, "@context:", `"@context":`) - return input +func MatchBlocks(markdownContent, templateContent string) ([]matcher.Block, error) { + markdownContent = strings.TrimSuffix(markdownContent, "\n") + + tpl, err := template.CompileTemplate(templateContent) + if err != nil { + return nil, fmt.Errorf("failed to compile template -> %w", err) + } + + return matcher.MatchBlocksFuzzy(markdownContent, tpl, 0.3), nil } func ParseFile(markdownContent string) (any, error) { - markdownContent = prepareMarkdown(markdownContent) - contentType, err := DetectType(markdownContent) if err != nil { - return nil, fmt.Errorf("could not detect type: %w", err) + return nil, fmt.Errorf("could not detect type -> %w", err) } templateContent, err := registry.GetTemplate(contentType) if err != nil { - return nil, fmt.Errorf("could not get schema: %w", err) + return nil, fmt.Errorf("could not get template -> %w", err) } - template, err := template.CompileTemplate(templateContent) + return ParseFileWithTemplate(markdownContent, templateContent) +} + +func ParseFileWithTemplate(markdownContent string, templateContent string) (any, error) { + markdownContent = strings.TrimSuffix(markdownContent, "\n") + + tpl, err := template.CompileTemplate(templateContent) if err != nil { - return nil, fmt.Errorf("failed to compile template: %w", err) + return nil, fmt.Errorf("failed to compile template -> %w", err) } - blocks := matcher.MatchBlocksFuzzy(markdownContent, template, 0.3) + blocks := matcher.MatchBlocksFuzzy(markdownContent, tpl, 0.3) result, err := decoders.Parse(blocks) if err != nil { - return nil, fmt.Errorf("failed to parse blocks: %w", err) + return nil, fmt.Errorf("failed to compile blocks -> %w", err) } return result, nil diff --git a/parser/parser_test.go b/parser/parser_test.go index f118c4b..83fe310 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -9,7 +9,30 @@ import ( "github.com/google/go-cmp/cmp" ) -func TestParseRecipe_Golden(t *testing.T) { +func TestParse_DetectType(t *testing.T) { + recipe := testdata.Read(t, "recipe_salad/input.md") + article := testdata.Read(t, "article_simple/input.md") + + recipeType, err := parser.DetectType(string(recipe)) + if err != nil { + t.Fatalf("failed to detect recipeType: %v", err) + } + + articleType, err := parser.DetectType(string(article)) + if err != nil { + t.Fatalf("failed to detect articleType: %v", err) + } + + if recipeType != "Recipe" { + t.Errorf("recipeType did not match expected type 'Recipe' -> %s", recipeType) + } + + if articleType != "Article" { + t.Errorf("articleType did not match expected type 'Article' -> %s", articleType) + } +} + +func TestParse_RecipeSalad(t *testing.T) { inputContent := testdata.Read(t, "recipe_salad/input.md") output := testdata.Read(t, "recipe_salad/output.json") @@ -28,7 +51,7 @@ func TestParseRecipe_Golden(t *testing.T) { } } -func TestParseRecipe_NoDescription(t *testing.T) { +func TestParse_RecipeNoDescription(t *testing.T) { inputContent := testdata.Read(t, "recipe_no_description/input.md") got, err := parser.ParseFile(string(inputContent)) @@ -47,7 +70,7 @@ func TestParseRecipe_NoDescription(t *testing.T) { } } -func TestParseRecipe_Baguette(t *testing.T) { +func TestParse_Baguette(t *testing.T) { inputContent := testdata.Read(t, "baguette/input.md") got, err := parser.ParseFile(string(inputContent)) @@ -66,7 +89,7 @@ func TestParseRecipe_Baguette(t *testing.T) { } } -func TestParseArticle_Simple(t *testing.T) { +func TestParse_Article(t *testing.T) { inputContent := testdata.Read(t, "article_simple/input.md") got, err := parser.ParseFile(string(inputContent)) diff --git a/playground/.gitignore b/playground/.gitignore new file mode 100644 index 0000000..3b462cb --- /dev/null +++ b/playground/.gitignore @@ -0,0 +1,23 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/playground/.npmrc b/playground/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/playground/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/playground/.prettierignore b/playground/.prettierignore new file mode 100644 index 0000000..7d74fe2 --- /dev/null +++ b/playground/.prettierignore @@ -0,0 +1,9 @@ +# Package Managers +package-lock.json +pnpm-lock.yaml +yarn.lock +bun.lock +bun.lockb + +# Miscellaneous +/static/ diff --git a/playground/.prettierrc b/playground/.prettierrc new file mode 100644 index 0000000..8103a0b --- /dev/null +++ b/playground/.prettierrc @@ -0,0 +1,16 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ], + "tailwindStylesheet": "./src/app.css" +} diff --git a/playground/README.md b/playground/README.md new file mode 100644 index 0000000..75842c4 --- /dev/null +++ b/playground/README.md @@ -0,0 +1,38 @@ +# sv + +Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```sh +# create a new project in the current directory +npx sv create + +# create a new project in my-app +npx sv create my-app +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```sh +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```sh +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. diff --git a/playground/eslint.config.js b/playground/eslint.config.js new file mode 100644 index 0000000..29b1aca --- /dev/null +++ b/playground/eslint.config.js @@ -0,0 +1,42 @@ +import prettier from 'eslint-config-prettier'; +import { fileURLToPath } from 'node:url'; +import { includeIgnoreFile } from '@eslint/compat'; +import js from '@eslint/js'; +import svelte from 'eslint-plugin-svelte'; +import { defineConfig } from 'eslint/config'; +import globals from 'globals'; +import ts from 'typescript-eslint'; +import svelteConfig from './svelte.config.js'; + +const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url)); + +export default defineConfig( + includeIgnoreFile(gitignorePath), + { ignores: ['static/wasm_exec.js'] }, + js.configs.recommended, + ...ts.configs.recommended, + ...svelte.configs.recommended, + prettier, + ...svelte.configs.prettier, + { + languageOptions: { + globals: { ...globals.browser, ...globals.node } + }, + rules: { + // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. + // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors + 'no-undef': 'off' + } + }, + { + files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], + languageOptions: { + parserOptions: { + projectService: true, + extraFileExtensions: ['.svelte'], + parser: ts.parser, + svelteConfig + } + } + } +); diff --git a/playground/package.json b/playground/package.json new file mode 100644 index 0000000..7f60f2a --- /dev/null +++ b/playground/package.json @@ -0,0 +1,58 @@ +{ + "name": "playground", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "format": "prettier --write .", + "lint": "prettier --check . && eslint ." + }, + "devDependencies": { + "@eslint/compat": "^1.2.5", + "@eslint/js": "^9.22.0", + "@sveltejs/adapter-auto": "^6.0.0", + "@sveltejs/adapter-static": "^3.0.9", + "@sveltejs/kit": "^2.22.0", + "@sveltejs/vite-plugin-svelte": "^6.0.0", + "@tailwindcss/vite": "^4.0.0", + "@types/node": "^22", + "eslint": "^9.22.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-svelte": "^3.0.0", + "globals": "^16.0.0", + "prettier": "^3.4.2", + "prettier-plugin-svelte": "^3.3.3", + "prettier-plugin-tailwindcss": "^0.6.11", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "tailwindcss": "^4.0.0", + "typescript": "^5.0.0", + "typescript-eslint": "^8.20.0", + "vite": "^7.0.4" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "esbuild" + ] + }, + "dependencies": { + "@codemirror/lang-javascript": "^6.2.4", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/lang-markdown": "^6.3.4", + "@codemirror/lang-yaml": "^6.1.2", + "@codemirror/language": "^6.11.3", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.38.3", + "@fsegurai/codemirror-theme-github-light": "^6.2.2", + "codemirror": "^6.0.2", + "codemirror-theme-github": "^1.1.0", + "lucide-svelte": "^0.544.0", + "svelte-codemirror-editor": "^2.0.0" + } +} diff --git a/playground/pnpm-lock.yaml b/playground/pnpm-lock.yaml new file mode 100644 index 0000000..57b2af5 --- /dev/null +++ b/playground/pnpm-lock.yaml @@ -0,0 +1,2933 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@codemirror/lang-javascript': + specifier: ^6.2.4 + version: 6.2.4 + '@codemirror/lang-json': + specifier: ^6.0.2 + version: 6.0.2 + '@codemirror/lang-markdown': + specifier: ^6.3.4 + version: 6.3.4 + '@codemirror/lang-yaml': + specifier: ^6.1.2 + version: 6.1.2 + '@codemirror/language': + specifier: ^6.11.3 + version: 6.11.3 + '@codemirror/state': + specifier: ^6.5.2 + version: 6.5.2 + '@codemirror/view': + specifier: ^6.38.3 + version: 6.38.3 + '@fsegurai/codemirror-theme-github-light': + specifier: ^6.2.2 + version: 6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.3)(@lezer/highlight@1.2.1) + codemirror: + specifier: ^6.0.2 + version: 6.0.2 + codemirror-theme-github: + specifier: ^1.1.0 + version: 1.1.0 + lucide-svelte: + specifier: ^0.544.0 + version: 0.544.0(svelte@5.39.6) + svelte-codemirror-editor: + specifier: ^2.0.0 + version: 2.0.0(codemirror@6.0.2)(svelte@5.39.6) + devDependencies: + '@eslint/compat': + specifier: ^1.2.5 + version: 1.4.0(eslint@9.36.0(jiti@2.6.0)) + '@eslint/js': + specifier: ^9.22.0 + version: 9.36.0 + '@sveltejs/adapter-auto': + specifier: ^6.0.0 + version: 6.1.0(@sveltejs/kit@2.43.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)))(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1))) + '@sveltejs/adapter-static': + specifier: ^3.0.9 + version: 3.0.9(@sveltejs/kit@2.43.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)))(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1))) + '@sveltejs/kit': + specifier: ^2.22.0 + version: 2.43.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)))(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)) + '@sveltejs/vite-plugin-svelte': + specifier: ^6.0.0 + version: 6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)) + '@tailwindcss/vite': + specifier: ^4.0.0 + version: 4.1.13(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)) + '@types/node': + specifier: ^22 + version: 22.18.6 + eslint: + specifier: ^9.22.0 + version: 9.36.0(jiti@2.6.0) + eslint-config-prettier: + specifier: ^10.0.1 + version: 10.1.8(eslint@9.36.0(jiti@2.6.0)) + eslint-plugin-svelte: + specifier: ^3.0.0 + version: 3.12.4(eslint@9.36.0(jiti@2.6.0))(svelte@5.39.6) + globals: + specifier: ^16.0.0 + version: 16.4.0 + prettier: + specifier: ^3.4.2 + version: 3.6.2 + prettier-plugin-svelte: + specifier: ^3.3.3 + version: 3.4.0(prettier@3.6.2)(svelte@5.39.6) + prettier-plugin-tailwindcss: + specifier: ^0.6.11 + version: 0.6.14(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.39.6))(prettier@3.6.2) + svelte: + specifier: ^5.0.0 + version: 5.39.6 + svelte-check: + specifier: ^4.0.0 + version: 4.3.2(picomatch@4.0.3)(svelte@5.39.6)(typescript@5.9.2) + tailwindcss: + specifier: ^4.0.0 + version: 4.1.13 + typescript: + specifier: ^5.0.0 + version: 5.9.2 + typescript-eslint: + specifier: ^8.20.0 + version: 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + vite: + specifier: ^7.0.4 + version: 7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1) + +packages: + + '@codemirror/autocomplete@6.18.7': + resolution: {integrity: sha512-8EzdeIoWPJDsMBwz3zdzwXnUpCzMiCyz5/A3FIPpriaclFCGDkAzK13sMcnsu5rowqiyeQN2Vs2TsOcoDPZirQ==} + + '@codemirror/commands@6.8.1': + resolution: {integrity: sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==} + + '@codemirror/lang-css@6.3.1': + resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==} + + '@codemirror/lang-html@6.4.10': + resolution: {integrity: sha512-h/SceTVsN5r+WE+TVP2g3KDvNoSzbSrtZXCKo4vkKdbfT5t4otuVgngGdFukOO/rwRD2++pCxoh6xD4TEVMkQA==} + + '@codemirror/lang-javascript@6.2.4': + resolution: {integrity: sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==} + + '@codemirror/lang-json@6.0.2': + resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==} + + '@codemirror/lang-markdown@6.3.4': + resolution: {integrity: sha512-fBm0BO03azXnTAsxhONDYHi/qWSI+uSEIpzKM7h/bkIc9fHnFp9y7KTMXKON0teNT97pFhc1a9DQTtWBYEZ7ug==} + + '@codemirror/lang-yaml@6.1.2': + resolution: {integrity: sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==} + + '@codemirror/language@6.11.3': + resolution: {integrity: sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==} + + '@codemirror/lint@6.8.5': + resolution: {integrity: sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==} + + '@codemirror/search@6.5.11': + resolution: {integrity: sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==} + + '@codemirror/state@6.5.2': + resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} + + '@codemirror/view@6.38.3': + resolution: {integrity: sha512-x2t87+oqwB1mduiQZ6huIghjMt4uZKFEdj66IcXw7+a5iBEvv9lh7EWDRHI7crnD4BMGpnyq/RzmCGbiEZLcvQ==} + + '@esbuild/aix-ppc64@0.25.10': + resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.10': + resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.10': + resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.10': + resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.10': + resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.10': + resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.10': + resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.10': + resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.10': + resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.10': + resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.10': + resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.10': + resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.10': + resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.10': + resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.10': + resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.10': + resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.10': + resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.10': + resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.10': + resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.10': + resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.10': + resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.10': + resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.10': + resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.10': + resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.10': + resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.10': + resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/compat@1.4.0': + resolution: {integrity: sha512-DEzm5dKeDBPm3r08Ixli/0cmxr8LkRdwxMRUIJBlSCpAwSrvFEJpVBzV+66JhDxiaqKxnRzCXhtiMiczF7Hglg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.40 || 9 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.3.1': + resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.2': + resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.16.0': + resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.36.0': + resolution: {integrity: sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.5': + resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@fsegurai/codemirror-theme-github-light@6.2.2': + resolution: {integrity: sha512-YQr5MbhMlhRlAQcSCSbet4NDDkMvd5sbUyk9JmM0vfZhQbatvw4c56gNG/54JKGM0kWY5zRWzgLtFuz6D7yEsw==} + peerDependencies: + '@codemirror/language': ^6.0.0 + '@codemirror/state': ^6.0.0 + '@codemirror/view': ^6.0.0 + '@lezer/highlight': ^1.0.0 + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@lezer/common@1.2.3': + resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} + + '@lezer/css@1.3.0': + resolution: {integrity: sha512-pBL7hup88KbI7hXnZV3PQsn43DHy6TWyzuyk2AO9UyoXcDltvIdqWKE1dLL/45JVZ+YZkHe1WVHqO6wugZZWcw==} + + '@lezer/highlight@1.2.1': + resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} + + '@lezer/html@1.3.11': + resolution: {integrity: sha512-SV04kK5EHDPPecMCiFNZAnQhUIxktP04yHxgOKK7TZ3+KUAlK9f4dcYbjAWwDx2C2pJmiOeSV05QEbHeQo5JqA==} + + '@lezer/javascript@1.5.4': + resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==} + + '@lezer/json@1.0.3': + resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==} + + '@lezer/lr@1.4.2': + resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} + + '@lezer/markdown@1.4.3': + resolution: {integrity: sha512-kfw+2uMrQ/wy/+ONfrH83OkdFNM0ye5Xq96cLlaCy7h5UT9FO54DU4oRoIc0CSBh5NWmWuiIJA7NGLMJbQ+Oxg==} + + '@lezer/yaml@1.0.3': + resolution: {integrity: sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==} + + '@marijn/find-cluster-break@1.0.2': + resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@rollup/rollup-android-arm-eabi@4.52.2': + resolution: {integrity: sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.52.2': + resolution: {integrity: sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.52.2': + resolution: {integrity: sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.52.2': + resolution: {integrity: sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.52.2': + resolution: {integrity: sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.52.2': + resolution: {integrity: sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.52.2': + resolution: {integrity: sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.52.2': + resolution: {integrity: sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.52.2': + resolution: {integrity: sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.52.2': + resolution: {integrity: sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.52.2': + resolution: {integrity: sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.52.2': + resolution: {integrity: sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.52.2': + resolution: {integrity: sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.52.2': + resolution: {integrity: sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.52.2': + resolution: {integrity: sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.52.2': + resolution: {integrity: sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.52.2': + resolution: {integrity: sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.52.2': + resolution: {integrity: sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.52.2': + resolution: {integrity: sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.52.2': + resolution: {integrity: sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.52.2': + resolution: {integrity: sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.52.2': + resolution: {integrity: sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw==} + cpu: [x64] + os: [win32] + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@sveltejs/acorn-typescript@1.0.5': + resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==} + peerDependencies: + acorn: ^8.9.0 + + '@sveltejs/adapter-auto@6.1.0': + resolution: {integrity: sha512-shOuLI5D2s+0zTv2ab5M5PqfknXqWbKi+0UwB9yLTRIdzsK1R93JOO8jNhIYSHdW+IYXIYnLniu+JZqXs7h9Wg==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + + '@sveltejs/adapter-static@3.0.9': + resolution: {integrity: sha512-aytHXcMi7lb9ljsWUzXYQ0p5X1z9oWud2olu/EpmH7aCu4m84h7QLvb5Wp+CFirKcwoNnYvYWhyP/L8Vh1ztdw==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + + '@sveltejs/kit@2.43.4': + resolution: {integrity: sha512-GfvOq3A/qMRhj2L9eKjxaI8FLqZDh5SY74YzhRKT//u2AvQw96ksEfjuHviC4jg9U08mBVB0Y47EwEJHO4BB4Q==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.0.0 + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + + '@sveltejs/vite-plugin-svelte-inspector@5.0.1': + resolution: {integrity: sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0 + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@sveltejs/vite-plugin-svelte@6.2.1': + resolution: {integrity: sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@tailwindcss/node@4.1.13': + resolution: {integrity: sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==} + + '@tailwindcss/oxide-android-arm64@4.1.13': + resolution: {integrity: sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.13': + resolution: {integrity: sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.13': + resolution: {integrity: sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.13': + resolution: {integrity: sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': + resolution: {integrity: sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': + resolution: {integrity: sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.13': + resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.13': + resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.13': + resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.13': + resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': + resolution: {integrity: sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.13': + resolution: {integrity: sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.13': + resolution: {integrity: sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.13': + resolution: {integrity: sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@22.18.6': + resolution: {integrity: sha512-r8uszLPpeIWbNKtvWRt/DbVi5zbqZyj1PTmhRMqBMvDnaz1QpmSKujUtJLrqGZeoM8v72MfYggDceY4K1itzWQ==} + + '@typescript-eslint/eslint-plugin@8.44.1': + resolution: {integrity: sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.44.1 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.44.1': + resolution: {integrity: sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.44.1': + resolution: {integrity: sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.44.1': + resolution: {integrity: sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.44.1': + resolution: {integrity: sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.44.1': + resolution: {integrity: sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.44.1': + resolution: {integrity: sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.44.1': + resolution: {integrity: sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.44.1': + resolution: {integrity: sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.44.1': + resolution: {integrity: sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + codemirror-theme-github@1.1.0: + resolution: {integrity: sha512-05qv2eBNdrLvkXx6gdW4IFnRQN0NbUOew8/x4/B62I5UfnIDaPUnoGzqQUluwsBhWTMM7EpFSXSPNCqvcwB89g==} + + codemirror@6.0.2: + resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + detect-libc@2.1.1: + resolution: {integrity: sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==} + engines: {node: '>=8'} + + devalue@5.3.2: + resolution: {integrity: sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + + esbuild@0.25.10: + resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-svelte@3.12.4: + resolution: {integrity: sha512-hD7wPe+vrPgx3U2X2b/wyTMtWobm660PygMGKrWWYTc9lvtY8DpNFDaU2CJQn1szLjGbn/aJ3g8WiXuKakrEkw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.1 || ^9.0.0 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.36.0: + resolution: {integrity: sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrap@2.1.0: + resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + engines: {node: '>=18'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jiti@2.6.0: + resolution: {integrity: sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + known-css-properties@0.37.0: + resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lucide-svelte@0.544.0: + resolution: {integrity: sha512-8kBxSivf8SJdEUJRHBpu9bRw0S/qfVK+Yfb92KQnRRBdP425RzT6aQfrIfZctG1oucPVTBQe1ZXgmth/3qVICg==} + peerDependencies: + svelte: ^3 || ^4 || ^5.0.0-next.42 + + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + postcss-load-config@3.1.4: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + engines: {node: '>=4'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-plugin-svelte@3.4.0: + resolution: {integrity: sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==} + peerDependencies: + prettier: ^3.0.0 + svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 + + prettier-plugin-tailwindcss@0.6.14: + resolution: {integrity: sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==} + engines: {node: '>=14.21.3'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-hermes': '*' + '@prettier/plugin-oxc': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-hermes': + optional: true + '@prettier/plugin-oxc': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@4.52.2: + resolution: {integrity: sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + style-mod@4.1.2: + resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + svelte-check@4.3.2: + resolution: {integrity: sha512-71udP5w2kaSTcX8iV0hn3o2FWlabQHhJTJLIQrCqMsrcOeDUO2VhCQKKCA8AMVHSPwdxLEWkUWh9OKxns5PD9w==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + + svelte-codemirror-editor@2.0.0: + resolution: {integrity: sha512-LJyCPGzOHL290ec4dLBsVyu6e5dsk2pLKUBfU8wA5/EN7Zgc0UXxjNeEsDHGLZhZr2RYMiYwa7tLRZCHOtsxCg==} + peerDependencies: + codemirror: ^6.0.0 + svelte: ^5.0.0 + + svelte-eslint-parser@1.3.3: + resolution: {integrity: sha512-oTrDR8Z7Wnguut7QH3YKh7JR19xv1seB/bz4dxU5J/86eJtZOU4eh0/jZq4dy6tAlz/KROxnkRQspv5ZEt7t+Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + svelte@5.39.6: + resolution: {integrity: sha512-bOJXmuwLNaoqPCTWO8mPu/fwxI5peGE5Efe7oo6Cakpz/G60vsnVF6mxbGODaxMUFUKEnjm6XOwHEqOht6cbvw==} + engines: {node: '>=18'} + + tailwindcss@4.1.13: + resolution: {integrity: sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==} + + tapable@2.2.3: + resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} + engines: {node: '>=6'} + + tar@7.5.1: + resolution: {integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==} + engines: {node: '>=18'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript-eslint@8.44.1: + resolution: {integrity: sha512-0ws8uWGrUVTjEeN2OM4K1pLKHK/4NiNP/vz6ns+LjT/6sqpaYzIVFajZb1fj/IDwpsrrHb3Jy0Qm5u9CPcKaeg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite@7.1.7: + resolution: {integrity: sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.1: + resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + vite: + optional: true + + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + +snapshots: + + '@codemirror/autocomplete@6.18.7': + dependencies: + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.3 + '@lezer/common': 1.2.3 + + '@codemirror/commands@6.8.1': + dependencies: + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.3 + '@lezer/common': 1.2.3 + + '@codemirror/lang-css@6.3.1': + dependencies: + '@codemirror/autocomplete': 6.18.7 + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@lezer/common': 1.2.3 + '@lezer/css': 1.3.0 + + '@codemirror/lang-html@6.4.10': + dependencies: + '@codemirror/autocomplete': 6.18.7 + '@codemirror/lang-css': 6.3.1 + '@codemirror/lang-javascript': 6.2.4 + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.3 + '@lezer/common': 1.2.3 + '@lezer/css': 1.3.0 + '@lezer/html': 1.3.11 + + '@codemirror/lang-javascript@6.2.4': + dependencies: + '@codemirror/autocomplete': 6.18.7 + '@codemirror/language': 6.11.3 + '@codemirror/lint': 6.8.5 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.3 + '@lezer/common': 1.2.3 + '@lezer/javascript': 1.5.4 + + '@codemirror/lang-json@6.0.2': + dependencies: + '@codemirror/language': 6.11.3 + '@lezer/json': 1.0.3 + + '@codemirror/lang-markdown@6.3.4': + dependencies: + '@codemirror/autocomplete': 6.18.7 + '@codemirror/lang-html': 6.4.10 + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.3 + '@lezer/common': 1.2.3 + '@lezer/markdown': 1.4.3 + + '@codemirror/lang-yaml@6.1.2': + dependencies: + '@codemirror/autocomplete': 6.18.7 + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + '@lezer/yaml': 1.0.3 + + '@codemirror/language@6.11.3': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.3 + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + style-mod: 4.1.2 + + '@codemirror/lint@6.8.5': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.3 + crelt: 1.0.6 + + '@codemirror/search@6.5.11': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.3 + crelt: 1.0.6 + + '@codemirror/state@6.5.2': + dependencies: + '@marijn/find-cluster-break': 1.0.2 + + '@codemirror/view@6.38.3': + dependencies: + '@codemirror/state': 6.5.2 + crelt: 1.0.6 + style-mod: 4.1.2 + w3c-keyname: 2.2.8 + + '@esbuild/aix-ppc64@0.25.10': + optional: true + + '@esbuild/android-arm64@0.25.10': + optional: true + + '@esbuild/android-arm@0.25.10': + optional: true + + '@esbuild/android-x64@0.25.10': + optional: true + + '@esbuild/darwin-arm64@0.25.10': + optional: true + + '@esbuild/darwin-x64@0.25.10': + optional: true + + '@esbuild/freebsd-arm64@0.25.10': + optional: true + + '@esbuild/freebsd-x64@0.25.10': + optional: true + + '@esbuild/linux-arm64@0.25.10': + optional: true + + '@esbuild/linux-arm@0.25.10': + optional: true + + '@esbuild/linux-ia32@0.25.10': + optional: true + + '@esbuild/linux-loong64@0.25.10': + optional: true + + '@esbuild/linux-mips64el@0.25.10': + optional: true + + '@esbuild/linux-ppc64@0.25.10': + optional: true + + '@esbuild/linux-riscv64@0.25.10': + optional: true + + '@esbuild/linux-s390x@0.25.10': + optional: true + + '@esbuild/linux-x64@0.25.10': + optional: true + + '@esbuild/netbsd-arm64@0.25.10': + optional: true + + '@esbuild/netbsd-x64@0.25.10': + optional: true + + '@esbuild/openbsd-arm64@0.25.10': + optional: true + + '@esbuild/openbsd-x64@0.25.10': + optional: true + + '@esbuild/openharmony-arm64@0.25.10': + optional: true + + '@esbuild/sunos-x64@0.25.10': + optional: true + + '@esbuild/win32-arm64@0.25.10': + optional: true + + '@esbuild/win32-ia32@0.25.10': + optional: true + + '@esbuild/win32-x64@0.25.10': + optional: true + + '@eslint-community/eslint-utils@4.9.0(eslint@9.36.0(jiti@2.6.0))': + dependencies: + eslint: 9.36.0(jiti@2.6.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/compat@1.4.0(eslint@9.36.0(jiti@2.6.0))': + dependencies: + '@eslint/core': 0.16.0 + optionalDependencies: + eslint: 9.36.0(jiti@2.6.0) + + '@eslint/config-array@0.21.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.3.1': {} + + '@eslint/core@0.15.2': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.16.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.36.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.5': + dependencies: + '@eslint/core': 0.15.2 + levn: 0.4.1 + + '@fsegurai/codemirror-theme-github-light@6.2.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.3)(@lezer/highlight@1.2.1)': + dependencies: + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.3 + '@lezer/highlight': 1.2.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@lezer/common@1.2.3': {} + + '@lezer/css@1.3.0': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@lezer/highlight@1.2.1': + dependencies: + '@lezer/common': 1.2.3 + + '@lezer/html@1.3.11': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@lezer/javascript@1.5.4': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@lezer/json@1.0.3': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@lezer/lr@1.4.2': + dependencies: + '@lezer/common': 1.2.3 + + '@lezer/markdown@1.4.3': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + + '@lezer/yaml@1.0.3': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@marijn/find-cluster-break@1.0.2': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@polka/url@1.0.0-next.29': {} + + '@rollup/rollup-android-arm-eabi@4.52.2': + optional: true + + '@rollup/rollup-android-arm64@4.52.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.52.2': + optional: true + + '@rollup/rollup-darwin-x64@4.52.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.52.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.52.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.52.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.52.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.52.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.52.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.52.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.52.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.52.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.52.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.52.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.52.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.52.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.52.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.52.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.52.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.52.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.52.2': + optional: true + + '@standard-schema/spec@1.0.0': {} + + '@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)': + dependencies: + acorn: 8.15.0 + + '@sveltejs/adapter-auto@6.1.0(@sveltejs/kit@2.43.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)))(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)))': + dependencies: + '@sveltejs/kit': 2.43.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)))(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)) + + '@sveltejs/adapter-static@3.0.9(@sveltejs/kit@2.43.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)))(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)))': + dependencies: + '@sveltejs/kit': 2.43.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)))(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)) + + '@sveltejs/kit@2.43.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)))(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1))': + dependencies: + '@standard-schema/spec': 1.0.0 + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)) + '@types/cookie': 0.6.0 + acorn: 8.15.0 + cookie: 0.6.0 + devalue: 5.3.2 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.19 + mrmime: 2.0.1 + sade: 1.8.1 + set-cookie-parser: 2.7.1 + sirv: 3.0.2 + svelte: 5.39.6 + vite: 7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1) + + '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)))(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1))': + dependencies: + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)) + debug: 4.4.3 + svelte: 5.39.6 + vite: 7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1) + transitivePeerDependencies: + - supports-color + + '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)))(svelte@5.39.6)(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)) + debug: 4.4.3 + deepmerge: 4.3.1 + magic-string: 0.30.19 + svelte: 5.39.6 + vite: 7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1) + vitefu: 1.1.1(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)) + transitivePeerDependencies: + - supports-color + + '@tailwindcss/node@4.1.13': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.6.0 + lightningcss: 1.30.1 + magic-string: 0.30.19 + source-map-js: 1.2.1 + tailwindcss: 4.1.13 + + '@tailwindcss/oxide-android-arm64@4.1.13': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.13': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.13': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.13': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.13': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.13': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.13': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.13': + optional: true + + '@tailwindcss/oxide@4.1.13': + dependencies: + detect-libc: 2.1.1 + tar: 7.5.1 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.13 + '@tailwindcss/oxide-darwin-arm64': 4.1.13 + '@tailwindcss/oxide-darwin-x64': 4.1.13 + '@tailwindcss/oxide-freebsd-x64': 4.1.13 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.13 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.13 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.13 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.13 + '@tailwindcss/oxide-linux-x64-musl': 4.1.13 + '@tailwindcss/oxide-wasm32-wasi': 4.1.13 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.13 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.13 + + '@tailwindcss/vite@4.1.13(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1))': + dependencies: + '@tailwindcss/node': 4.1.13 + '@tailwindcss/oxide': 4.1.13 + tailwindcss: 4.1.13 + vite: 7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1) + + '@types/cookie@0.6.0': {} + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@22.18.6': + dependencies: + undici-types: 6.21.0 + + '@typescript-eslint/eslint-plugin@8.44.1(@typescript-eslint/parser@8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.44.1 + '@typescript-eslint/type-utils': 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + '@typescript-eslint/utils': 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.44.1 + eslint: 9.36.0(jiti@2.6.0) + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)': + dependencies: + '@typescript-eslint/scope-manager': 8.44.1 + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.44.1 + debug: 4.4.3 + eslint: 9.36.0(jiti@2.6.0) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.44.1(typescript@5.9.2)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.44.1(typescript@5.9.2) + '@typescript-eslint/types': 8.44.1 + debug: 4.4.3 + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.44.1': + dependencies: + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/visitor-keys': 8.44.1 + + '@typescript-eslint/tsconfig-utils@8.44.1(typescript@5.9.2)': + dependencies: + typescript: 5.9.2 + + '@typescript-eslint/type-utils@8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)': + dependencies: + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.9.2) + '@typescript-eslint/utils': 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + debug: 4.4.3 + eslint: 9.36.0(jiti@2.6.0) + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.44.1': {} + + '@typescript-eslint/typescript-estree@8.44.1(typescript@5.9.2)': + dependencies: + '@typescript-eslint/project-service': 8.44.1(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.44.1(typescript@5.9.2) + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/visitor-keys': 8.44.1 + debug: 4.4.3 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.6.0)) + '@typescript-eslint/scope-manager': 8.44.1 + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.9.2) + eslint: 9.36.0(jiti@2.6.0) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.44.1': + dependencies: + '@typescript-eslint/types': 8.44.1 + eslint-visitor-keys: 4.2.1 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chownr@3.0.0: {} + + clsx@2.1.1: {} + + codemirror-theme-github@1.1.0: {} + + codemirror@6.0.2: + dependencies: + '@codemirror/autocomplete': 6.18.7 + '@codemirror/commands': 6.8.1 + '@codemirror/language': 6.11.3 + '@codemirror/lint': 6.8.5 + '@codemirror/search': 6.5.11 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + cookie@0.6.0: {} + + crelt@1.0.6: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssesc@3.0.0: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + detect-libc@2.1.1: {} + + devalue@5.3.2: {} + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.3 + + esbuild@0.25.10: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.10 + '@esbuild/android-arm': 0.25.10 + '@esbuild/android-arm64': 0.25.10 + '@esbuild/android-x64': 0.25.10 + '@esbuild/darwin-arm64': 0.25.10 + '@esbuild/darwin-x64': 0.25.10 + '@esbuild/freebsd-arm64': 0.25.10 + '@esbuild/freebsd-x64': 0.25.10 + '@esbuild/linux-arm': 0.25.10 + '@esbuild/linux-arm64': 0.25.10 + '@esbuild/linux-ia32': 0.25.10 + '@esbuild/linux-loong64': 0.25.10 + '@esbuild/linux-mips64el': 0.25.10 + '@esbuild/linux-ppc64': 0.25.10 + '@esbuild/linux-riscv64': 0.25.10 + '@esbuild/linux-s390x': 0.25.10 + '@esbuild/linux-x64': 0.25.10 + '@esbuild/netbsd-arm64': 0.25.10 + '@esbuild/netbsd-x64': 0.25.10 + '@esbuild/openbsd-arm64': 0.25.10 + '@esbuild/openbsd-x64': 0.25.10 + '@esbuild/openharmony-arm64': 0.25.10 + '@esbuild/sunos-x64': 0.25.10 + '@esbuild/win32-arm64': 0.25.10 + '@esbuild/win32-ia32': 0.25.10 + '@esbuild/win32-x64': 0.25.10 + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.8(eslint@9.36.0(jiti@2.6.0)): + dependencies: + eslint: 9.36.0(jiti@2.6.0) + + eslint-plugin-svelte@3.12.4(eslint@9.36.0(jiti@2.6.0))(svelte@5.39.6): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.6.0)) + '@jridgewell/sourcemap-codec': 1.5.5 + eslint: 9.36.0(jiti@2.6.0) + esutils: 2.0.3 + globals: 16.4.0 + known-css-properties: 0.37.0 + postcss: 8.5.6 + postcss-load-config: 3.1.4(postcss@8.5.6) + postcss-safe-parser: 7.0.1(postcss@8.5.6) + semver: 7.7.2 + svelte-eslint-parser: 1.3.3(svelte@5.39.6) + optionalDependencies: + svelte: 5.39.6 + transitivePeerDependencies: + - ts-node + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.36.0(jiti@2.6.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.6.0)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.1 + '@eslint/core': 0.15.2 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.36.0 + '@eslint/plugin-kit': 0.3.5 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.0 + transitivePeerDependencies: + - supports-color + + esm-env@1.2.2: {} + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrap@2.1.0: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + fsevents@2.3.3: + optional: true + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.4.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + isexe@2.0.0: {} + + jiti@2.6.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@4.1.5: {} + + known-css-properties@0.37.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true + + lightningcss-win32-x64-msvc@1.30.1: + optional: true + + lightningcss@1.30.1: + dependencies: + detect-libc: 2.1.1 + optionalDependencies: + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 + + lilconfig@2.1.0: {} + + locate-character@3.0.0: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lucide-svelte@0.544.0(svelte@5.39.6): + dependencies: + svelte: 5.39.6 + + magic-string@0.30.19: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minipass@7.1.2: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.2 + + mri@1.2.0: {} + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + postcss-load-config@3.1.4(postcss@8.5.6): + dependencies: + lilconfig: 2.1.0 + yaml: 1.10.2 + optionalDependencies: + postcss: 8.5.6 + + postcss-safe-parser@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-scss@4.0.9(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-selector-parser@7.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.39.6): + dependencies: + prettier: 3.6.2 + svelte: 5.39.6 + + prettier-plugin-tailwindcss@0.6.14(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.39.6))(prettier@3.6.2): + dependencies: + prettier: 3.6.2 + optionalDependencies: + prettier-plugin-svelte: 3.4.0(prettier@3.6.2)(svelte@5.39.6) + + prettier@3.6.2: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + readdirp@4.1.2: {} + + resolve-from@4.0.0: {} + + reusify@1.1.0: {} + + rollup@4.52.2: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.52.2 + '@rollup/rollup-android-arm64': 4.52.2 + '@rollup/rollup-darwin-arm64': 4.52.2 + '@rollup/rollup-darwin-x64': 4.52.2 + '@rollup/rollup-freebsd-arm64': 4.52.2 + '@rollup/rollup-freebsd-x64': 4.52.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.2 + '@rollup/rollup-linux-arm-musleabihf': 4.52.2 + '@rollup/rollup-linux-arm64-gnu': 4.52.2 + '@rollup/rollup-linux-arm64-musl': 4.52.2 + '@rollup/rollup-linux-loong64-gnu': 4.52.2 + '@rollup/rollup-linux-ppc64-gnu': 4.52.2 + '@rollup/rollup-linux-riscv64-gnu': 4.52.2 + '@rollup/rollup-linux-riscv64-musl': 4.52.2 + '@rollup/rollup-linux-s390x-gnu': 4.52.2 + '@rollup/rollup-linux-x64-gnu': 4.52.2 + '@rollup/rollup-linux-x64-musl': 4.52.2 + '@rollup/rollup-openharmony-arm64': 4.52.2 + '@rollup/rollup-win32-arm64-msvc': 4.52.2 + '@rollup/rollup-win32-ia32-msvc': 4.52.2 + '@rollup/rollup-win32-x64-gnu': 4.52.2 + '@rollup/rollup-win32-x64-msvc': 4.52.2 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + semver@7.7.2: {} + + set-cookie-parser@2.7.1: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + source-map-js@1.2.1: {} + + strip-json-comments@3.1.1: {} + + style-mod@4.1.2: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + svelte-check@4.3.2(picomatch@4.0.3)(svelte@5.39.6)(typescript@5.9.2): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + chokidar: 4.0.3 + fdir: 6.5.0(picomatch@4.0.3) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.39.6 + typescript: 5.9.2 + transitivePeerDependencies: + - picomatch + + svelte-codemirror-editor@2.0.0(codemirror@6.0.2)(svelte@5.39.6): + dependencies: + codemirror: 6.0.2 + svelte: 5.39.6 + + svelte-eslint-parser@1.3.3(svelte@5.39.6): + dependencies: + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + postcss: 8.5.6 + postcss-scss: 4.0.9(postcss@8.5.6) + postcss-selector-parser: 7.1.0 + optionalDependencies: + svelte: 5.39.6 + + svelte@5.39.6: + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@types/estree': 1.0.8 + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + esm-env: 1.2.2 + esrap: 2.1.0 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.19 + zimmerframe: 1.1.4 + + tailwindcss@4.1.13: {} + + tapable@2.2.3: {} + + tar@7.5.1: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.1.0 + yallist: 5.0.0 + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + totalist@3.0.1: {} + + ts-api-utils@2.1.0(typescript@5.9.2): + dependencies: + typescript: 5.9.2 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2): + dependencies: + '@typescript-eslint/eslint-plugin': 8.44.1(@typescript-eslint/parser@8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + '@typescript-eslint/parser': 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.9.2) + '@typescript-eslint/utils': 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2) + eslint: 9.36.0(jiti@2.6.0) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + typescript@5.9.2: {} + + undici-types@6.21.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1): + dependencies: + esbuild: 0.25.10 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.52.2 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.18.6 + fsevents: 2.3.3 + jiti: 2.6.0 + lightningcss: 1.30.1 + + vitefu@1.1.1(vite@7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1)): + optionalDependencies: + vite: 7.1.7(@types/node@22.18.6)(jiti@2.6.0)(lightningcss@1.30.1) + + w3c-keyname@2.2.8: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yallist@5.0.0: {} + + yaml@1.10.2: {} + + yocto-queue@0.1.0: {} + + zimmerframe@1.1.4: {} diff --git a/playground/src/app.css b/playground/src/app.css new file mode 100644 index 0000000..96ed033 --- /dev/null +++ b/playground/src/app.css @@ -0,0 +1,6 @@ +@import 'tailwindcss'; + + +.cm-foldGutter + .cm-foldGutter { + display: none !important; +} diff --git a/playground/src/app.d.ts b/playground/src/app.d.ts new file mode 100644 index 0000000..47ac7b0 --- /dev/null +++ b/playground/src/app.d.ts @@ -0,0 +1,30 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } + + class Go { + new(): { + run: (inst: WebAssembly.Instance) => Promise; + importObject: WebAssembly.Imports; + }; + } + + const marka: { + matchBlocks(s: string, t: string): string; + detectType(markdown: string): string; + parseFile(input: string): string; + parseFileWithTemplate(markdown: string, template: string): string; + listTemplates(): string; + getTemplate(name: string): string; + compileTemplate(source: string): string; + }; +} + +export {}; \ No newline at end of file diff --git a/playground/src/app.html b/playground/src/app.html new file mode 100644 index 0000000..9445e7b --- /dev/null +++ b/playground/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/playground/src/lib/assets/favicon.svg b/playground/src/lib/assets/favicon.svg new file mode 100644 index 0000000..cc5dc66 --- /dev/null +++ b/playground/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/playground/src/lib/components/EditorPanel.svelte b/playground/src/lib/components/EditorPanel.svelte new file mode 100644 index 0000000..8d4fb09 --- /dev/null +++ b/playground/src/lib/components/EditorPanel.svelte @@ -0,0 +1,94 @@ + + +
+
+ {#if status === 'success'} + + {:else if status === 'error'} + + {:else if status === 'indeterminate'} + + {/if} +
+

+ {title} + {#if pillText} + + {pillText} + + {/if} +

+ {#if subtitle} +

{subtitle}

+ {/if} +
+ {#if headerActions} +
+ {@render headerActions()} +
+ {/if} + {#if timing !== undefined} +
{timing}ms
+ {/if} +
+ + {#if error} +
+
{error}
+
+ {/if} + + + + {#if children} + {@render children()} + {/if} +
diff --git a/playground/src/lib/components/Header.svelte b/playground/src/lib/components/Header.svelte new file mode 100644 index 0000000..65beb31 --- /dev/null +++ b/playground/src/lib/components/Header.svelte @@ -0,0 +1,21 @@ + + +
+
+
+
+ +
+

Marka

+

Bidirectional Markdown ↔ JSON Parser

+
+
+ + + +
+
+
diff --git a/playground/src/lib/components/Playground.svelte b/playground/src/lib/components/Playground.svelte new file mode 100644 index 0000000..7c10be6 --- /dev/null +++ b/playground/src/lib/components/Playground.svelte @@ -0,0 +1,197 @@ + + +
+
+ + {#snippet headerActions()} + + {/snippet} + + + + {#snippet headerActions()} + + {/snippet} + + + +
+
diff --git a/playground/src/lib/components/StatusBar.svelte b/playground/src/lib/components/StatusBar.svelte new file mode 100644 index 0000000..c73527e --- /dev/null +++ b/playground/src/lib/components/StatusBar.svelte @@ -0,0 +1,25 @@ + + +
+
+
+ {#if status === 'success'} + + {message} + {:else if status === 'error'} + + {message} + {:else} +
+ Ready + {/if} +
+ +
Schema.org validation enabled
+
+
diff --git a/playground/src/lib/icons/CheckCircleIcon.svelte b/playground/src/lib/icons/CheckCircleIcon.svelte new file mode 100644 index 0000000..4c37324 --- /dev/null +++ b/playground/src/lib/icons/CheckCircleIcon.svelte @@ -0,0 +1,18 @@ + + + + + diff --git a/playground/src/lib/icons/Logo.svelte b/playground/src/lib/icons/Logo.svelte new file mode 100644 index 0000000..68c327c --- /dev/null +++ b/playground/src/lib/icons/Logo.svelte @@ -0,0 +1,10 @@ +logo + + diff --git a/playground/src/lib/icons/MinusCircleIcon.svelte b/playground/src/lib/icons/MinusCircleIcon.svelte new file mode 100644 index 0000000..910b505 --- /dev/null +++ b/playground/src/lib/icons/MinusCircleIcon.svelte @@ -0,0 +1,14 @@ + + + + + diff --git a/playground/src/lib/icons/XCircleIcon.svelte b/playground/src/lib/icons/XCircleIcon.svelte new file mode 100644 index 0000000..562f888 --- /dev/null +++ b/playground/src/lib/icons/XCircleIcon.svelte @@ -0,0 +1,18 @@ + + + + + diff --git a/playground/src/lib/index.ts b/playground/src/lib/index.ts new file mode 100644 index 0000000..856f2b6 --- /dev/null +++ b/playground/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/playground/src/lib/wasm.ts b/playground/src/lib/wasm.ts new file mode 100644 index 0000000..fe80d1f --- /dev/null +++ b/playground/src/lib/wasm.ts @@ -0,0 +1,114 @@ +import { readable } from "svelte/store"; + + + +export const wasmReady = readable(false, (set) => { + if (typeof window === "undefined") { + return; + } + + const loadWasm = async () => { + const go = new globalThis.Go(); + try { + const result = await WebAssembly.instantiateStreaming( + fetch("/main.wasm"), + go.importObject, + ); + go.run(result.instance); + set(true); + } catch (error) { + console.error("Error loading wasm module:", error); + } + }; + + if (document.readyState === "complete") { + loadWasm(); + } else { + globalThis.addEventListener("load", loadWasm); + } +}); + +export type ParseResultSuccess = { + data: unknown; + timings: { [key: string]: number }; +}; + +export type ParseResultError = { + error: string; +}; + +export type ParseResult = ParseResultSuccess | ParseResultError; + +export function parseMarkdown(markdown: string): ParseResult { + if (typeof globalThis.marka?.parseFile !== "function") { + throw new Error("Wasm module not ready"); + } + const resultString = globalThis.marka.parseFile(markdown); + return JSON.parse(resultString); +} + +export function compileTemplate(templateSource: string) { + if (typeof globalThis.marka?.compileTemplate !== "function") { + throw new Error("Wasm module not ready"); + } + const resultString = globalThis.marka.compileTemplate(templateSource); + const result = JSON.parse(resultString); + console.log({ result }); + return result; +} + +export function matchBlocks(markdown: string, template: string): ParseResult { + if (typeof globalThis.marka?.matchBlocks !== "function") { + throw new Error("Wasm module not ready"); + } + const resultString = globalThis.marka.matchBlocks(markdown, template); + return JSON.parse(resultString); +} + +export function parseMarkdownWithTemplate( + markdown: string, + template: string, +): ParseResult { + if (typeof globalThis.marka?.parseFileWithTemplate !== "function") { + throw new Error("Wasm module not ready"); + } + const resultString = globalThis.marka.parseFileWithTemplate( + markdown, + template, + ); + return JSON.parse(resultString); +} + +export function listTemplates(): string[] { + if (typeof globalThis.marka?.listTemplates !== "function") { + throw new Error("Wasm module not ready"); + } + const resultString = globalThis.marka.listTemplates(); + return JSON.parse(resultString); +} + +export function getTemplate(name: string): string { + if (typeof globalThis.marka?.getTemplate !== "function") { + throw new Error("Wasm module not ready"); + } + return globalThis.marka.getTemplate(name); +} + +export function detectType(markdown: string): string | ParseResultError { + if (typeof globalThis.marka?.detectType !== "function") { + throw new Error("Wasm module not ready"); + } + const result = globalThis.marka.detectType(markdown); + try { + // If the result is a JSON string with an error, parse and return it + const parsed = JSON.parse(result); + if (parsed.error) { + return parsed; + } + } catch (e) { + // Otherwise, it's a plain string for success + return result; + } + return result; +} + diff --git a/playground/src/routes/+layout.svelte b/playground/src/routes/+layout.svelte new file mode 100644 index 0000000..90a1632 --- /dev/null +++ b/playground/src/routes/+layout.svelte @@ -0,0 +1,14 @@ + + + + + Marka Playground + + + +{@render children?.()} diff --git a/playground/src/routes/+layout.ts b/playground/src/routes/+layout.ts new file mode 100644 index 0000000..189f71e --- /dev/null +++ b/playground/src/routes/+layout.ts @@ -0,0 +1 @@ +export const prerender = true; diff --git a/playground/src/routes/+page.svelte b/playground/src/routes/+page.svelte new file mode 100644 index 0000000..1e54ebe --- /dev/null +++ b/playground/src/routes/+page.svelte @@ -0,0 +1,33 @@ + + +
+
+
+ +
+
+ + diff --git a/playground/static/logo-2.svg b/playground/static/logo-2.svg new file mode 100644 index 0000000..0d59cff --- /dev/null +++ b/playground/static/logo-2.svg @@ -0,0 +1,3 @@ + + + diff --git a/playground/static/logo.svg b/playground/static/logo.svg new file mode 100644 index 0000000..e4f85db --- /dev/null +++ b/playground/static/logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/playground/static/main.wasm b/playground/static/main.wasm new file mode 100644 index 0000000..a68d48f Binary files /dev/null and b/playground/static/main.wasm differ diff --git a/playground/static/robots.txt b/playground/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/playground/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/playground/static/wasm_exec.js b/playground/static/wasm_exec.js new file mode 100644 index 0000000..53ea75f --- /dev/null +++ b/playground/static/wasm_exec.js @@ -0,0 +1,553 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// This file has been modified for use by the TinyGo compiler. + +(() => { + // Map multiple JavaScript environments to a single common API, + // preferring web standards over Node.js API. + // + // Environments considered: + // - Browsers + // - Node.js + // - Electron + // - Parcel + + if (typeof global !== "undefined") { + // global already exists + } else if (typeof window !== "undefined") { + window.global = window; + } else if (typeof self !== "undefined") { + self.global = self; + } else { + throw new Error("cannot export Go (neither global, window nor self is defined)"); + } + + if (!global.require && typeof require !== "undefined") { + global.require = require; + } + + if (!global.fs && global.require) { + global.fs = require("node:fs"); + } + + const enosys = () => { + const err = new Error("not implemented"); + err.code = "ENOSYS"; + return err; + }; + + if (!global.fs) { + let outputBuf = ""; + global.fs = { + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused + writeSync(fd, buf) { + outputBuf += decoder.decode(buf); + const nl = outputBuf.lastIndexOf("\n"); + if (nl != -1) { + console.log(outputBuf.substr(0, nl)); + outputBuf = outputBuf.substr(nl + 1); + } + return buf.length; + }, + write(fd, buf, offset, length, position, callback) { + if (offset !== 0 || length !== buf.length || position !== null) { + callback(enosys()); + return; + } + const n = this.writeSync(fd, buf); + callback(null, n); + }, + chmod(path, mode, callback) { callback(enosys()); }, + chown(path, uid, gid, callback) { callback(enosys()); }, + close(fd, callback) { callback(enosys()); }, + fchmod(fd, mode, callback) { callback(enosys()); }, + fchown(fd, uid, gid, callback) { callback(enosys()); }, + fstat(fd, callback) { callback(enosys()); }, + fsync(fd, callback) { callback(null); }, + ftruncate(fd, length, callback) { callback(enosys()); }, + lchown(path, uid, gid, callback) { callback(enosys()); }, + link(path, link, callback) { callback(enosys()); }, + lstat(path, callback) { callback(enosys()); }, + mkdir(path, perm, callback) { callback(enosys()); }, + open(path, flags, mode, callback) { callback(enosys()); }, + read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, + readdir(path, callback) { callback(enosys()); }, + readlink(path, callback) { callback(enosys()); }, + rename(from, to, callback) { callback(enosys()); }, + rmdir(path, callback) { callback(enosys()); }, + stat(path, callback) { callback(enosys()); }, + symlink(path, link, callback) { callback(enosys()); }, + truncate(path, length, callback) { callback(enosys()); }, + unlink(path, callback) { callback(enosys()); }, + utimes(path, atime, mtime, callback) { callback(enosys()); }, + }; + } + + if (!global.process) { + global.process = { + getuid() { return -1; }, + getgid() { return -1; }, + geteuid() { return -1; }, + getegid() { return -1; }, + getgroups() { throw enosys(); }, + pid: -1, + ppid: -1, + umask() { throw enosys(); }, + cwd() { throw enosys(); }, + chdir() { throw enosys(); }, + } + } + + if (!global.crypto) { + const nodeCrypto = require("node:crypto"); + global.crypto = { + getRandomValues(b) { + nodeCrypto.randomFillSync(b); + }, + }; + } + + if (!global.performance) { + global.performance = { + now() { + const [sec, nsec] = process.hrtime(); + return sec * 1000 + nsec / 1000000; + }, + }; + } + + if (!global.TextEncoder) { + global.TextEncoder = require("node:util").TextEncoder; + } + + if (!global.TextDecoder) { + global.TextDecoder = require("node:util").TextDecoder; + } + + // End of polyfills for common API. + + const encoder = new TextEncoder("utf-8"); + const decoder = new TextDecoder("utf-8"); + let reinterpretBuf = new DataView(new ArrayBuffer(8)); + var logLine = []; + const wasmExit = {}; // thrown to exit via proc_exit (not an error) + + global.Go = class { + constructor() { + this._callbackTimeouts = new Map(); + this._nextCallbackTimeoutID = 1; + + const mem = () => { + // The buffer may change when requesting more memory. + return new DataView(this._inst.exports.memory.buffer); + } + + const unboxValue = (v_ref) => { + reinterpretBuf.setBigInt64(0, v_ref, true); + const f = reinterpretBuf.getFloat64(0, true); + if (f === 0) { + return undefined; + } + if (!isNaN(f)) { + return f; + } + + const id = v_ref & 0xffffffffn; + return this._values[id]; + } + + + const loadValue = (addr) => { + let v_ref = mem().getBigUint64(addr, true); + return unboxValue(v_ref); + } + + const boxValue = (v) => { + const nanHead = 0x7FF80000n; + + if (typeof v === "number") { + if (isNaN(v)) { + return nanHead << 32n; + } + if (v === 0) { + return (nanHead << 32n) | 1n; + } + reinterpretBuf.setFloat64(0, v, true); + return reinterpretBuf.getBigInt64(0, true); + } + + switch (v) { + case undefined: + return 0n; + case null: + return (nanHead << 32n) | 2n; + case true: + return (nanHead << 32n) | 3n; + case false: + return (nanHead << 32n) | 4n; + } + + let id = this._ids.get(v); + if (id === undefined) { + id = this._idPool.pop(); + if (id === undefined) { + id = BigInt(this._values.length); + } + this._values[id] = v; + this._goRefCounts[id] = 0; + this._ids.set(v, id); + } + this._goRefCounts[id]++; + let typeFlag = 1n; + switch (typeof v) { + case "string": + typeFlag = 2n; + break; + case "symbol": + typeFlag = 3n; + break; + case "function": + typeFlag = 4n; + break; + } + return id | ((nanHead | typeFlag) << 32n); + } + + const storeValue = (addr, v) => { + let v_ref = boxValue(v); + mem().setBigUint64(addr, v_ref, true); + } + + const loadSlice = (array, len, cap) => { + return new Uint8Array(this._inst.exports.memory.buffer, array, len); + } + + const loadSliceOfValues = (array, len, cap) => { + const a = new Array(len); + for (let i = 0; i < len; i++) { + a[i] = loadValue(array + i * 8); + } + return a; + } + + const loadString = (ptr, len) => { + return decoder.decode(new DataView(this._inst.exports.memory.buffer, ptr, len)); + } + + const timeOrigin = Date.now() - performance.now(); + this.importObject = { + wasi_snapshot_preview1: { + // https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_write + fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) { + let nwritten = 0; + if (fd == 1) { + for (let iovs_i=0; iovs_i 0, // dummy + fd_fdstat_get: () => 0, // dummy + fd_seek: () => 0, // dummy + proc_exit: (code) => { + this.exited = true; + this.exitCode = code; + this._resolveExitPromise(); + throw wasmExit; + }, + random_get: (bufPtr, bufLen) => { + crypto.getRandomValues(loadSlice(bufPtr, bufLen)); + return 0; + }, + }, + gojs: { + // func ticks() float64 + "runtime.ticks": () => { + return timeOrigin + performance.now(); + }, + + // func sleepTicks(timeout float64) + "runtime.sleepTicks": (timeout) => { + // Do not sleep, only reactivate scheduler after the given timeout. + setTimeout(() => { + if (this.exited) return; + try { + this._inst.exports.go_scheduler(); + } catch (e) { + if (e !== wasmExit) throw e; + } + }, timeout); + }, + + // func finalizeRef(v ref) + "syscall/js.finalizeRef": (v_ref) => { + // Note: TinyGo does not support finalizers so this is only called + // for one specific case, by js.go:jsString. and can/might leak memory. + const id = v_ref & 0xffffffffn; + if (this._goRefCounts?.[id] !== undefined) { + this._goRefCounts[id]--; + if (this._goRefCounts[id] === 0) { + const v = this._values[id]; + this._values[id] = null; + this._ids.delete(v); + this._idPool.push(id); + } + } else { + console.error("syscall/js.finalizeRef: unknown id", id); + } + }, + + // func stringVal(value string) ref + "syscall/js.stringVal": (value_ptr, value_len) => { + value_ptr >>>= 0; + const s = loadString(value_ptr, value_len); + return boxValue(s); + }, + + // func valueGet(v ref, p string) ref + "syscall/js.valueGet": (v_ref, p_ptr, p_len) => { + let prop = loadString(p_ptr, p_len); + let v = unboxValue(v_ref); + let result = Reflect.get(v, prop); + return boxValue(result); + }, + + // func valueSet(v ref, p string, x ref) + "syscall/js.valueSet": (v_ref, p_ptr, p_len, x_ref) => { + const v = unboxValue(v_ref); + const p = loadString(p_ptr, p_len); + const x = unboxValue(x_ref); + Reflect.set(v, p, x); + }, + + // func valueDelete(v ref, p string) + "syscall/js.valueDelete": (v_ref, p_ptr, p_len) => { + const v = unboxValue(v_ref); + const p = loadString(p_ptr, p_len); + Reflect.deleteProperty(v, p); + }, + + // func valueIndex(v ref, i int) ref + "syscall/js.valueIndex": (v_ref, i) => { + return boxValue(Reflect.get(unboxValue(v_ref), i)); + }, + + // valueSetIndex(v ref, i int, x ref) + "syscall/js.valueSetIndex": (v_ref, i, x_ref) => { + Reflect.set(unboxValue(v_ref), i, unboxValue(x_ref)); + }, + + // func valueCall(v ref, m string, args []ref) (ref, bool) + "syscall/js.valueCall": (ret_addr, v_ref, m_ptr, m_len, args_ptr, args_len, args_cap) => { + const v = unboxValue(v_ref); + const name = loadString(m_ptr, m_len); + const args = loadSliceOfValues(args_ptr, args_len, args_cap); + try { + const m = Reflect.get(v, name); + storeValue(ret_addr, Reflect.apply(m, v, args)); + mem().setUint8(ret_addr + 8, 1); + } catch (err) { + storeValue(ret_addr, err); + mem().setUint8(ret_addr + 8, 0); + } + }, + + // func valueInvoke(v ref, args []ref) (ref, bool) + "syscall/js.valueInvoke": (ret_addr, v_ref, args_ptr, args_len, args_cap) => { + try { + const v = unboxValue(v_ref); + const args = loadSliceOfValues(args_ptr, args_len, args_cap); + storeValue(ret_addr, Reflect.apply(v, undefined, args)); + mem().setUint8(ret_addr + 8, 1); + } catch (err) { + storeValue(ret_addr, err); + mem().setUint8(ret_addr + 8, 0); + } + }, + + // func valueNew(v ref, args []ref) (ref, bool) + "syscall/js.valueNew": (ret_addr, v_ref, args_ptr, args_len, args_cap) => { + const v = unboxValue(v_ref); + const args = loadSliceOfValues(args_ptr, args_len, args_cap); + try { + storeValue(ret_addr, Reflect.construct(v, args)); + mem().setUint8(ret_addr + 8, 1); + } catch (err) { + storeValue(ret_addr, err); + mem().setUint8(ret_addr+ 8, 0); + } + }, + + // func valueLength(v ref) int + "syscall/js.valueLength": (v_ref) => { + return unboxValue(v_ref).length; + }, + + // valuePrepareString(v ref) (ref, int) + "syscall/js.valuePrepareString": (ret_addr, v_ref) => { + const s = String(unboxValue(v_ref)); + const str = encoder.encode(s); + storeValue(ret_addr, str); + mem().setInt32(ret_addr + 8, str.length, true); + }, + + // valueLoadString(v ref, b []byte) + "syscall/js.valueLoadString": (v_ref, slice_ptr, slice_len, slice_cap) => { + const str = unboxValue(v_ref); + loadSlice(slice_ptr, slice_len, slice_cap).set(str); + }, + + // func valueInstanceOf(v ref, t ref) bool + "syscall/js.valueInstanceOf": (v_ref, t_ref) => { + return unboxValue(v_ref) instanceof unboxValue(t_ref); + }, + + // func copyBytesToGo(dst []byte, src ref) (int, bool) + "syscall/js.copyBytesToGo": (ret_addr, dest_addr, dest_len, dest_cap, src_ref) => { + let num_bytes_copied_addr = ret_addr; + let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable + + const dst = loadSlice(dest_addr, dest_len); + const src = unboxValue(src_ref); + if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { + mem().setUint8(returned_status_addr, 0); // Return "not ok" status + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + mem().setUint32(num_bytes_copied_addr, toCopy.length, true); + mem().setUint8(returned_status_addr, 1); // Return "ok" status + }, + + // copyBytesToJS(dst ref, src []byte) (int, bool) + // Originally copied from upstream Go project, then modified: + // https://github.com/golang/go/blob/3f995c3f3b43033013013e6c7ccc93a9b1411ca9/misc/wasm/wasm_exec.js#L404-L416 + "syscall/js.copyBytesToJS": (ret_addr, dst_ref, src_addr, src_len, src_cap) => { + let num_bytes_copied_addr = ret_addr; + let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable + + const dst = unboxValue(dst_ref); + const src = loadSlice(src_addr, src_len); + if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { + mem().setUint8(returned_status_addr, 0); // Return "not ok" status + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + mem().setUint32(num_bytes_copied_addr, toCopy.length, true); + mem().setUint8(returned_status_addr, 1); // Return "ok" status + }, + } + }; + + // Go 1.20 uses 'env'. Go 1.21 uses 'gojs'. + // For compatibility, we use both as long as Go 1.20 is supported. + this.importObject.env = this.importObject.gojs; + } + + async run(instance) { + this._inst = instance; + this._values = [ // JS values that Go currently has references to, indexed by reference id + NaN, + 0, + null, + true, + false, + global, + this, + ]; + this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id + this._ids = new Map(); // mapping from JS values to reference ids + this._idPool = []; // unused ids that have been garbage collected + this.exited = false; // whether the Go program has exited + this.exitCode = 0; + + if (this._inst.exports._start) { + let exitPromise = new Promise((resolve, reject) => { + this._resolveExitPromise = resolve; + }); + + // Run program, but catch the wasmExit exception that's thrown + // to return back here. + try { + this._inst.exports._start(); + } catch (e) { + if (e !== wasmExit) throw e; + } + + await exitPromise; + return this.exitCode; + } else { + this._inst.exports._initialize(); + } + } + + _resume() { + if (this.exited) { + throw new Error("Go program has already exited"); + } + try { + this._inst.exports.resume(); + } catch (e) { + if (e !== wasmExit) throw e; + } + if (this.exited) { + this._resolveExitPromise(); + } + } + + _makeFuncWrapper(id) { + const go = this; + return function () { + const event = { id: id, this: this, args: arguments }; + go._pendingEvent = event; + go._resume(); + return event.result; + }; + } + } + + if ( + global.require && + global.require.main === module && + global.process && + global.process.versions && + !global.process.versions.electron + ) { + if (process.argv.length != 3) { + console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); + process.exit(1); + } + + const go = new Go(); + WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then(async (result) => { + let exitCode = await go.run(result.instance); + process.exit(exitCode); + }).catch((err) => { + console.error(err); + process.exit(1); + }); + } +})(); diff --git a/playground/svelte.config.js b/playground/svelte.config.js new file mode 100644 index 0000000..aad7014 --- /dev/null +++ b/playground/svelte.config.js @@ -0,0 +1,18 @@ +import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; +import adapter from "@sveltejs/adapter-static"; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://svelte.dev/docs/kit/integrations + // for more information about preprocessors + preprocess: vitePreprocess(), + + kit: { + // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. + // If your environment is not supported, or you settled on a specific environment, switch out the adapter. + // See https://svelte.dev/docs/kit/adapters for more information about adapters. + adapter: adapter(), + }, +}; + +export default config; diff --git a/playground/tsconfig.json b/playground/tsconfig.json new file mode 100644 index 0000000..a5567ee --- /dev/null +++ b/playground/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript +} diff --git a/playground/vite.config.ts b/playground/vite.config.ts new file mode 100644 index 0000000..76b2098 --- /dev/null +++ b/playground/vite.config.ts @@ -0,0 +1,14 @@ +import tailwindcss from "@tailwindcss/vite"; +import { sveltekit } from "@sveltejs/kit/vite"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [tailwindcss(), sveltekit()], + optimizeDeps: { + exclude: [ + "svelte-codemirror-editor", + "codemirror", + "@codemirror/language", + ], + }, +}); diff --git a/playground/wasm/build.sh b/playground/wasm/build.sh new file mode 100755 index 0000000..b30f76b --- /dev/null +++ b/playground/wasm/build.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +OUT_DIR="$SCRIPT_DIR/../static" +OUT_WASM="$OUT_DIR/main.wasm" + +mkdir -p "$OUT_DIR" + +tinygo build -target=wasm \ + -no-debug -panic=print -gc=leaking \ + -o "$OUT_WASM" "$SCRIPT_DIR" + +command -v wasm-opt >/dev/null && wasm-opt -Oz --strip-debug --strip-dwarf --strip-producers \ + -o "$OUT_WASM.tmp" "$OUT_WASM" && mv "$OUT_WASM.tmp" "$OUT_WASM" +# command -v wasm-strip >/dev/null && wasm-strip "$OUT_WASM" + +cp -f "$(tinygo env TINYGOROOT)/targets/wasm_exec.js" "$OUT_DIR/wasm_exec.js" diff --git a/playground/wasm/go.mod b/playground/wasm/go.mod new file mode 100644 index 0000000..d405ec4 --- /dev/null +++ b/playground/wasm/go.mod @@ -0,0 +1,3 @@ +module git.max-richter.dev/max/marka/playground-wasm + +go 1.24.7 diff --git a/playground/wasm/main.go b/playground/wasm/main.go new file mode 100644 index 0000000..1ac1178 --- /dev/null +++ b/playground/wasm/main.go @@ -0,0 +1,115 @@ +//go:build js && wasm + +package main + +import ( + "encoding/json" + "syscall/js" + + p "git.max-richter.dev/max/marka/parser" + "git.max-richter.dev/max/marka/registry" + "git.max-richter.dev/max/marka/template" +) + +func wrapError(err error) string { + errMap := map[string]any{"error": err.Error()} + errJSON, _ := json.Marshal(errMap) + return string(errJSON) +} + +func MatchBlocks(this js.Value, args []js.Value) any { + s := args[0].String() + t := args[1].String() + matched, err := p.MatchBlocks(s, t) + if err != nil { + return wrapError(err) + } + jsonString, _ := json.Marshal(matched) + return string(jsonString) +} + +func DetectType(this js.Value, args []js.Value) any { + markdown := args[0].String() + t, err := p.DetectType(markdown) + if err != nil { + return wrapError(err) + } + return t +} + +func ParseFile(this js.Value, args []js.Value) any { + markdown := args[0].String() + res, err := p.ParseFile(markdown) + if err != nil { + return wrapError(err) + } + b, err := json.Marshal(res) + if err != nil { + return wrapError(err) + } + return string(b) +} + +func ParseFileWithTemplate(this js.Value, args []js.Value) any { + markdown := args[0].String() + template := args[1].String() + res, err := p.ParseFileWithTemplate(markdown, template) + if err != nil { + return wrapError(err) + } + b, err := json.Marshal(res) + if err != nil { + return wrapError(err) + } + return string(b) +} + +func ListTemplates(this js.Value, args []js.Value) any { + templates, err := registry.ListTemplates() + if err != nil { + return wrapError(err) + } + b, err := json.Marshal(templates) + if err != nil { + return wrapError(err) + } + return string(b) +} + +func GetTemplate(this js.Value, args []js.Value) any { + name := args[0].String() + template, err := registry.GetTemplate(name) + if err != nil { + return wrapError(err) + } + return template +} + +func CompileTemplate(this js.Value, args []js.Value) any { + source := args[0].String() + template, err := template.CompileTemplate(source) + if err != nil { + return wrapError(err) + } + b, err := json.Marshal(template) + if err != nil { + return wrapError(err) + } + return string(b) +} + +func main() { + marka := js.Global().Get("Object").New() + + marka.Set("matchBlocks", js.FuncOf(MatchBlocks)) + marka.Set("detectType", js.FuncOf(DetectType)) + marka.Set("parseFile", js.FuncOf(ParseFile)) + marka.Set("parseFileWithTemplate", js.FuncOf(ParseFileWithTemplate)) + marka.Set("listTemplates", js.FuncOf(ListTemplates)) + marka.Set("getTemplate", js.FuncOf(GetTemplate)) + marka.Set("compileTemplate", js.FuncOf(CompileTemplate)) + + js.Global().Set("marka", marka) + + select {} +} \ No newline at end of file diff --git a/registry/go.mod b/registry/go.mod index 61e060e..c2bc5c8 100644 --- a/registry/go.mod +++ b/registry/go.mod @@ -1,3 +1,3 @@ module git.max-richter.dev/max/marka/registry -go 1.24.3 +go 1.24.7 diff --git a/registry/go.sum b/registry/go.sum index b46b517..e69de29 100644 --- a/registry/go.sum +++ b/registry/go.sum @@ -1,4 +0,0 @@ -github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/registry/schema-org/Recipe.schema.json b/registry/schema-org/Recipe.schema.json index 73a967d..0c1668a 100644 --- a/registry/schema-org/Recipe.schema.json +++ b/registry/schema-org/Recipe.schema.json @@ -23,8 +23,15 @@ "ingredients": { "description": "A single ingredient used in the recipe, e.g. sugar, flour or garlic.", "oneOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } ] }, "nutrition": { @@ -42,27 +49,54 @@ "recipeIngredient": { "description": "A single ingredient used in the recipe, e.g. sugar, flour or garlic.", "oneOf": [ - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } ] }, "recipeInstructions": { "description": "A step in making the recipe, in the form of a single item (document, video, etc.) or an ordered list with HowToStep and/or HowToSection items.", "anyOf": [ - { "type": "string" }, - { "$ref": "schema:CreativeWork" }, - { "$ref": "schema:ItemList" } + { + "type": "string" + }, + { + "$ref": "schema:CreativeWork" + }, + { + "$ref": "schema:ItemList" + } ] }, "recipeYield": { "description": "The quantity produced by the recipe (for example, number of people served, number of servings, etc).", - "anyOf": [{ "type": "string" }, { "$ref": "schema:QuantitativeValue" }] + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "schema:QuantitativeValue" + } + ] }, "suitableForDiet": { "description": "Indicates a dietary restriction or guideline for which this recipe or menu item is suitable, e.g. diabetic, halal etc.", "oneOf": [ - { "$ref": "schema:RestrictedDiet" }, - { "type": "array", "items": { "$ref": "schema:RestrictedDiet" } } + { + "$ref": "schema:RestrictedDiet" + }, + { + "type": "array", + "items": { + "$ref": "schema:RestrictedDiet" + } + } ] } } diff --git a/registry/templates.go b/registry/templates.go index 315e30a..d7466ae 100644 --- a/registry/templates.go +++ b/registry/templates.go @@ -4,6 +4,9 @@ package registry import ( "embed" "io" + "io/fs" + "path/filepath" + "strings" ) //go:embed templates/*.marka @@ -23,3 +26,20 @@ func GetTemplate(name string) (string, error) { return string(templateBytes), nil } + +func ListTemplates() ([]string, error) { + var templateNames []string + err := fs.WalkDir(templates, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if !d.IsDir() && strings.HasSuffix(path, ".marka") { + templateNames = append(templateNames, strings.TrimSuffix(filepath.Base(path), ".marka")) + } + return nil + }) + if err != nil { + return nil, err + } + return templateNames, nil +} diff --git a/registry/templates/Article.marka b/registry/templates/Article.marka index f6175f3..f5cb9b0 100644 --- a/registry/templates/Article.marka +++ b/registry/templates/Article.marka @@ -3,29 +3,32 @@ path: . codec: yaml fields: - - path: "@context" - codec: const - value: https://schema.org - hidden: true - - path: "@schema" + - path: "_schema" codec: const value: Article hidden: true - - path: "@type" + - path: "_type" codec: const value: Article - path: image - path: author.name - - path: author.@type + - path: author._type codec: const value: Person hidden: true - path: datePublished - path: articleSection + - path: reviewRating.ratingValue + pathAlias: rating + - path: reviewRating.bestRating + codec: const + hidden: true + - path: reviewRating.worstRating + codec: const + hidden: true } --- # { headline } -{ keywords | hashtags } { articleBody } diff --git a/registry/templates/Recipe.marka b/registry/templates/Recipe.marka index 94e83ce..0bba9fc 100644 --- a/registry/templates/Recipe.marka +++ b/registry/templates/Recipe.marka @@ -3,19 +3,15 @@ path: . codec: yaml fields: - - path: "@context" - codec: const - value: https://schema.org - hidden: true - - path: "@schema" + - path: "_schema" codec: const value: Recipe hidden: true - - path: "@type" + - path: "_type" codec: const value: Recipe - path: image - - path: author.@type + - path: author._type codec: const hidden: true value: Person @@ -25,11 +21,14 @@ - path: prepTime - path: cookTime - path: recipeYield + - path: cookingMethod + - path: nutrition + - path: recipeCategory + - path: suitableForDiet } --- # { name | text } -{ keywords | hashtags } { description | text } diff --git a/registry/templates/Review.marka b/registry/templates/Review.marka index cf49de2..6c57232 100644 --- a/registry/templates/Review.marka +++ b/registry/templates/Review.marka @@ -3,21 +3,17 @@ path: . codec: yaml fields: - - path: "@context" - codec: const - value: https://schema.org - hidden: true - - path: "@schema" + - path: "_schema" codec: const value: Review hidden: true - - path: "@type" + - path: "_type" codec: const value: Review - path: tmdbId - path: image - path: author.name - - path: author.@type + - path: author._type codec: const value: Person hidden: true @@ -39,4 +35,5 @@ # { itemReviewed.name } { keywords | hashtags } +## Review { reviewBody } diff --git a/registry/templates/_default.marka b/registry/templates/_default.marka index 92d86bb..a4fd42e 100644 --- a/registry/templates/_default.marka +++ b/registry/templates/_default.marka @@ -3,8 +3,6 @@ path: . codec: yaml fields: - - path: "@type" - codec: const - value: Recipe + - path: "_type" } --- diff --git a/renderer/encoders/encoders.go b/renderer/encoders/encoders.go index 6086d0e..aa3b842 100644 --- a/renderer/encoders/encoders.go +++ b/renderer/encoders/encoders.go @@ -13,8 +13,7 @@ import ( const emptyBlock = "\uE000" func fixRenderedBlock(input string) string { - input = strings.ReplaceAll(input, "'@type':", "@type:") - input = strings.ReplaceAll(input, "'@context':", "@context:") + input = strings.ReplaceAll(input, "'_type':", "_type:") if len(input) == 0 { return emptyBlock } diff --git a/renderer/go.mod b/renderer/go.mod index 2594145..a08c91d 100644 --- a/renderer/go.mod +++ b/renderer/go.mod @@ -1,3 +1,18 @@ module git.max-richter.dev/max/marka/renderer -go 1.24.3 +go 1.24.7 + +require ( + git.max-richter.dev/max/marka/parser v0.0.0-20250819170608-69c2550f448e + git.max-richter.dev/max/marka/registry v0.0.0-20250819170608-69c2550f448e + git.max-richter.dev/max/marka/template v0.0.0-20250819170608-69c2550f448e + git.max-richter.dev/max/marka/testdata v0.0.0-20250819195334-b3c01bb43d9a + git.max-richter.dev/max/marka/validator v0.0.0-20250819170608-69c2550f448e + github.com/google/go-cmp v0.7.0 + go.yaml.in/yaml/v4 v4.0.0-rc.1 +) + +require ( + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect + golang.org/x/text v0.14.0 // indirect +) diff --git a/renderer/go.sum b/renderer/go.sum new file mode 100644 index 0000000..11e46b8 --- /dev/null +++ b/renderer/go.sum @@ -0,0 +1,20 @@ +git.max-richter.dev/max/marka/parser v0.0.0-20250819170608-69c2550f448e h1:enZufetD3UoIVTnTNTQSFlr1Ir0jG7wObUAxb6+xwWg= +git.max-richter.dev/max/marka/parser v0.0.0-20250819170608-69c2550f448e/go.mod h1:xQK6tsgr9BOoeFw8JxjBwDkVENlOqapmcRkYyf/L+SQ= +git.max-richter.dev/max/marka/registry v0.0.0-20250819170608-69c2550f448e h1:eXAE0JHDvLGqtYSSlX5mw1XAuK+Cmu74c52PyveRhlE= +git.max-richter.dev/max/marka/registry v0.0.0-20250819170608-69c2550f448e/go.mod h1:n793S7TENIfgHpZLz0lm0qorM7eCx3zBLby3Fb++hZA= +git.max-richter.dev/max/marka/template v0.0.0-20250819170608-69c2550f448e h1:6327yeKE0dYbwsEBhIFcXOJEWTxBUWGBeB0uj9BTJqA= +git.max-richter.dev/max/marka/template v0.0.0-20250819170608-69c2550f448e/go.mod h1:Uxi5xcxtnjopsIZjjMlFaWJGuglB9JNL++FuaSbOf6U= +git.max-richter.dev/max/marka/testdata v0.0.0-20250819195334-b3c01bb43d9a h1:LAU2LlLZ96s8hcg1OEGD5HBshDspWVwWTa7YG5+A70w= +git.max-richter.dev/max/marka/testdata v0.0.0-20250819195334-b3c01bb43d9a/go.mod h1:88SkY5pTONkgfBy1FT10LoqRC8rt36iF1fk/rupjuJY= +git.max-richter.dev/max/marka/validator v0.0.0-20250819170608-69c2550f448e h1:MBxIk3jvbjSmpZk7xlR6Yog61375PMya3FzOxZ5TuYs= +git.max-richter.dev/max/marka/validator v0.0.0-20250819170608-69c2550f448e/go.mod h1:qdGfCFRzsGedmnd77vb7pu/EMx0W0DcQBMEfvNxMYsw= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +go.yaml.in/yaml/v4 v4.0.0-rc.1 h1:4J1+yLKUIPGexM/Si+9d3pij4hdc7aGO04NhrElqXbY= +go.yaml.in/yaml/v4 v4.0.0-rc.1/go.mod h1:CBdeces52/nUXndfQ5OY8GEQuNR9uEEOJPZj/Xq5IzU= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/renderer/renderer.go b/renderer/renderer.go index 3ce8d89..de7556c 100644 --- a/renderer/renderer.go +++ b/renderer/renderer.go @@ -18,10 +18,10 @@ func RenderFile(rawJSON []byte) ([]byte, error) { return nil, fmt.Errorf("failed to parse JSON: %w", err) } - // 2) extract type from "@type" Property - contentType, ok := data["@type"].(string) + // 2) extract type from "_type" Property + contentType, ok := data["_type"].(string) if !ok || contentType == "" { - return nil, fmt.Errorf("JSON does not contain a valid '@type' property") + return nil, fmt.Errorf("JSON does not contain a valid '_type' property") } // 3) get the template from the registry diff --git a/renderer/renderer_test.go b/renderer/renderer_test.go index 363d286..cd1d271 100644 --- a/renderer/renderer_test.go +++ b/renderer/renderer_test.go @@ -41,10 +41,10 @@ func TestRenderFile_MissingType(t *testing.T) { rawJSON := []byte(`{"name": "Test"}`) _, err := renderer.RenderFile(rawJSON) if err == nil { - t.Fatal("expected error for missing @type, got nil") + t.Fatal("expected error for missing _type, got nil") } - if !strings.Contains(err.Error(), "JSON does not contain a valid '@type' property") { - t.Errorf("expected missing @type error, got: %v", err) + if !strings.Contains(err.Error(), "JSON does not contain a valid '_type' property") { + t.Errorf("expected missing _type error, got: %v", err) } } diff --git a/server/.air.toml b/server/.air.toml new file mode 100644 index 0000000..b2eced4 --- /dev/null +++ b/server/.air.toml @@ -0,0 +1,29 @@ +root = "." +tmp_dir = "tmp" + +[build] +# Command to build the application. +cmd = "go build -o ./tmp/marka-server ./cmd/marka-server" + +# The binary to run. +bin = "./tmp/marka-server" + +# Command to run the application with arguments. +full_bin = "./tmp/marka-server -root=../examples -addr=:8080" + +# Watch these file extensions. +include_ext = ["go", "http"] + +# Ignore these directories. +exclude_dir = ["tmp"] + +# Log file for build errors. +log = "air_errors.log" + +[log] +# Show time in logs. +time = true + +[misc] +# Delete tmp directory on exit. +clean_on_exit = true diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..3fec32c --- /dev/null +++ b/server/.gitignore @@ -0,0 +1 @@ +tmp/ diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000..fd96eee --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,16 @@ +FROM golang:1.24.7 AS build +WORKDIR /src +COPY . . +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -trimpath -ldflags="-s -w" \ + -o /out/server ./server/cmd/marka-server + +FROM scratch +WORKDIR /app +COPY --from=build /out/server /app/server +USER 65532:65532 +EXPOSE 8080 +CMD ["/server","-root=/app/data","-addr=:8080"] + diff --git a/server/cmd/marka-server/main.go b/server/cmd/marka-server/main.go new file mode 100644 index 0000000..b001bfd --- /dev/null +++ b/server/cmd/marka-server/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "flag" + "log" + "net/http" + "os" + "path/filepath" + + "git.max-richter.dev/max/marka/server-new/internal/adapters" + "git.max-richter.dev/max/marka/server-new/internal/handler" +) + +func main() { + root := flag.String("root", ".", "filesystem root to serve") + addr := flag.String("addr", ":8080", "listen address") + flag.Parse() + + absRoot, err := filepath.Abs(*root) + must(err) + + info, err := os.Stat(absRoot) + must(err) + if !info.IsDir() { + log.Fatal("root is not a directory") + } + + fsAdapter, err := adapters.NewLocalFsAdapter(absRoot) + must(err) + + http.Handle("/", handler.NewHandler(fsAdapter)) + + log.Printf("listening on %s, root=%s", *addr, absRoot) + log.Fatal(http.ListenAndServe(*addr, nil)) +} + +func must(err error) { + if err != nil { + log.Fatal(err) + } +} diff --git a/server/go.mod b/server/go.mod new file mode 100644 index 0000000..3653730 --- /dev/null +++ b/server/go.mod @@ -0,0 +1,3 @@ +module git.max-richter.dev/max/marka/server-new + +go 1.24.7 diff --git a/server/go.sum b/server/go.sum new file mode 100644 index 0000000..8678458 --- /dev/null +++ b/server/go.sum @@ -0,0 +1,22 @@ +git.max-richter.dev/max/marka/parser v0.0.0-20250819170608-69c2550f448e h1:enZufetD3UoIVTnTNTQSFlr1Ir0jG7wObUAxb6+xwWg= +git.max-richter.dev/max/marka/parser v0.0.0-20250819170608-69c2550f448e/go.mod h1:xQK6tsgr9BOoeFw8JxjBwDkVENlOqapmcRkYyf/L+SQ= +git.max-richter.dev/max/marka/registry v0.0.0-20250817132016-6db87db32567 h1:oe7Xb8dE43S8mRla5hfEqagMnvhvEVHsvRlzl2v540w= +git.max-richter.dev/max/marka/registry v0.0.0-20250817132016-6db87db32567/go.mod h1:qGWl42P8mgEktfor/IjQp0aS9SqmpeIlhSuVTlUOXLQ= +git.max-richter.dev/max/marka/renderer v0.0.0-20250819170608-69c2550f448e h1:9Eg81l8YMTXWZC3xlZ5L/NJRuK26bksrVtEHyCTV4sM= +git.max-richter.dev/max/marka/renderer v0.0.0-20250819170608-69c2550f448e/go.mod h1:mjJqEqALg4YJoiebk3V21yJVUVEs3K2RiLO/IW6DGCM= +git.max-richter.dev/max/marka/template v0.0.0-20250817132016-6db87db32567 h1:XIx89KqTgd/h14oe5mLvT9E8+jGEAjWgudqiMtQdcec= +git.max-richter.dev/max/marka/template v0.0.0-20250817132016-6db87db32567/go.mod h1:Uxi5xcxtnjopsIZjjMlFaWJGuglB9JNL++FuaSbOf6U= +git.max-richter.dev/max/marka/testdata v0.0.0-20250819195334-b3c01bb43d9a h1:LAU2LlLZ96s8hcg1OEGD5HBshDspWVwWTa7YG5+A70w= +git.max-richter.dev/max/marka/testdata v0.0.0-20250819195334-b3c01bb43d9a/go.mod h1:88SkY5pTONkgfBy1FT10LoqRC8rt36iF1fk/rupjuJY= +github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= +github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +go.yaml.in/yaml/v4 v4.0.0-rc.1 h1:4J1+yLKUIPGexM/Si+9d3pij4hdc7aGO04NhrElqXbY= +go.yaml.in/yaml/v4 v4.0.0-rc.1/go.mod h1:CBdeces52/nUXndfQ5OY8GEQuNR9uEEOJPZj/Xq5IzU= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/server/http/.env b/server/http/.env new file mode 100644 index 0000000..d417754 --- /dev/null +++ b/server/http/.env @@ -0,0 +1 @@ +SERVER_URL=http://localhost:8080 diff --git a/server/http/post-recipe.http b/server/http/post-recipe.http new file mode 100644 index 0000000..45154e2 --- /dev/null +++ b/server/http/post-recipe.http @@ -0,0 +1,5 @@ +POST {{SERVER_URL}}/Recipes/bolognaise.md + +{ + +} diff --git a/server/internal/adapters/errors.go b/server/internal/adapters/errors.go new file mode 100644 index 0000000..c121579 --- /dev/null +++ b/server/internal/adapters/errors.go @@ -0,0 +1,5 @@ +package adapters + +import "errors" + +var ErrNotFound = errors.New("not found") diff --git a/server/internal/adapters/fs.go b/server/internal/adapters/fs.go new file mode 100644 index 0000000..cfe95dd --- /dev/null +++ b/server/internal/adapters/fs.go @@ -0,0 +1,97 @@ +package adapters + +import ( + "os" + "path/filepath" +) + +type LocalFsAdapter struct { + root string +} + +func (l LocalFsAdapter) readDir(path string) (FsResponse, error) { + dirInfo, _ := os.Stat(path) + + entries, err := os.ReadDir(path) + if err != nil { + return FsResponse{}, err + } + + out := make([]FsDirEntry, 0, len(entries)) + for _, e := range entries { + info, _ := e.Info() + + entryType := "dir" + if !e.IsDir() { + entryType = contentTypeFor(e.Name()) + } + + out = append(out, FsDirEntry{ + Name: e.Name(), + Type: entryType, + ModTime: info.ModTime(), + }) + } + + return FsResponse{ + Dir: &FsDir{ + Files: out, + Name: ResponsePath(l.root, path), + ModTime: dirInfo.ModTime(), + }, + }, nil +} + +func (l LocalFsAdapter) readFile(path string) (FsResponse, error) { + fi, err := os.Stat(path) + if err != nil { + return FsResponse{}, err + } + + data, err := os.ReadFile(path) + if err != nil { + return FsResponse{}, err + } + + return FsResponse{ + File: &FsFile{ + Name: ResponsePath(l.root, path), + Type: contentTypeFor(path), + ModTime: fi.ModTime(), + Content: data, + }, + }, nil +} + +func (l LocalFsAdapter) Read(path string) (FsResponse, error) { + cleanRel, err := SafeRel(l.root, path) + if err != nil { + return FsResponse{}, err + } + target := filepath.Join(l.root, filepath.FromSlash(cleanRel)) + + fi, err := os.Stat(target) + if err != nil { + if os.IsNotExist(err) { + return FsResponse{}, ErrNotFound + } + + return FsResponse{}, err + } + + if fi.IsDir() { + return l.readDir(target) + } + + return l.readFile(target) +} + +func (LocalFsAdapter) Write(path string, content []byte) error { + return nil +} + +func NewLocalFsAdapter(root string) (FileAdapter, error) { + return LocalFsAdapter{ + root: root, + }, nil +} diff --git a/server/internal/adapters/fs_utils.go b/server/internal/adapters/fs_utils.go new file mode 100644 index 0000000..8ebdcde --- /dev/null +++ b/server/internal/adapters/fs_utils.go @@ -0,0 +1,78 @@ +package adapters + +import ( + "errors" + "mime" + "os" + "path/filepath" + "strings" + "time" +) + +func SafeRel(root, requested string) (string, error) { + s := requested + if after, ok := strings.CutPrefix(s, "/"); ok { + s = after + } + full := filepath.Join(root, filepath.FromSlash(s)) + rel, err := filepath.Rel(root, full) + if err != nil { + return "", err + } + if rel == "." { + return "/", nil + } + sep := string(filepath.Separator) + if strings.HasPrefix(rel, "..") || strings.Contains(rel, ".."+sep) { + return "", errors.New("path escapes root") + } + return "/" + filepath.ToSlash(rel), nil +} + +func ResponsePath(root, full string) string { + rel, err := filepath.Rel(root, full) + if err != nil || rel == "." { + return "/" + } + return "/" + filepath.ToSlash(rel) +} + +func sizeOrZero(fi os.FileInfo) int64 { + if fi == nil { + return 0 + } + return fi.Size() +} + +func modTimeOrZero(fi os.FileInfo) time.Time { + if fi == nil { + return time.Time{} + } + return fi.ModTime() +} + +var textPlainExtensions = map[string]bool{ + ".txt": true, + ".log": true, + ".json": true, + ".yaml": true, + ".yml": true, + ".toml": true, + ".xml": true, + ".csv": true, +} + +func contentTypeFor(name string) string { + ext := strings.ToLower(filepath.Ext(name)) + switch ext { + case ".md", ".markdown", ".mdown": + return "application/markdown" + } + if ct := mime.TypeByExtension(ext); ct != "" { + return ct + } + if textPlainExtensions[ext] { + return "text/plain; charset=utf-8" + } + return "application/octet-stream" +} diff --git a/server/internal/adapters/interface.go b/server/internal/adapters/interface.go new file mode 100644 index 0000000..a482676 --- /dev/null +++ b/server/internal/adapters/interface.go @@ -0,0 +1,34 @@ +// Package adapters are the backend to that connects the marka server to a storage +package adapters + +import "time" + +type FileAdapter interface { + Read(path string) (FsResponse, error) + Write(path string, content []byte) error +} + +type FsFile struct { + Name string `json:"name"` + Type string `json:"type"` + Content []byte `json:"content"` + ModTime time.Time `json:"modTime"` +} + +type FsDirEntry struct { + Name string `json:"name"` + Type string `json:"type"` + IsDir bool `json:"isDir,omitempty"` + ModTime time.Time `json:"modTime"` +} + +type FsDir struct { + Files []FsDirEntry `json:"files"` + Name string `json:"name"` + ModTime time.Time `json:"modTime"` +} + +type FsResponse struct { + Dir *FsDir `json:"dir,omitempty"` + File *FsFile `json:"file,omitempty"` +} diff --git a/server/internal/handler/error.go b/server/internal/handler/error.go new file mode 100644 index 0000000..37fcc3d --- /dev/null +++ b/server/internal/handler/error.go @@ -0,0 +1,20 @@ +package handler + +import ( + "encoding/json" + "net/http" +) + +type ErrorResponse struct { + Error string `json:"error"` +} + +func writeError(w http.ResponseWriter, code int, err error) { + writeJSON(w, code, ErrorResponse{Error: err.Error()}) +} + +func writeJSON(w http.ResponseWriter, code int, v any) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + _ = json.NewEncoder(w).Encode(v) +} diff --git a/server/internal/handler/handler.go b/server/internal/handler/handler.go new file mode 100644 index 0000000..9a1f13e --- /dev/null +++ b/server/internal/handler/handler.go @@ -0,0 +1,104 @@ +// Package handler provides the HTTP handler for the marka server +package handler + +import ( + "errors" + "net/http" + "time" + + "git.max-richter.dev/max/marka/parser" + "git.max-richter.dev/max/marka/server-new/internal/adapters" +) + +type ResponseItem struct { + Name string `json:"name"` + Content any `json:"content,omitempty"` + Type string `json:"type,omitempty"` + IsDir bool `json:"isDir"` + Size int64 `json:"size,omitempty"` + ModTime time.Time `json:"modTime"` +} + +type Handler struct { + adapter adapters.FileAdapter +} + +func (h *Handler) get(w http.ResponseWriter, target string) { + fsEntry, err := h.adapter.Read(target) + if err != nil { + writeError(w, 500, err) + return + } + + if fsEntry.File != nil { + + if fsEntry.File.Content != nil && fsEntry.File.Type == "application/markdown" { + data, err := parser.ParseFile(string(fsEntry.File.Content)) + if err != nil { + writeError(w, 500, err) + return + } + + res := ResponseItem{ + Name: fsEntry.File.Name, + Type: fsEntry.File.Type, + Content: data, + IsDir: false, + Size: int64(len(fsEntry.File.Content)), + ModTime: fsEntry.File.ModTime, + } + + writeJSON(w, 200, res) + return + } + + res := ResponseItem{ + Name: fsEntry.File.Name, + Content: fsEntry.File.Content, + Type: fsEntry.File.Type, + IsDir: false, + Size: int64(len(fsEntry.File.Content)), + ModTime: fsEntry.File.ModTime, + } + writeJSON(w, 200, res) + return + } + + if fsEntry.Dir != nil { + res := ResponseItem{ + Name: fsEntry.Dir.Name, + Content: fsEntry.Dir.Files, + IsDir: true, + ModTime: fsEntry.Dir.ModTime, + } + writeJSON(w, 200, res) + return + } +} + +func (h *Handler) post(w http.ResponseWriter, target string) { +} + +func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + reqPath := r.URL.Path + if reqPath == "" { + reqPath = "/" + } + + target := cleanURLLike(reqPath) + + switch r.Method { + case http.MethodGet: + h.get(w, target) + case http.MethodPost: + h.post(w, target) + default: + writeError(w, http.StatusMethodNotAllowed, errors.New("method not allowed")) + } +} + +func NewHandler(adapter adapters.FileAdapter) http.Handler { + return &Handler{ + adapter: adapter, + } +} diff --git a/server/internal/handler/utils.go b/server/internal/handler/utils.go new file mode 100644 index 0000000..1869ced --- /dev/null +++ b/server/internal/handler/utils.go @@ -0,0 +1,24 @@ +package handler + +import "strings" + +func cleanURLLike(p string) string { + p = strings.TrimSpace(p) + if p == "" || p == "/" { + return "/" + } + parts := []string{} + for seg := range strings.SplitSeq(p, "/") { + switch seg { + case "", ".": + continue + case "..": + if len(parts) > 0 { + parts = parts[:len(parts)-1] + } + default: + parts = append(parts, seg) + } + } + return "/" + strings.Join(parts, "/") +} diff --git a/template/blocks.go b/template/blocks.go index 37a5c87..820a2c1 100644 --- a/template/blocks.go +++ b/template/blocks.go @@ -1,11 +1,8 @@ -// Package template contains the logic for parsing template blocks. +// Package template contains the logic for parsing templates. package template import ( - "fmt" "strings" - - "go.yaml.in/yaml/v4" ) // TemplateType represents whether a template is short, long, or invalid. @@ -18,8 +15,8 @@ const ( ) // DetectTemplateType checks if the template is short or long. -func DetectTemplateType(tmpl string) TemplateType { - trimmed := strings.TrimSpace(tmpl) +func DetectTemplateType(tmpl Slice) TemplateType { + trimmed := strings.TrimSpace(tmpl.String()) // Short type: starts with "{" and ends with "}" on a single line, // and contains "|" or "," inside for inline definition @@ -45,123 +42,15 @@ func DetectTemplateType(tmpl string) TemplateType { return InvalidTemplate } -func cleanTemplate(input string) string { - s := strings.TrimSpace(input) +func cleanTemplate(input Slice) string { + s := strings.TrimSpace(input.String()) s = strings.TrimPrefix(s, "{") s = strings.TrimSuffix(s, "}") s = strings.Trim(s, "\n") return s } -func parseShortTemplate(input string) (Block, error) { - split := strings.Split(cleanTemplate(input), "|") - if len(split) < 1 { - return Block{}, fmt.Errorf("invalid short template") - } - - block := Block{ - Type: DataBlock, - Path: strings.TrimSpace(split[0]), - Codec: CodecText, - content: input, - } - - if len(split) > 1 { - optionSplit := strings.SplitSeq(split[1], ",") - for option := range optionSplit { - switch strings.TrimSpace(option) { - case "number": - block.Codec = CodecNumber - case "text": - block.Codec = CodecText - case "hashtags": - block.Codec = CodecHashtags - default: - return block, fmt.Errorf("unknown codec option: %s", option) - } - } - } - - return block, nil -} - -type yamlBlock struct { - Path string `yaml:"path"` - Codec string `yaml:"codec"` - Value any `yaml:"value,omitempty"` - Fields []yamlField `yaml:"fields"` - ListTemplate string `yaml:"listTemplate,omitempty"` - Hidden bool `yaml:"hidden,omitempty"` -} - -type yamlField struct { - Path string `yaml:"path"` - Value any `yaml:"value,omitempty"` - Codec string `yaml:"codec"` - Hidden bool `yaml:"hidden,omitempty"` -} - -func parseYamlTemplate(input string) (block Block, err error) { - var blk yamlBlock - - cleaned := cleanTemplate(input) - - dec := yaml.NewDecoder(strings.NewReader(cleaned)) - dec.KnownFields(true) - - if err := dec.Decode(&blk); err != nil { - return block, fmt.Errorf("content '%q': %w", cleaned, err) - } - - if blk.Path == "" { - return block, fmt.Errorf("missing top-level 'path'") - } - - if blk.Codec == "" { - blk.Codec = "text" - } - - codec, err := parseCodecType(blk.Codec) - if err != nil { - return block, fmt.Errorf("failed to parse codec: %w", err) - } - - var fields []BlockField - - for _, field := range blk.Fields { - if field.Path == "" { - return block, fmt.Errorf("failed to parse field: %v", field) - } - - if field.Codec == "" { - field.Codec = "text" - } - - fieldCodec, err := parseCodecType(field.Codec) - if err != nil { - return block, fmt.Errorf("failed to parse codec: %w", err) - } - - fields = append(fields, BlockField{ - Path: field.Path, - CodecType: fieldCodec, - Value: field.Value, - Hidden: field.Hidden, - }) - - } - - return Block{ - Type: DataBlock, - Path: blk.Path, - Codec: codec, - Fields: fields, - ListTemplate: blk.ListTemplate, - content: input, - }, nil -} - -func ParseTemplateBlock(template string, blockType BlockType) (block Block, err error) { +func ParseTemplateBlock(template Slice, blockType BlockType) (block Block, err error) { if blockType == MatchingBlock { return Block{ Type: MatchingBlock, @@ -176,5 +65,5 @@ func ParseTemplateBlock(template string, blockType BlockType) (block Block, err return parseYamlTemplate(template) } - return block, fmt.Errorf("invalid template") + return block, NewErrorf("invalid template: '%s'", template.String()).WithPosition(template.start, template.end) } diff --git a/template/blocks_short.go b/template/blocks_short.go new file mode 100644 index 0000000..972552b --- /dev/null +++ b/template/blocks_short.go @@ -0,0 +1,39 @@ +package template + +import ( + "fmt" + "strings" +) + +func parseShortTemplate(input Slice) (Block, error) { + split := strings.Split(cleanTemplate(input), "|") + if len(split) < 1 { + return Block{}, fmt.Errorf("invalid short template") + } + + block := Block{ + Type: DataBlock, + Path: strings.TrimSpace(split[0]), + Codec: CodecText, + content: input, + } + + if len(split) > 1 { + for option := range strings.SplitSeq(split[1], ",") { + switch strings.TrimSpace(option) { + case "number": + block.Codec = CodecNumber + case "text": + block.Codec = CodecText + case "hashtags": + block.Codec = CodecHashtags + case "optional": + block.Optional = true + default: + return block, fmt.Errorf("unknown codec option: %s", option) + } + } + } + + return block, nil +} diff --git a/template/blocks_yaml.go b/template/blocks_yaml.go new file mode 100644 index 0000000..a4a9db9 --- /dev/null +++ b/template/blocks_yaml.go @@ -0,0 +1,86 @@ +package template + +import ( + "fmt" + "strings" + + "go.yaml.in/yaml/v4" +) + +type yamlBlock struct { + Path string `yaml:"path"` + Codec string `yaml:"codec"` + Value any `yaml:"value,omitempty"` + Fields []yamlField `yaml:"fields"` + ListTemplate string `yaml:"listTemplate,omitempty"` + Hidden bool `yaml:"hidden,omitempty"` + PathAlias []string `yaml:"pathAlias,omitempty"` +} + +type yamlField struct { + Path string `yaml:"path"` + Value any `yaml:"value,omitempty"` + Codec string `yaml:"codec"` + Hidden bool `yaml:"hidden,omitempty"` + PathAlias string `yaml:"pathAlias,omitempty"` +} + +func parseYamlTemplate(input Slice) (block Block, err error) { + var blk yamlBlock + + cleaned := cleanTemplate(input) + + dec := yaml.NewDecoder(strings.NewReader(cleaned)) + dec.KnownFields(true) + + if err := dec.Decode(&blk); err != nil { + return block, NewErrorf("failed to parse yaml -> %w", err).WithPosition(input.start, input.end) + } + + if blk.Path == "" { + return block, fmt.Errorf("missing top-level 'path'") + } + + if blk.Codec == "" { + blk.Codec = "text" + } + + codec, err := parseCodecType(blk.Codec) + if err != nil { + return block, fmt.Errorf("failed to parse codec -> %w", err) + } + + var fields []BlockField + + for _, field := range blk.Fields { + if field.Path == "" { + return block, fmt.Errorf("failed to parse field: %v", field) + } + + if field.Codec == "" { + field.Codec = "text" + } + + fieldCodec, err := parseCodecType(field.Codec) + if err != nil { + return block, fmt.Errorf("failed to parse codec -> %w", err) + } + + fields = append(fields, BlockField{ + Path: field.Path, + CodecType: fieldCodec, + Value: field.Value, + Hidden: field.Hidden, + }) + + } + + return Block{ + Type: DataBlock, + Path: blk.Path, + Codec: codec, + Fields: fields, + ListTemplate: blk.ListTemplate, + content: input, + }, nil +} diff --git a/template/compile.go b/template/compile.go index ff22bb4..d82794d 100644 --- a/template/compile.go +++ b/template/compile.go @@ -1,13 +1,13 @@ package template -import ( - "fmt" -) +import "strings" // CompileTemplate scans once, emitting: // - data blocks: inner content between a line that's exactly "{" and a line that's exactly "}" // - matching blocks: gaps between data blocks (excluding the brace lines themselves) -func CompileTemplate(template string) ([]Block, error) { +func CompileTemplate(templateSource string) ([]Block, error) { + templateSource = strings.TrimSuffix(templateSource, "\n") + var out []Block var curlyIndex int @@ -16,14 +16,15 @@ func CompileTemplate(template string) ([]Block, error) { var start int var blockType BlockType + template := NewSlice(templateSource) - if len(template) > 0 && template[0] == OPENING { + if template.Len() > 0 && template.At(0) == OPENING { blockType = DataBlock } else { blockType = MatchingBlock } - for i, r := range template { + for i, r := range template.Chars() { nextCurlyIndex := curlyIndex @@ -36,27 +37,26 @@ func CompileTemplate(template string) ([]Block, error) { if curlyIndex == 0 && nextCurlyIndex == 1 { if i > start { - block, err := ParseTemplateBlock(template[start:i], blockType) + block, err := ParseTemplateBlock(template.Slice(start, i), blockType) if err != nil { - return nil, fmt.Errorf("cannot parse block: %w", err) + return nil, NewErrorf("cannot parse block @pos -> %w", err).WithPosition(start, i) } out = append(out, block) } start = i blockType = DataBlock } else if curlyIndex == 1 && nextCurlyIndex == 0 { - if i > start { - block, err := ParseTemplateBlock(template[start:i+1], blockType) + block, err := ParseTemplateBlock(template.Slice(start, i+1), blockType) if err != nil { - return nil, fmt.Errorf("cannot parse block: %w", err) + return nil, NewErrorf("cannot parse block @pos -> %w", err).WithPosition(start, i+1) } out = append(out, block) } nextChar := ' ' - if i+1 < len(template) { - nextChar = rune(template[i+1]) + if i+1 < template.Len() { + nextChar = rune(template.At(i + 1)) } if nextChar == OPENING { @@ -71,5 +71,17 @@ func CompileTemplate(template string) ([]Block, error) { curlyIndex = nextCurlyIndex } + if curlyIndex != 0 { + return nil, NewErrorf("unclosed block").WithPosition(start, template.Len()) + } + + if start < template.Len() { + block, err := ParseTemplateBlock(template.Slice(start, template.Len()), blockType) + if err != nil { + return nil, NewErrorf("cannot parse final block @pos -> %w", err).WithPosition(start, template.Len()) + } + out = append(out, block) + } + return out, nil } diff --git a/template/compile_test.go b/template/compile_test.go index f4efe95..6fe799f 100644 --- a/template/compile_test.go +++ b/template/compile_test.go @@ -1,6 +1,7 @@ package template_test import ( + "fmt" "testing" "git.max-richter.dev/max/marka/registry" @@ -10,16 +11,20 @@ import ( func TestExtractBlocks(t *testing.T) { src, err := registry.GetTemplate("Recipe") if err != nil { - t.Errorf("Failed to extract blocks: %s", err.Error()) + t.Errorf("failed to load template 'Recipe' -> %s", err.Error()) t.FailNow() } templateBlocks, err := template.CompileTemplate(src) if err != nil { - t.Errorf("Failed to extract blocks: %s", err.Error()) + t.Errorf("failed to compile template -> %s", err.Error()) t.FailNow() } + for i, b := range templateBlocks { + fmt.Printf("Block#%d: %q\n", i, b.GetContent()) + } + expected := []template.Block{ { Type: template.MatchingBlock, @@ -30,13 +35,13 @@ func TestExtractBlocks(t *testing.T) { Path: ".", Fields: []template.BlockField{ { - Path: "@type", + Path: "_type", }, { Path: "image", }, { - Path: "author.@type", + Path: "author._type", }, { Path: "author.name", diff --git a/template/error.go b/template/error.go new file mode 100644 index 0000000..df45db8 --- /dev/null +++ b/template/error.go @@ -0,0 +1,36 @@ +package template + +import ( + "fmt" + "strings" +) + +type Error struct { + err error + start, end int +} + +func (e Error) Error() string { + content := e.err.Error() + + if strings.Contains(content, " @pos ") { + if e.start == e.end { + return strings.ReplaceAll(content, " @pos ", " ") + } + return strings.ReplaceAll(content, " @pos ", fmt.Sprintf(" position=%d:%d ", e.start, e.end)) + } + + return content +} + +func NewErrorf(msg string, args ...any) Error { + return Error{ + err: fmt.Errorf(msg, args...), + } +} + +func (e Error) WithPosition(start, end int) Error { + e.start = start + e.end = end + return e +} diff --git a/template/go.mod b/template/go.mod index 2a9c7a9..cd0b20a 100644 --- a/template/go.mod +++ b/template/go.mod @@ -1,5 +1,8 @@ module git.max-richter.dev/max/marka/template -go 1.24.3 +go 1.24.7 -require go.yaml.in/yaml/v4 v4.0.0-rc.1 +require ( + git.max-richter.dev/max/marka/registry v0.0.0-20250819170608-69c2550f448e + go.yaml.in/yaml/v4 v4.0.0-rc.1 +) diff --git a/template/go.sum b/template/go.sum index 1dd5057..b6217d4 100644 --- a/template/go.sum +++ b/template/go.sum @@ -1,2 +1,4 @@ +git.max-richter.dev/max/marka/registry v0.0.0-20250819170608-69c2550f448e h1:eXAE0JHDvLGqtYSSlX5mw1XAuK+Cmu74c52PyveRhlE= +git.max-richter.dev/max/marka/registry v0.0.0-20250819170608-69c2550f448e/go.mod h1:n793S7TENIfgHpZLz0lm0qorM7eCx3zBLby3Fb++hZA= go.yaml.in/yaml/v4 v4.0.0-rc.1 h1:4J1+yLKUIPGexM/Si+9d3pij4hdc7aGO04NhrElqXbY= go.yaml.in/yaml/v4 v4.0.0-rc.1/go.mod h1:CBdeces52/nUXndfQ5OY8GEQuNR9uEEOJPZj/Xq5IzU= diff --git a/template/slice.go b/template/slice.go new file mode 100644 index 0000000..d1908e4 --- /dev/null +++ b/template/slice.go @@ -0,0 +1,46 @@ +package template + +func NewSlice(s string) Slice { + return Slice{ + source: &s, + start: 0, + end: len(s), + } +} + +type Slice struct { + source *string + start, end int +} + +func (s Slice) Chars() []byte { + return []byte((*s.source)[s.start:s.end]) +} + +func (s Slice) Len() int { + return s.end - s.start +} + +func (s Slice) At(i int) byte { + return (*s.source)[s.start+i] +} + +func SliceFromString(s string, start, end int) Slice { + return Slice{ + source: &s, + start: start, + end: end, + } +} + +func (s Slice) String() string { + return (*s.source)[s.start:s.end] +} + +func (s Slice) Slice(start, end int) Slice { + return Slice{ + source: s.source, + start: s.start + start, + end: s.start + end, + } +} diff --git a/template/structs.go b/template/structs.go index bf4a0da..ff8d6be 100644 --- a/template/structs.go +++ b/template/structs.go @@ -20,10 +20,11 @@ type Block struct { Codec CodecType ListTemplate string Fields []BlockField + Optional bool Value any - content string + content Slice } func (b Block) GetContent() string { - return b.content + return b.content.String() } diff --git a/testdata/data/article_simple/input.md b/testdata/data/article_simple/input.md index 4fe93f4..0a53a95 100644 --- a/testdata/data/article_simple/input.md +++ b/testdata/data/article_simple/input.md @@ -1,5 +1,5 @@ --- -@type: Article +_type: Article author.name: John Doe --- diff --git a/testdata/data/article_simple/output.json b/testdata/data/article_simple/output.json index c330d18..a8a1f8f 100644 --- a/testdata/data/article_simple/output.json +++ b/testdata/data/article_simple/output.json @@ -1,10 +1,10 @@ { - "@context": "https://schema.org", - "@type": "Article", + "_schema": "Article", + "_type": "Article", "headline": "My First Article", "author": { - "@type": "Person", + "_type": "Person", "name": "John Doe" }, "articleBody": "This is the content of my first article. It's a simple one." -} \ No newline at end of file +} diff --git a/testdata/data/baguette/input.md b/testdata/data/baguette/input.md index 37fd637..b1fb4bd 100644 --- a/testdata/data/baguette/input.md +++ b/testdata/data/baguette/input.md @@ -1,5 +1,5 @@ --- -@type: Recipe +_type: Recipe author.name: Max Richter --- diff --git a/testdata/data/baguette/output.json b/testdata/data/baguette/output.json index 7e0d06e..61e7b2a 100644 --- a/testdata/data/baguette/output.json +++ b/testdata/data/baguette/output.json @@ -1,9 +1,9 @@ { - "@context": "https://schema.org", - "@type": "Recipe", + "_schema": "Recipe", + "_type": "Recipe", "name": "Baguette", "author": { - "@type": "Person", + "_type": "Person", "name": "Max Richter" }, "description": "My favourite baguette recipe", diff --git a/testdata/data/complex_front_matter/input.md b/testdata/data/complex_front_matter/input.md index ad9d3f2..d384305 100644 --- a/testdata/data/complex_front_matter/input.md +++ b/testdata/data/complex_front_matter/input.md @@ -1,8 +1,8 @@ --- -@type: Book +_type: Book name: The Great Book author: - @type: Person + _type: Person name: Jane Doe email: jane.doe@example.com tags: @@ -17,4 +17,4 @@ chapters: # The Great Book -This is the content of the great book. \ No newline at end of file +This is the content of the great book. diff --git a/testdata/data/complex_front_matter/output.json b/testdata/data/complex_front_matter/output.json index 93f098d..5cfd11e 100644 --- a/testdata/data/complex_front_matter/output.json +++ b/testdata/data/complex_front_matter/output.json @@ -1,9 +1,8 @@ { - "@context": "https://schema.org", - "@type": "Book", + "_type": "Book", "name": "The Great Book", "author": { - "@type": "Person", + "_type": "Person", "name": "Jane Doe", "email": "jane.doe@example.com" }, @@ -23,4 +22,4 @@ ], "headline": "The Great Book", "articleBody": "This is the content of the great book." -} \ No newline at end of file +} diff --git a/testdata/data/recipe_no_description/input.md b/testdata/data/recipe_no_description/input.md index 4c21274..758b134 100644 --- a/testdata/data/recipe_no_description/input.md +++ b/testdata/data/recipe_no_description/input.md @@ -1,5 +1,5 @@ --- -@type: Recipe +_type: Recipe author.name: Alex Chef cookTime: PT0M image: https://example.com/salad.jpg diff --git a/testdata/data/recipe_no_description/output.json b/testdata/data/recipe_no_description/output.json index 3b2ae3f..835f831 100644 --- a/testdata/data/recipe_no_description/output.json +++ b/testdata/data/recipe_no_description/output.json @@ -1,11 +1,10 @@ { - "@context": "https://schema.org", - "@schema": "Recipe", - "@type": "Recipe", + "_schema": "Recipe", + "_type": "Recipe", "name": "Simple Salad", "image": "https://example.com/salad.jpg", "author": { - "@type": "Person", + "_type": "Person", "name": "Alex Chef" }, "prepTime": "PT10M", diff --git a/testdata/data/recipe_salad/input.md b/testdata/data/recipe_salad/input.md index 7d39d7a..c9c917d 100644 --- a/testdata/data/recipe_salad/input.md +++ b/testdata/data/recipe_salad/input.md @@ -1,5 +1,5 @@ --- -@type: Recipe +_type: Recipe author.name: Alex Chef cookTime: PT0M image: https://example.com/salad.jpg @@ -8,7 +8,6 @@ recipeYield: 2 servings --- # Simple Salad -#healthy #salad A quick green salad. diff --git a/testdata/data/recipe_salad/output.json b/testdata/data/recipe_salad/output.json index 6e5163d..0b908b4 100644 --- a/testdata/data/recipe_salad/output.json +++ b/testdata/data/recipe_salad/output.json @@ -1,17 +1,12 @@ { - "@context": "https://schema.org", - "@schema": "Recipe", - "@type": "Recipe", + "_schema": "Recipe", + "_type": "Recipe", "name": "Simple Salad", "image": "https://example.com/salad.jpg", "author": { - "@type": "Person", + "_type": "Person", "name": "Alex Chef" }, - "keywords": [ - "healthy", - "salad" - ], "description": "A quick green salad.", "prepTime": "PT10M", "cookTime": "PT0M", diff --git a/testdata/data/typo_section_header/input.md b/testdata/data/typo_section_header/input.md index a38af47..917eb8b 100644 --- a/testdata/data/typo_section_header/input.md +++ b/testdata/data/typo_section_header/input.md @@ -1,5 +1,5 @@ --- -@type: Recipe +_type: Recipe name: Typo Recipe --- @@ -11,4 +11,4 @@ name: Typo Recipe ## Stps 1. Step 1 -2. Step 2 \ No newline at end of file +2. Step 2 diff --git a/testdata/data/typo_section_header/output.json b/testdata/data/typo_section_header/output.json index a8fdd75..da7b691 100644 --- a/testdata/data/typo_section_header/output.json +++ b/testdata/data/typo_section_header/output.json @@ -1,6 +1,5 @@ { - "@context": "https://schema.org", - "@type": "Recipe", + "_type": "Recipe", "name": "Typo Recipe", "recipeIngredient": [ "Item 1", @@ -10,4 +9,4 @@ "Step 1", "Step 2" ] -} \ No newline at end of file +} diff --git a/testdata/go.mod b/testdata/go.mod index dd01c9b..ef2d7b5 100644 --- a/testdata/go.mod +++ b/testdata/go.mod @@ -1,3 +1,3 @@ module git.max-richter.dev/max/marka/testdata -go 1.24.5 +go 1.24.7 diff --git a/validator/go.mod b/validator/go.mod index 36391e1..84c4302 100644 --- a/validator/go.mod +++ b/validator/go.mod @@ -1,7 +1,10 @@ module git.max-richter.dev/max/marka/validator -go 1.24.5 +go 1.24.7 -require github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 +require ( + git.max-richter.dev/max/marka/registry v0.0.0-20250819170608-69c2550f448e + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 +) require golang.org/x/text v0.14.0 // indirect diff --git a/validator/go.sum b/validator/go.sum index b46b517..3ed7dc1 100644 --- a/validator/go.sum +++ b/validator/go.sum @@ -1,3 +1,7 @@ +git.max-richter.dev/max/marka/registry v0.0.0-20250819170608-69c2550f448e h1:eXAE0JHDvLGqtYSSlX5mw1XAuK+Cmu74c52PyveRhlE= +git.max-richter.dev/max/marka/registry v0.0.0-20250819170608-69c2550f448e/go.mod h1:n793S7TENIfgHpZLz0lm0qorM7eCx3zBLby3Fb++hZA= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= diff --git a/validator/validator_test.go b/validator/validator_test.go index 69a4817..7268eef 100644 --- a/validator/validator_test.go +++ b/validator/validator_test.go @@ -8,7 +8,7 @@ import ( func TestValidateRecipe_InvalidType(t *testing.T) { recipe := map[string]any{ - "@type": "Recipe", + "_type": "Recipe", "recipeYield": 4, "recipeIngredient": []string{ "500 g flour", @@ -24,7 +24,7 @@ func TestValidateRecipe_InvalidType(t *testing.T) { func TestValidateRecipe_Valid(t *testing.T) { recipe := map[string]any{ - "@type": "Recipe", + "_type": "Recipe", "name": "Simple Bread", "cookTime": "PT30M", "recipeIngredient": []any{"500 g flour", "300 ml water", "10 g salt", "3 g yeast"},