mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-06-06 00:50:15 -04:00
Compare commits
2 Commits
renovate/p
...
chore/docs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
845b2c2c73 | ||
|
|
15b3e59c55 |
@@ -44,7 +44,6 @@
|
||||
8000, // used by mkdocs
|
||||
9000,
|
||||
9091, // used by docker production
|
||||
51204, // used for test coverage report
|
||||
24678 // used by nuxt when hot-reloading using polling
|
||||
],
|
||||
// Use 'onCreateCommand' to run commands at the end of container creation.
|
||||
|
||||
2
.github/copilot-instructions.md
vendored
2
.github/copilot-instructions.md
vendored
@@ -63,7 +63,7 @@ task setup # Install all dependencies (Python + Node)
|
||||
task dev:services # Start Postgres & Mailpit containers
|
||||
task py # Start FastAPI backend (port 9000)
|
||||
task ui # Start Nuxt frontend (port 3000)
|
||||
task docs # Start MkDocs documentation server
|
||||
task docs # Start Zensical documentation server
|
||||
```
|
||||
|
||||
**Code generation (REQUIRED after schema changes):**
|
||||
|
||||
6
.github/workflows/docs.yml
vendored
6
.github/workflows/docs.yml
vendored
@@ -20,10 +20,6 @@ concurrency:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Install from the committed lockfile; never re-resolve (see pyproject
|
||||
# [tool.uv] exclude-newer cooling window).
|
||||
UV_FROZEN: "1"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
@@ -34,7 +30,7 @@ jobs:
|
||||
run: uv sync --only-group docs --no-install-project
|
||||
|
||||
- name: Build docs
|
||||
run: uv run --no-project mkdocs build -d site
|
||||
run: uv run --no-project zensical build
|
||||
working-directory: docs
|
||||
|
||||
- name: Upload artifact
|
||||
|
||||
4
.github/workflows/locale-sync.yml
vendored
4
.github/workflows/locale-sync.yml
vendored
@@ -14,10 +14,6 @@ permissions:
|
||||
jobs:
|
||||
sync-locales:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Install from the committed lockfile; never re-resolve (see pyproject
|
||||
# [tool.uv] exclude-newer cooling window).
|
||||
UV_FROZEN: "1"
|
||||
steps:
|
||||
- name: Generate GitHub App Token
|
||||
id: app-token
|
||||
|
||||
49
.github/workflows/pull-request-lint.yml
vendored
49
.github/workflows/pull-request-lint.yml
vendored
@@ -3,7 +3,7 @@ name: Pull Request Linter
|
||||
on:
|
||||
workflow_call:
|
||||
pull_request:
|
||||
types: [edited, reopened] # This captures the PR title/body changing
|
||||
types: [edited] # This captures the PR title changing
|
||||
branches:
|
||||
- mealie-next
|
||||
|
||||
@@ -41,50 +41,3 @@ jobs:
|
||||
ignoreLabels: |
|
||||
bot
|
||||
ignore-semantic-pull-request
|
||||
|
||||
validate-template:
|
||||
name: Validate PR template
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Check required PR template sections
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const pr = context.payload.pull_request;
|
||||
|
||||
if (pr.user.type === "Bot") {
|
||||
console.log("Skipping template check for bot");
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await github.rest.repos.getContent({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
path: ".github/pull_request_template.md",
|
||||
});
|
||||
|
||||
const template = Buffer.from(response.data.content, "base64").toString("utf8");
|
||||
const lines = template.split("\n");
|
||||
|
||||
const requiredHeadings = [];
|
||||
let lastHeading = null;
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("## ")) {
|
||||
lastHeading = line.trim();
|
||||
} else if (line.trim() === "_(REQUIRED)_" && lastHeading) {
|
||||
requiredHeadings.push(lastHeading);
|
||||
lastHeading = null;
|
||||
}
|
||||
}
|
||||
|
||||
const body = pr.body || "";
|
||||
const missing = requiredHeadings.filter(h => !body.includes(h));
|
||||
|
||||
if (missing.length > 0) {
|
||||
core.setFailed(`Missing headings:\n${missing.join("\n")}`);
|
||||
} else {
|
||||
console.log("All required headings present");
|
||||
}
|
||||
|
||||
2
.github/workflows/pull-requests.yml
vendored
2
.github/workflows/pull-requests.yml
vendored
@@ -18,8 +18,6 @@ jobs:
|
||||
name: "Lint PR"
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: ./.github/workflows/pull-request-lint.yml
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
backend-tests:
|
||||
name: "Backend Server Tests"
|
||||
|
||||
4
.github/workflows/test-backend.yml
vendored
4
.github/workflows/test-backend.yml
vendored
@@ -13,10 +13,6 @@ jobs:
|
||||
|
||||
env:
|
||||
PRODUCTION: false
|
||||
# Install from the committed lockfile; never re-resolve. The rolling
|
||||
# `exclude-newer` cooling window (pyproject [tool.uv]) would otherwise make
|
||||
# every uv command re-resolve and fail on in-window pins.
|
||||
UV_FROZEN: "1"
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
|
||||
30
.vscode/test-block.code-snippets
vendored
30
.vscode/test-block.code-snippets
vendored
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"Test Block": {
|
||||
"prefix": "mtest",
|
||||
"body": [
|
||||
"import { mount } from \"@vue/test-utils\";",
|
||||
"import { describe, expect, test, vi } from \"vitest\";",
|
||||
"import { makeWrapper } from \"~/tests/utils\";",
|
||||
"",
|
||||
"const wrapper = () => makeWrapper(() => {",
|
||||
" return ${1:composable}();",
|
||||
"});",
|
||||
"",
|
||||
"describe(\"${TM_FILENAME_BASE/(.*)\\..+$/$1/}\", () => {",
|
||||
" describe(\"${2:method}\", () => {",
|
||||
" test(\"It does the thing\", () => {",
|
||||
" const { ${2:method} } = wrapper();",
|
||||
" const result = ${2:method}();",
|
||||
" expect(result).toBe(EXPECTED);",
|
||||
" });",
|
||||
" });",
|
||||
"});",
|
||||
"",
|
||||
],
|
||||
"description": "Insert a test block",
|
||||
"scope": "typescript",
|
||||
"include": [
|
||||
"**/*.test.{ts,tsx,vue}"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,6 @@ env:
|
||||
DEFAULT_GROUP: Home
|
||||
DEFAULT_HOUSEHOLD: Family
|
||||
PRODUCTION: false
|
||||
# Install from the committed lockfile; never re-resolve. Required because the
|
||||
# rolling `exclude-newer` cooling window (pyproject [tool.uv]) would otherwise
|
||||
# make every `uv run`/`uv sync` re-resolve and fail on in-window pins.
|
||||
UV_FROZEN: "1"
|
||||
API_PORT: 9000
|
||||
API_DOCS: True
|
||||
TOKEN_TIME: 256 # hours
|
||||
@@ -33,7 +29,7 @@ tasks:
|
||||
desc: runs the documentation server
|
||||
dir: docs
|
||||
cmds:
|
||||
- uv run python -m mkdocs serve
|
||||
- uv run zensical serve
|
||||
|
||||
setup:ui:
|
||||
desc: setup frontend dependencies
|
||||
|
||||
@@ -52,11 +52,6 @@ RUN apt-get update \
|
||||
|
||||
RUN pip install uv
|
||||
|
||||
# Install from the committed lockfile; never re-resolve. The rolling
|
||||
# `exclude-newer` cooling window (pyproject [tool.uv]) would otherwise make
|
||||
# `uv export` below re-resolve and fail on in-window pins.
|
||||
ENV UV_FROZEN=1
|
||||
|
||||
WORKDIR /mealie
|
||||
|
||||
# copy project files here to ensure they will be cached.
|
||||
|
||||
@@ -17,6 +17,31 @@
|
||||
--md-default-accent-bg-color: #1f1e1e;
|
||||
}
|
||||
|
||||
/*
|
||||
* Zensical's "modern" theme ships a flat header that uses the page background
|
||||
* color. Restore the classic Material colored app-bar (and matching nav tabs)
|
||||
* driven by the brand orange, and brand the link color to match.
|
||||
*/
|
||||
.md-header,
|
||||
.md-tabs {
|
||||
background-color: var(--md-primary-fg-color);
|
||||
color: var(--md-primary-bg-color);
|
||||
}
|
||||
|
||||
/*
|
||||
* Brand the accent colors. The modern theme leaves links, the nav active-item
|
||||
* pill, and the announce banner on its default indigo accent (the latter two
|
||||
* via --md-accent-fg-color--transparent), which clashes with the orange. The
|
||||
* [scheme][primary] selector matches the theme's own (0,2,0) accent rules so
|
||||
* the override wins in both light (mealie) and dark (slate) modes.
|
||||
*/
|
||||
[data-md-color-scheme="mealie"][data-md-color-primary="indigo"],
|
||||
[data-md-color-scheme="slate"][data-md-color-primary="indigo"] {
|
||||
--md-typeset-a-color: var(--md-primary-fg-color);
|
||||
--md-accent-fg-color: #e58325;
|
||||
--md-accent-fg-color--transparent: #e583251a;
|
||||
}
|
||||
|
||||
/* frontpage elements */
|
||||
.tx-hero h1 {
|
||||
font-size: 2.41rem !important;
|
||||
@@ -64,7 +89,7 @@ th {
|
||||
}
|
||||
|
||||
.announce-left > a {
|
||||
color: #e0e0e0;
|
||||
color: var(--md-default-fg-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ If you have another provider you'd like to use, such as Azure, you can configure
|
||||
|
||||
Note that some models are capable of handling multiple features (e.g. `gpt-5` can handle both normal chat requests and image recognition requests). You may configure one provider for multiple provider features.
|
||||
|
||||
While Mealie has prompts for each AI task, you can override these with your own prompts if you'd like. For more information, check out the [backend configuration](./installation/ai-providers.md).
|
||||
While Mealie has prompts for each AI task, you can override these with your own prompts if you'd like. For more information, check out the [backend configuration](./backend-config.md).
|
||||
|
||||
## AI Features
|
||||
- The OpenAI Ingredient Parser can be used as an alternative to the NLP and Brute Force parsers. Simply choose the OpenAI parser while parsing ingredients (:octicons-tag-24: v1.7.0)
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
| --------------------------- | :-----: | ----------------------------------------------------------------------------------- |
|
||||
| SECURITY_MAX_LOGIN_ATTEMPTS | 5 | Maximum times a user can provide an invalid password before their account is locked |
|
||||
| SECURITY_USER_LOCKOUT_TIME | 24 | Time in hours for how long a users account is locked |
|
||||
| ALLOWED_IFRAME_HOSTS | `""` | Comma-separated extra hostnames allowed as `<iframe>` sources in recipe content. Extends the built-in list of trusted video providers (YouTube, Vimeo). Subdomains are included automatically. Only `https` sources are permitted. Adding hosts here opts into rendering embeds from those origins to all viewers, including the public, so add only origins you trust. |
|
||||
|
||||
### Database
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
site_name: Mealie
|
||||
demo_url: https://demo.mealie.io
|
||||
site_url: https://docs.mealie.io
|
||||
use_directory_urls: true
|
||||
theme:
|
||||
@@ -16,7 +15,7 @@ theme:
|
||||
toggle:
|
||||
icon: material/weather-sunny
|
||||
name: Switch to light mode
|
||||
custom_dir: docs/overrides
|
||||
custom_dir: overrides
|
||||
features:
|
||||
- content.code.annotate
|
||||
- content.code.copy
|
||||
@@ -28,6 +27,9 @@ theme:
|
||||
- navigation.tabs.sticky
|
||||
favicon: assets/img/favicon.png
|
||||
name: material
|
||||
font:
|
||||
text: Roboto
|
||||
code: Roboto Mono
|
||||
icon:
|
||||
logo: material/silverware-variant
|
||||
|
||||
@@ -50,6 +52,8 @@ markdown_extensions:
|
||||
class: mermaid
|
||||
format: !!python/name:pymdownx.superfences.fence_code_format
|
||||
- pymdownx.details
|
||||
extra:
|
||||
demo_url: https://demo.mealie.io
|
||||
extra_css:
|
||||
- assets/stylesheets/custom.css
|
||||
extra_javascript:
|
||||
|
||||
@@ -228,7 +228,7 @@
|
||||
class="md-button md-button--primary">
|
||||
Get started
|
||||
</a>
|
||||
<a href="{{ config.demo_url }}" title="{{ lang.t('source.link.title') }}" target="_blank" class="md-button">
|
||||
<a href="{{ config.extra.demo_url }}" title="View the Mealie demo" target="_blank" class="md-button">
|
||||
View the Demo
|
||||
</a>
|
||||
</div>
|
||||
@@ -1,54 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<p>
|
||||
To harden Mealie against malicious content, <code><iframe></code> embeds in recipe
|
||||
instructions, notes, and descriptions are now restricted to a trusted set of hosts.
|
||||
</p>
|
||||
<div class="mb-2">
|
||||
By default, embeds are allowed only from well-known video providers:
|
||||
<ul class="ml-6">
|
||||
<li>YouTube</li>
|
||||
<li>Vimeo</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>
|
||||
Existing recipes that embed content from <strong>other</strong> hosts will no longer render
|
||||
those embeds. The rest of the recipe is unaffected.
|
||||
</p>
|
||||
<div v-if="user?.admin">
|
||||
<hr class="mt-2 mb-4">
|
||||
<p>
|
||||
As an admin, you can allow additional hosts with the <code>ALLOWED_IFRAME_HOSTS</code>
|
||||
environment variable (comma-separated). It extends the built-in defaults, and only
|
||||
<code>https</code> sources are permitted. See the configuration docs for details:
|
||||
<br>
|
||||
<v-btn
|
||||
class="mt-2"
|
||||
color="primary"
|
||||
href="https://docs.mealie.io/documentation/getting-started/installation/backend-config/"
|
||||
target="_blank"
|
||||
>
|
||||
Backend Configuration
|
||||
</v-btn>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { AnnouncementMeta } from "~/composables/use-announcements";
|
||||
|
||||
const { user } = useMealieAuth();
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export const meta: AnnouncementMeta = {
|
||||
title: "Recipe embeds restricted to trusted hosts",
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="css">
|
||||
p {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -59,10 +59,9 @@
|
||||
>
|
||||
<v-card-text>
|
||||
{{ $t("general.confirm-delete-generic") }}
|
||||
<p v-if="deleteTarget" class="mt-4 mb-0 font-weight-bold">
|
||||
<p v-if="deleteTarget" class="mt-4 ml-4">
|
||||
{{ deleteTarget.name || deleteTarget.title || deleteTarget.id }}
|
||||
</p>
|
||||
<slot name="delete-dialog-bottom" />
|
||||
</v-card-text>
|
||||
</BaseDialog>
|
||||
|
||||
@@ -89,7 +88,6 @@
|
||||
</template>
|
||||
</v-virtual-scroll>
|
||||
</v-card>
|
||||
<slot name="delete-dialog-bottom" />
|
||||
</v-card-text>
|
||||
</BaseDialog>
|
||||
|
||||
@@ -153,7 +151,7 @@ const createDialog = defineModel("createDialog", { type: Boolean, default: false
|
||||
const editForm = defineModel<{ items: AutoFormItems; data: Record<string, any> }>("editForm", { required: true });
|
||||
const editDialog = defineModel("editDialog", { type: Boolean, default: false });
|
||||
|
||||
const props = defineProps({
|
||||
defineProps({
|
||||
icon: {
|
||||
type: String,
|
||||
required: true,
|
||||
@@ -187,10 +185,6 @@ const props = defineProps({
|
||||
type: String,
|
||||
default: "name",
|
||||
},
|
||||
onDeleteDialogOpen: {
|
||||
type: Function as PropType<(items: any[]) => Promise<void>>,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
@@ -218,11 +212,8 @@ const editEventHandler = (item: any) => {
|
||||
const deleteTarget = ref<any>(null);
|
||||
const deleteDialog = ref(false);
|
||||
|
||||
async function deleteEventHandler(item: any) {
|
||||
function deleteEventHandler(item: any) {
|
||||
deleteTarget.value = item;
|
||||
if (props.onDeleteDialogOpen) {
|
||||
await props.onDeleteDialogOpen([item]);
|
||||
}
|
||||
deleteDialog.value = true;
|
||||
}
|
||||
|
||||
@@ -231,11 +222,8 @@ async function deleteEventHandler(item: any) {
|
||||
const bulkDeleteTarget = ref<Array<any>>([]);
|
||||
const bulkDeleteDialog = ref(false);
|
||||
|
||||
async function bulkDeleteEventHandler(items: Array<any>) {
|
||||
function bulkDeleteEventHandler(items: Array<any>) {
|
||||
bulkDeleteTarget.value = items;
|
||||
if (props.onDeleteDialogOpen) {
|
||||
await props.onDeleteDialogOpen(items);
|
||||
}
|
||||
bulkDeleteDialog.value = true;
|
||||
console.log("Bulk Delete Event Handler", items);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { RecipeCategory, RecipeTag, RecipeTool } from "~/lib/api/types/recipe";
|
||||
import { truncateText as truncatePlainText } from "~/lib/sanitize/text";
|
||||
|
||||
export type UrlPrefixParam = "tags" | "categories" | "tools";
|
||||
|
||||
@@ -51,7 +50,10 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
defineEmits(["item-selected"]);
|
||||
function truncateText(text: string, length = 20, clamp = "...") {
|
||||
if (!props.truncate) return text;
|
||||
return truncatePlainText(text, length, clamp);
|
||||
const node = document.createElement("div");
|
||||
node.innerHTML = text;
|
||||
const content = node.textContent || "";
|
||||
return content.length > length ? content.slice(0, length) + clamp : content;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import DOMPurify from "isomorphic-dompurify";
|
||||
import { marked } from "marked";
|
||||
import { sanitizeMarkdownHtml } from "~/lib/sanitize/markdown";
|
||||
|
||||
enum DOMPurifyHook {
|
||||
UponSanitizeAttribute = "uponSanitizeAttribute",
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
source: {
|
||||
@@ -14,11 +18,48 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const { $appInfo } = useNuxtApp();
|
||||
const ALLOWED_STYLE_TAGS = [
|
||||
"background-color", "color", "font-style", "font-weight", "text-decoration", "text-align",
|
||||
];
|
||||
|
||||
function sanitizeMarkdown(rawHtml: string | null | undefined): string {
|
||||
if (!rawHtml) {
|
||||
return "";
|
||||
}
|
||||
|
||||
DOMPurify.addHook(DOMPurifyHook.UponSanitizeAttribute, (node, data) => {
|
||||
if (data.attrName === "style") {
|
||||
const styles = data.attrValue.split(";").filter((style) => {
|
||||
const [property] = style.split(":");
|
||||
return ALLOWED_STYLE_TAGS.includes(property.trim().toLowerCase());
|
||||
});
|
||||
data.attrValue = styles.join(";");
|
||||
}
|
||||
});
|
||||
|
||||
const sanitized = DOMPurify.sanitize(rawHtml, {
|
||||
ALLOWED_TAGS: [
|
||||
"strong", "em", "b", "i", "u", "p", "code", "pre", "samp", "kbd", "var", "sub", "sup", "dfn", "cite",
|
||||
"small", "address", "hr", "br", "id", "div", "span", "h1", "h2", "h3", "h4", "h5", "h6",
|
||||
"ul", "ol", "li", "dl", "dt", "dd", "abbr", "a", "img", "blockquote", "iframe",
|
||||
"del", "ins", "table", "thead", "tbody", "tfoot", "tr", "th", "td", "colgroup",
|
||||
],
|
||||
ALLOWED_ATTR: [
|
||||
"href", "src", "alt", "height", "width", "class", "allow", "title", "allowfullscreen", "frameborder",
|
||||
"scrolling", "cite", "datetime", "name", "abbr", "target", "border", "start", "style",
|
||||
],
|
||||
});
|
||||
|
||||
Object.values(DOMPurifyHook).forEach((hook) => {
|
||||
DOMPurify.removeHook(hook);
|
||||
});
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
const value = computed(() => {
|
||||
const rawHtml = marked.parse(props.source || "", { async: false, breaks: true });
|
||||
return sanitizeMarkdownHtml(rawHtml, $appInfo?.allowedIframeHosts ?? []);
|
||||
return sanitizeMarkdown(rawHtml);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
import type { IngredientFood, RecipeSummary, ShoppingListItemOut, ShoppingListMultiPurposeLabelOut, ShoppingListOut } from "~/lib/api/types/household";
|
||||
|
||||
export const MOCK_ITEM: ShoppingListItemOut = {
|
||||
shoppingListId: "",
|
||||
id: "",
|
||||
groupId: "",
|
||||
householdId: "",
|
||||
display: "MOCK_ITEM",
|
||||
updatedAt: "100",
|
||||
position: 1,
|
||||
checked: false,
|
||||
createdAt: "100",
|
||||
};
|
||||
|
||||
export const MOCK_RECIPE: RecipeSummary = {
|
||||
id: "recipe-id",
|
||||
name: "Recipe!",
|
||||
};
|
||||
|
||||
export const MOCK_RECIPE2: RecipeSummary = {
|
||||
...MOCK_RECIPE,
|
||||
id: undefined,
|
||||
name: "Recipe 2!",
|
||||
};
|
||||
|
||||
export const MOCK_FOOD: IngredientFood = {
|
||||
id: "1",
|
||||
name: "food 1",
|
||||
};
|
||||
|
||||
export const MOCK_FOOD2: IngredientFood = {
|
||||
id: "2",
|
||||
name: "food 2",
|
||||
};
|
||||
|
||||
export const MOCK_LABEL: ShoppingListMultiPurposeLabelOut = {
|
||||
shoppingListId: "",
|
||||
labelId: "",
|
||||
id: "",
|
||||
label: {
|
||||
name: "MOCK_LABEL",
|
||||
groupId: "",
|
||||
id: "",
|
||||
},
|
||||
};
|
||||
|
||||
export const MOCK_LABEL2: ShoppingListMultiPurposeLabelOut = {
|
||||
shoppingListId: "",
|
||||
labelId: "",
|
||||
id: "",
|
||||
label: {
|
||||
name: "MOCK_LABEL2",
|
||||
groupId: "",
|
||||
id: "",
|
||||
},
|
||||
};
|
||||
|
||||
export const MOCK_SHOPPING_LIST: ShoppingListOut = {
|
||||
groupId: "",
|
||||
userId: "",
|
||||
id: "",
|
||||
householdId: "",
|
||||
labelSettings: [
|
||||
MOCK_LABEL,
|
||||
MOCK_LABEL2,
|
||||
],
|
||||
listItems: [
|
||||
MOCK_ITEM,
|
||||
],
|
||||
recipeReferences: [{
|
||||
id: "",
|
||||
shoppingListId: "",
|
||||
recipeId: "",
|
||||
recipeQuantity: 0,
|
||||
recipe: MOCK_RECIPE,
|
||||
}, {
|
||||
id: "",
|
||||
shoppingListId: "",
|
||||
recipeId: "",
|
||||
recipeQuantity: 0,
|
||||
recipe: MOCK_RECIPE2,
|
||||
}],
|
||||
};
|
||||
@@ -1,89 +0,0 @@
|
||||
import * as vueusecore from "@vueuse/core";
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import type { ShoppingListItemOut } from "~/lib/api/types/household";
|
||||
import { makeWrapper } from "~/tests/utils";
|
||||
import { useShoppingListCopy } from "../use-shopping-list-copy";
|
||||
import { MOCK_ITEM } from "./mocks";
|
||||
|
||||
vi.mock("@vueuse/core", { spy: true });
|
||||
|
||||
const mockCopy = vi.fn().mockImplementation(args => new Promise(resolve => resolve(args)));
|
||||
|
||||
vi.mocked(vueusecore.useClipboard).mockImplementation(() => {
|
||||
return {
|
||||
isSupported: computed(() => true),
|
||||
copied: computed(() => true),
|
||||
text: computed(() => ""),
|
||||
copy: mockCopy,
|
||||
};
|
||||
});
|
||||
const wrapper = () => makeWrapper(useShoppingListCopy);
|
||||
|
||||
const TEST_HEADER = "SPECIAL HEADER!";
|
||||
|
||||
const MOCK_LIST: { [key: string]: ShoppingListItemOut[] } = {
|
||||
[TEST_HEADER]: [MOCK_ITEM],
|
||||
[TEST_HEADER + "2"]: [MOCK_ITEM],
|
||||
};
|
||||
|
||||
describe("Shopping list copy composable", () => {
|
||||
describe("copyListItems", () => {
|
||||
test("copies markdown lists correctly", () => {
|
||||
const { copyListItems } = wrapper();
|
||||
copyListItems(MOCK_LIST, "markdown");
|
||||
const expected = [
|
||||
"# SPECIAL HEADER!",
|
||||
"- [ ] MOCK_ITEM",
|
||||
"",
|
||||
"# SPECIAL HEADER!2",
|
||||
"- [ ] MOCK_ITEM",
|
||||
].join("\n");
|
||||
|
||||
expect(mockCopy).toBeCalledWith(expected);
|
||||
});
|
||||
test("copies plain text lists correctly", () => {
|
||||
const { copyListItems } = wrapper();
|
||||
copyListItems(MOCK_LIST, "plain");
|
||||
const expected = [
|
||||
"[SPECIAL HEADER!]",
|
||||
"MOCK_ITEM",
|
||||
"",
|
||||
"[SPECIAL HEADER!2]",
|
||||
"MOCK_ITEM",
|
||||
].join("\n");
|
||||
|
||||
expect(mockCopy).toBeCalledWith(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatCopiedLabelHeading", () => {
|
||||
test("copies markdown headers correctly", () => {
|
||||
const { formatCopiedLabelHeading } = wrapper();
|
||||
const header = formatCopiedLabelHeading("markdown", TEST_HEADER);
|
||||
expect(header).toEqual(`# ${TEST_HEADER}`);
|
||||
});
|
||||
test("copies plain text headers correctly", () => {
|
||||
const { formatCopiedLabelHeading } = wrapper();
|
||||
const header = formatCopiedLabelHeading("plain", TEST_HEADER);
|
||||
expect(header).toEqual(`[${TEST_HEADER}]`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatCopiedListItem", () => {
|
||||
test("copies markdown items correctly", () => {
|
||||
const { formatCopiedListItem } = wrapper();
|
||||
const header = formatCopiedListItem("markdown", MOCK_ITEM);
|
||||
expect(header).toEqual(`- [ ] ${MOCK_ITEM.display}`);
|
||||
});
|
||||
test("copies plain text items correctly", () => {
|
||||
const { formatCopiedListItem } = wrapper();
|
||||
const header = formatCopiedListItem("plain", MOCK_ITEM);
|
||||
expect(header).toEqual(MOCK_ITEM.display);
|
||||
});
|
||||
test("copies items without a display as empty", () => {
|
||||
const { formatCopiedListItem } = wrapper();
|
||||
const header = formatCopiedListItem("plain", { ...MOCK_ITEM, display: undefined });
|
||||
expect(header).toEqual("");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,175 +0,0 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import type { ShoppingListOut } from "~/lib/api/types/household";
|
||||
import { makeWrapper } from "~/tests/utils";
|
||||
import { useShoppingListSorting } from "../use-shopping-list-sorting";
|
||||
import { MOCK_FOOD, MOCK_FOOD2, MOCK_ITEM, MOCK_LABEL, MOCK_LABEL2, MOCK_SHOPPING_LIST } from "./mocks";
|
||||
|
||||
const wrapper = () => makeWrapper(() => {
|
||||
const { t } = useI18n();
|
||||
return {
|
||||
t,
|
||||
...useShoppingListSorting(),
|
||||
};
|
||||
});
|
||||
|
||||
describe("use-shopping-list-sorting", () => {
|
||||
describe("sortItems", () => {
|
||||
const { sortItems } = wrapper();
|
||||
test("sorts by position first", () => {
|
||||
const result = sortItems(MOCK_ITEM, { ...MOCK_ITEM, position: 0 });
|
||||
const result2 = sortItems({ ...MOCK_ITEM, position: 0 }, MOCK_ITEM);
|
||||
expect(result).toBe(1);
|
||||
expect(result2).toBe(-1);
|
||||
});
|
||||
test("sorts by createdAt next", () => {
|
||||
const result = sortItems(MOCK_ITEM, { ...MOCK_ITEM, createdAt: "0" });
|
||||
const result2 = sortItems({ ...MOCK_ITEM, createdAt: "0" }, MOCK_ITEM);
|
||||
expect(result).toBe(1);
|
||||
expect(result2).toBe(-1);
|
||||
});
|
||||
test("sorts similar items into the same spot", () => {
|
||||
const result = sortItems(MOCK_ITEM, MOCK_ITEM);
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
test("handles nulls", () => {
|
||||
const result = sortItems(MOCK_ITEM, { ...MOCK_ITEM, position: undefined });
|
||||
const result2 = sortItems({ ...MOCK_ITEM, position: undefined }, MOCK_ITEM);
|
||||
expect(result).toBe(1);
|
||||
expect(result2).toBe(-1);
|
||||
});
|
||||
test("handles nulls", () => {
|
||||
const result = sortItems(MOCK_ITEM, { ...MOCK_ITEM, createdAt: undefined });
|
||||
const result2 = sortItems({ ...MOCK_ITEM, createdAt: undefined }, MOCK_ITEM);
|
||||
expect(result).toBe(1);
|
||||
expect(result2).toBe(-1);
|
||||
});
|
||||
});
|
||||
describe("sortListItems", () => {
|
||||
const { sortListItems } = wrapper();
|
||||
test("sorts by position first", () => {
|
||||
const sortedList = { ...MOCK_SHOPPING_LIST, listItems: [MOCK_ITEM, { ...MOCK_ITEM, position: 0 }, { ...MOCK_ITEM, createdAt: "0" }] };
|
||||
sortListItems(sortedList);
|
||||
expect(sortedList.listItems).toEqual([
|
||||
{ ...MOCK_ITEM, position: 0 },
|
||||
{ ...MOCK_ITEM, createdAt: "0" },
|
||||
MOCK_ITEM,
|
||||
]);
|
||||
});
|
||||
test("handles nulls", () => {
|
||||
const sortedList = { ...MOCK_SHOPPING_LIST, listItems: undefined };
|
||||
sortListItems(sortedList);
|
||||
expect(sortedList.listItems).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
describe("updateItemsByLabel", () => {
|
||||
const { updateItemsByLabel, t } = wrapper();
|
||||
test("sorts by group", () => {
|
||||
const sortedList = {
|
||||
...MOCK_SHOPPING_LIST, listItems: [
|
||||
MOCK_ITEM,
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL2.label, labelId: "2" },
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL.label, labelId: "1" },
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL.label, labelId: "1" },
|
||||
],
|
||||
};
|
||||
const result = updateItemsByLabel(sortedList);
|
||||
expect(result).toEqual({
|
||||
[t("shopping-list.no-label")]: [
|
||||
MOCK_ITEM,
|
||||
],
|
||||
[MOCK_LABEL.label.name]: [
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL.label, labelId: "1" },
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL.label, labelId: "1" },
|
||||
],
|
||||
[MOCK_LABEL2.label.name]: [
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL2.label, labelId: "2" },
|
||||
],
|
||||
});
|
||||
});
|
||||
test("ignores checked items", () => {
|
||||
const sortedList = {
|
||||
...MOCK_SHOPPING_LIST, listItems: [
|
||||
MOCK_ITEM,
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL2.label, labelId: "2" },
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL.label, labelId: "1" },
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL.label, labelId: "1", checked: true },
|
||||
],
|
||||
};
|
||||
const result = updateItemsByLabel(sortedList);
|
||||
expect(result).toEqual({
|
||||
[t("shopping-list.no-label")]: [
|
||||
MOCK_ITEM,
|
||||
],
|
||||
[MOCK_LABEL.label.name]: [
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL.label, labelId: "1" },
|
||||
],
|
||||
[MOCK_LABEL2.label.name]: [
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL2.label, labelId: "2" },
|
||||
],
|
||||
});
|
||||
});
|
||||
test("returns unordered labels if no ordering is specified", () => {
|
||||
const sortedList = {
|
||||
...MOCK_SHOPPING_LIST,
|
||||
labelSettings: undefined,
|
||||
listItems: [
|
||||
MOCK_ITEM,
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL2.label, labelId: "2" },
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL.label, labelId: "1" },
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL.label, labelId: "1", checked: true },
|
||||
],
|
||||
};
|
||||
const result = updateItemsByLabel(sortedList);
|
||||
expect(result).toEqual({
|
||||
[t("shopping-list.no-label")]: [
|
||||
MOCK_ITEM,
|
||||
],
|
||||
[MOCK_LABEL2.label.name]: [
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL2.label, labelId: "2" },
|
||||
],
|
||||
[MOCK_LABEL.label.name]: [
|
||||
{ ...MOCK_ITEM, label: MOCK_LABEL.label, labelId: "1" },
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("groupAndSortListItemsByFood", () => {
|
||||
const { groupAndSortListItemsByFood } = wrapper();
|
||||
test("sorts by group", () => {
|
||||
const sortedList = { ...MOCK_SHOPPING_LIST };
|
||||
groupAndSortListItemsByFood(sortedList);
|
||||
expect(sortedList.listItems).toEqual(MOCK_SHOPPING_LIST.listItems);
|
||||
});
|
||||
test("groups checked items together", () => {
|
||||
const sortedList: ShoppingListOut = {
|
||||
...MOCK_SHOPPING_LIST, listItems: [
|
||||
{ ...MOCK_ITEM, checked: true, food: MOCK_FOOD },
|
||||
{ ...MOCK_ITEM, checked: true, food: MOCK_FOOD2 },
|
||||
],
|
||||
};
|
||||
groupAndSortListItemsByFood(sortedList);
|
||||
expect(sortedList.listItems).toEqual([
|
||||
{ ...MOCK_ITEM, checked: true, food: MOCK_FOOD },
|
||||
{ ...MOCK_ITEM, checked: true, food: MOCK_FOOD2, position: 1 },
|
||||
]);
|
||||
});
|
||||
test("populates position and created at if not present", () => {
|
||||
const sortedList: ShoppingListOut = {
|
||||
...MOCK_SHOPPING_LIST, listItems: [
|
||||
{ ...MOCK_ITEM, food: MOCK_FOOD, position: undefined },
|
||||
{ ...MOCK_ITEM, food: MOCK_FOOD2, createdAt: undefined },
|
||||
],
|
||||
};
|
||||
groupAndSortListItemsByFood(sortedList);
|
||||
expect(sortedList.listItems).toEqual([
|
||||
{ ...MOCK_ITEM, food: MOCK_FOOD2, createdAt: undefined },
|
||||
{ ...MOCK_ITEM, food: MOCK_FOOD, position: 1 },
|
||||
]);
|
||||
});
|
||||
test("handles nulls", () => {
|
||||
const sortedList: ShoppingListOut = { ...MOCK_SHOPPING_LIST, listItems: undefined };
|
||||
groupAndSortListItemsByFood(sortedList);
|
||||
expect(sortedList.listItems).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,63 +0,0 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import type { ShoppingListOut } from "~/lib/api/types/household";
|
||||
import { makeWrapper } from "~/tests/utils";
|
||||
import { useShoppingListState } from "../use-shopping-list-state";
|
||||
import { MOCK_ITEM, MOCK_RECIPE, MOCK_RECIPE2, MOCK_SHOPPING_LIST } from "./mocks";
|
||||
|
||||
const wrapper = (list: ShoppingListOut = MOCK_SHOPPING_LIST) => makeWrapper(() => {
|
||||
const { shoppingList, ...state } = useShoppingListState();
|
||||
shoppingList.value = list;
|
||||
return {
|
||||
shoppingList,
|
||||
...state,
|
||||
};
|
||||
});
|
||||
|
||||
describe("use-shopping-list-state", () => {
|
||||
describe("checked items are sorted", () => {
|
||||
const { sortCheckedItems } = wrapper();
|
||||
|
||||
test("by timestamp", () => {
|
||||
const sorted = sortCheckedItems(MOCK_ITEM, { ...MOCK_ITEM, updatedAt: "200" });
|
||||
const sorted2 = sortCheckedItems(MOCK_ITEM, { ...MOCK_ITEM, updatedAt: "0" });
|
||||
expect(sorted).toBe(1);
|
||||
expect(sorted2).toBe(-1);
|
||||
});
|
||||
test("by position if timestamps match", () => {
|
||||
const sorted = sortCheckedItems(MOCK_ITEM, { ...MOCK_ITEM, position: 2 });
|
||||
const sorted2 = sortCheckedItems(MOCK_ITEM, { ...MOCK_ITEM, position: 0 });
|
||||
const sorted3 = sortCheckedItems({ ...MOCK_ITEM, position: undefined }, { ...MOCK_ITEM, position: undefined });
|
||||
expect(sorted).toBe(1);
|
||||
expect(sorted2).toBe(-1);
|
||||
expect(sorted3).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("recipeMap", () => {
|
||||
test("Updates to match shopping list recipe references", () => {
|
||||
const { recipeMap } = wrapper();
|
||||
expect(recipeMap).toEqual(new Map([
|
||||
[MOCK_RECIPE.id, MOCK_RECIPE],
|
||||
["", MOCK_RECIPE2],
|
||||
]));
|
||||
});
|
||||
test("handles nulls", () => {
|
||||
const { recipeMap } = wrapper({ ...MOCK_SHOPPING_LIST, recipeReferences: undefined });
|
||||
expect(recipeMap).toEqual(new Map([]));
|
||||
});
|
||||
});
|
||||
|
||||
describe("checked and unchecked items", () => {
|
||||
test("update appropriately", () => {
|
||||
const mockCheckedItem = { ...MOCK_ITEM, checked: true };
|
||||
const { listItems: { checked, unchecked } } = wrapper({
|
||||
...MOCK_SHOPPING_LIST, listItems: [
|
||||
MOCK_ITEM,
|
||||
mockCheckedItem,
|
||||
],
|
||||
});
|
||||
expect(unchecked[0]).toEqual(MOCK_ITEM);
|
||||
expect(checked[0]).toEqual(mockCheckedItem);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -462,7 +462,7 @@
|
||||
"mealie-text": "Mealie can import recipes from the Mealie application from a pre v1.0 release. Export your recipes from your old instance, and upload the zip file below. Note that only recipes can be imported from the export.",
|
||||
"plantoeat": {
|
||||
"title": "Plan to Eat",
|
||||
"description-long": "Mealie can import recipes from Plan to Eat. Upload a ZIP archive, CSV, or TXT file exported from Plan to Eat."
|
||||
"description-long": "Mealie can import recipies from Plan to Eat."
|
||||
},
|
||||
"myrecipebox": {
|
||||
"title": "My Recipe Box",
|
||||
@@ -1144,8 +1144,6 @@
|
||||
},
|
||||
"data-pages": {
|
||||
"foods": {
|
||||
"delete-affects-recipes": "Warning: this food is used in {count} recipe(s). Deleting it will leave an empty ingredient in the recipe(s).",
|
||||
"delete-affects-recipes-more": "View all {count} recipes",
|
||||
"merge-dialog-text": "Combining the selected foods will merge the source food and target food into a single food. The source food will be deleted and all of the references to the source food will be updated to point to the target food.",
|
||||
"merge-food-example": "Merging {food1} into {food2}",
|
||||
"seed-dialog-text": "Seed the database with foods based on your local language. This will create ~2700 common foods that can be used to organize your database. Foods are translated via a community effort.",
|
||||
|
||||
@@ -48,7 +48,6 @@ export interface AppInfo {
|
||||
oidcRedirect: boolean;
|
||||
oidcProviderName: string;
|
||||
tokenTime: number;
|
||||
allowedIframeHosts?: string[];
|
||||
}
|
||||
export interface AppStartupInfo {
|
||||
isFirstLogin: boolean;
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { sanitizeMarkdownHtml } from "./markdown";
|
||||
|
||||
describe("sanitizeMarkdownHtml", () => {
|
||||
test("returns empty string for nullish input", () => {
|
||||
expect(sanitizeMarkdownHtml(null)).toEqual("");
|
||||
expect(sanitizeMarkdownHtml(undefined)).toEqual("");
|
||||
expect(sanitizeMarkdownHtml("")).toEqual("");
|
||||
});
|
||||
|
||||
test("keeps allowed formatting tags", () => {
|
||||
const html = sanitizeMarkdownHtml("<p>Mix <strong>flour</strong> and <em>water</em></p>");
|
||||
expect(html).toContain("<strong>flour</strong>");
|
||||
expect(html).toContain("<em>water</em>");
|
||||
});
|
||||
|
||||
test("strips script tags and event handlers", () => {
|
||||
const html = sanitizeMarkdownHtml("<p onclick=\"alert(1)\">hi</p><script>alert(1)</script>");
|
||||
expect(html).not.toContain("script");
|
||||
expect(html).not.toContain("onclick");
|
||||
expect(html).not.toContain("alert");
|
||||
});
|
||||
|
||||
test("strips img onerror payloads", () => {
|
||||
const html = sanitizeMarkdownHtml("<img src=x onerror=alert(1)>");
|
||||
expect(html).not.toContain("onerror");
|
||||
});
|
||||
|
||||
// Form controls must never render in user content.
|
||||
test("strips form, input, and button elements", () => {
|
||||
const html = sanitizeMarkdownHtml("<form action=/x><input name=p><button>go</button></form>");
|
||||
expect(html).not.toContain("<form");
|
||||
expect(html).not.toContain("<input");
|
||||
expect(html).not.toContain("<button");
|
||||
});
|
||||
|
||||
test("strips iframes when no allowed hosts are configured", () => {
|
||||
const html = sanitizeMarkdownHtml("<iframe src=\"https://evil.example/x\"></iframe>", []);
|
||||
expect(html).not.toContain("<iframe");
|
||||
});
|
||||
|
||||
test("strips iframes whose src host is not allowlisted", () => {
|
||||
const html = sanitizeMarkdownHtml(
|
||||
"<iframe src=\"https://evil.example/x\"></iframe>",
|
||||
["youtube.com"],
|
||||
);
|
||||
expect(html).not.toContain("<iframe");
|
||||
});
|
||||
|
||||
test("strips non-https iframes even for an allowlisted host", () => {
|
||||
const html = sanitizeMarkdownHtml(
|
||||
"<iframe src=\"http://www.youtube.com/embed/abc\"></iframe>",
|
||||
["youtube.com"],
|
||||
);
|
||||
expect(html).not.toContain("<iframe");
|
||||
});
|
||||
|
||||
test("keeps iframes from an allowlisted host (incl. subdomains)", () => {
|
||||
const html = sanitizeMarkdownHtml(
|
||||
"<iframe src=\"https://www.youtube.com/embed/abc\"></iframe>",
|
||||
["youtube.com"],
|
||||
);
|
||||
expect(html).toContain("<iframe");
|
||||
expect(html).toContain("https://www.youtube.com/embed/abc");
|
||||
});
|
||||
|
||||
test("does not allow a lookalike host to pass the suffix check", () => {
|
||||
const html = sanitizeMarkdownHtml(
|
||||
"<iframe src=\"https://notyoutube.com/embed/abc\"></iframe>",
|
||||
["youtube.com"],
|
||||
);
|
||||
expect(html).not.toContain("<iframe");
|
||||
});
|
||||
});
|
||||
@@ -1,98 +0,0 @@
|
||||
import DOMPurify from "isomorphic-dompurify";
|
||||
|
||||
enum DOMPurifyHook {
|
||||
UponSanitizeAttribute = "uponSanitizeAttribute",
|
||||
AfterSanitizeAttributes = "afterSanitizeAttributes",
|
||||
}
|
||||
|
||||
const ALLOWED_STYLE_PROPERTIES = [
|
||||
"background-color", "color", "font-style", "font-weight", "text-decoration", "text-align",
|
||||
];
|
||||
|
||||
const BASE_ALLOWED_TAGS = [
|
||||
"strong", "em", "b", "i", "u", "p", "code", "pre", "samp", "kbd", "var", "sub", "sup", "dfn", "cite",
|
||||
"small", "address", "hr", "br", "id", "div", "span", "h1", "h2", "h3", "h4", "h5", "h6",
|
||||
"ul", "ol", "li", "dl", "dt", "dd", "abbr", "a", "img", "blockquote",
|
||||
"del", "ins", "table", "thead", "tbody", "tfoot", "tr", "th", "td", "colgroup",
|
||||
];
|
||||
|
||||
const BASE_ALLOWED_ATTR = [
|
||||
"href", "src", "alt", "height", "width", "class", "title",
|
||||
"cite", "datetime", "name", "abbr", "target", "border", "start", "style",
|
||||
];
|
||||
|
||||
// Attributes only meaningful on an <iframe>; added to the allowlist solely when iframe embeds
|
||||
// are enabled via a configured host allowlist.
|
||||
const IFRAME_ALLOWED_ATTR = ["allow", "allowfullscreen", "frameborder", "scrolling"];
|
||||
|
||||
/**
|
||||
* Returns true if an iframe `src` points at one of the allowed hosts. Only https URLs are
|
||||
* accepted, and a configured host matches the URL's hostname exactly or as a parent domain
|
||||
* (e.g. "youtube.com" matches "www.youtube.com").
|
||||
*/
|
||||
function isAllowedIframeSrc(src: string, allowedHosts: string[]): boolean {
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(src);
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (url.protocol !== "https:") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hostname = url.hostname.toLowerCase();
|
||||
return allowedHosts.some((host) => {
|
||||
const allowed = host.toLowerCase();
|
||||
return hostname === allowed || hostname.endsWith(`.${allowed}`);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes pre-rendered HTML (from markdown) for display in user content such as recipe
|
||||
* instructions, notes, and descriptions.
|
||||
*
|
||||
* Only the tags in `BASE_ALLOWED_TAGS` and attributes in `BASE_ALLOWED_ATTR` survive; everything
|
||||
* else (scripts, event handlers, form controls, ...) is dropped. `style` attributes are filtered
|
||||
* down to the properties in `ALLOWED_STYLE_PROPERTIES`. `<iframe>` is only kept when
|
||||
* `allowedIframeHosts` is non-empty, and even then any iframe whose `src` is not an https URL on
|
||||
* the host allowlist is removed.
|
||||
*/
|
||||
export function sanitizeMarkdownHtml(rawHtml: string | null | undefined, allowedIframeHosts: string[] = []): string {
|
||||
if (!rawHtml) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const allowIframe = allowedIframeHosts.length > 0;
|
||||
|
||||
DOMPurify.addHook(DOMPurifyHook.UponSanitizeAttribute, (_node, data) => {
|
||||
if (data.attrName === "style") {
|
||||
const styles = data.attrValue.split(";").filter((style) => {
|
||||
const [property] = style.split(":");
|
||||
return ALLOWED_STYLE_PROPERTIES.includes(property.trim().toLowerCase());
|
||||
});
|
||||
data.attrValue = styles.join(";");
|
||||
}
|
||||
});
|
||||
|
||||
if (allowIframe) {
|
||||
DOMPurify.addHook(DOMPurifyHook.AfterSanitizeAttributes, (node) => {
|
||||
if (node.nodeName === "IFRAME" && !isAllowedIframeSrc(node.getAttribute("src") || "", allowedIframeHosts)) {
|
||||
node.parentNode?.removeChild(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const sanitized = DOMPurify.sanitize(rawHtml, {
|
||||
ALLOWED_TAGS: allowIframe ? [...BASE_ALLOWED_TAGS, "iframe"] : BASE_ALLOWED_TAGS,
|
||||
ALLOWED_ATTR: allowIframe ? [...BASE_ALLOWED_ATTR, ...IFRAME_ALLOWED_ATTR] : BASE_ALLOWED_ATTR,
|
||||
});
|
||||
|
||||
Object.values(DOMPurifyHook).forEach((hook) => {
|
||||
DOMPurify.removeHook(hook);
|
||||
});
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { truncateText } from "./text";
|
||||
|
||||
describe("truncateText", () => {
|
||||
test("returns short text unchanged", () => {
|
||||
expect(truncateText("Dinner")).toEqual("Dinner");
|
||||
});
|
||||
|
||||
test("truncates long text with clamp", () => {
|
||||
expect(truncateText("a".repeat(25))).toEqual(`${"a".repeat(20)}...`);
|
||||
});
|
||||
|
||||
test("respects custom length and clamp", () => {
|
||||
expect(truncateText("abcdef", 3, "~")).toEqual("abc~");
|
||||
});
|
||||
|
||||
test("does not clamp text exactly at the length boundary", () => {
|
||||
expect(truncateText("abcde", 5)).toEqual("abcde");
|
||||
expect(truncateText("abcdef", 5)).toEqual("abcde...");
|
||||
});
|
||||
|
||||
// Markup in the input must be treated as plain text and never parsed into the live document.
|
||||
test("does not parse or execute HTML payloads", () => {
|
||||
const createElement = vi.spyOn(document, "createElement");
|
||||
const payload = "<img src=x onerror=alert(1)>";
|
||||
|
||||
const result = truncateText(payload);
|
||||
|
||||
// The payload is returned verbatim (truncated only by length), proving it is treated as text.
|
||||
expect(result).toEqual(`${payload.slice(0, 20)}...`);
|
||||
// No DOM element is constructed, so no <img> can fire its onerror handler.
|
||||
expect(createElement).not.toHaveBeenCalled();
|
||||
createElement.mockRestore();
|
||||
});
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* Truncates plain text to `length` characters, appending `clamp` when truncated.
|
||||
*
|
||||
* The input is treated strictly as text and is never parsed as HTML, so markup in the input is
|
||||
* returned verbatim rather than interpreted.
|
||||
*/
|
||||
export function truncateText(text: string, length = 20, clamp = "..."): string {
|
||||
return text.length > length ? text.slice(0, length) + clamp : text;
|
||||
}
|
||||
@@ -69,7 +69,11 @@
|
||||
</template>
|
||||
</v-autocomplete>
|
||||
|
||||
<v-alert v-if="foods && foods.length > 0" type="error" class="mb-0 text-body-2">
|
||||
<v-alert
|
||||
v-if="foods && foods.length > 0"
|
||||
type="error"
|
||||
class="mb-0 text-body-2"
|
||||
>
|
||||
{{ $t("data-pages.foods.seed-dialog-warning") }}
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
@@ -108,7 +112,11 @@
|
||||
:label="$t('data-pages.foods.food-label')"
|
||||
/>
|
||||
<v-card variant="outlined">
|
||||
<v-virtual-scroll height="400" item-height="25" :items="bulkAssignTarget">
|
||||
<v-virtual-scroll
|
||||
height="400"
|
||||
item-height="25"
|
||||
:items="bulkAssignTarget"
|
||||
>
|
||||
<template #default="{ item }">
|
||||
<v-list-item class="pb-2">
|
||||
<v-list-item-title>{{ item.name }}</v-list-item-title>
|
||||
@@ -133,7 +141,6 @@
|
||||
]"
|
||||
:create-form="createForm"
|
||||
:edit-form="editForm"
|
||||
:on-delete-dialog-open="onDeleteDialogOpen"
|
||||
@create-one="handleCreate"
|
||||
@edit-one="handleEdit"
|
||||
@delete-one="foodStore.actions.deleteOne"
|
||||
@@ -144,12 +151,15 @@
|
||||
<template #icon>
|
||||
{{ $globals.icons.externalLink }}
|
||||
</template>
|
||||
{{ $t("data-pages.combine") }}
|
||||
{{ $t('data-pages.combine') }}
|
||||
</BaseButton>
|
||||
</template>
|
||||
|
||||
<template #[`item.label`]="{ item }">
|
||||
<MultiPurposeLabel v-if="item.label" :label="item.label">
|
||||
<MultiPurposeLabel
|
||||
v-if="item.label"
|
||||
:label="item.label"
|
||||
>
|
||||
{{ item.label.name }}
|
||||
</MultiPurposeLabel>
|
||||
</template>
|
||||
@@ -161,7 +171,7 @@
|
||||
</template>
|
||||
|
||||
<template #[`item.createdAt`]="{ item }">
|
||||
{{ item.createdAt ? $d(new Date(item.createdAt)) : "" }}
|
||||
{{ item.createdAt ? $d(new Date(item.createdAt)) : '' }}
|
||||
</template>
|
||||
|
||||
<template #table-button-bottom>
|
||||
@@ -169,33 +179,18 @@
|
||||
<template #icon>
|
||||
{{ $globals.icons.database }}
|
||||
</template>
|
||||
{{ $t("data-pages.seed") }}
|
||||
{{ $t('data-pages.seed') }}
|
||||
</BaseButton>
|
||||
</template>
|
||||
|
||||
<template #edit-dialog-custom-action>
|
||||
<BaseButton edit @click="aliasManagerDialog = true">
|
||||
{{ $t("data-pages.manage-aliases") }}
|
||||
<BaseButton
|
||||
edit
|
||||
@click="aliasManagerDialog = true"
|
||||
>
|
||||
{{ $t('data-pages.manage-aliases') }}
|
||||
</BaseButton>
|
||||
</template>
|
||||
|
||||
<template #delete-dialog-bottom>
|
||||
<v-alert v-if="affectedRecipes.length > 0" type="warning" density="compact" class="mt-4 mb-0">
|
||||
{{ $t("data-pages.foods.delete-affects-recipes", { count: affectedRecipesTotal }) }}
|
||||
<ul class="mt-1 pl-5 mb-0">
|
||||
<li v-for="recipe in affectedRecipes.slice(0, 5)" :key="recipe.slug">
|
||||
<NuxtLink :to="recipe.url" class="text-white">{{ recipe.name }}</NuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
<NuxtLink
|
||||
v-if="affectedRecipesTotal > 5"
|
||||
:to="affectedRecipesMoreLink"
|
||||
class="text-white d-inline-block mt-1"
|
||||
>
|
||||
{{ $t("data-pages.foods.delete-affects-recipes-more", { count: affectedRecipesTotal }) }}
|
||||
</NuxtLink>
|
||||
</v-alert>
|
||||
</template>
|
||||
</GroupDataPage>
|
||||
</div>
|
||||
</template>
|
||||
@@ -223,7 +218,6 @@ interface CreateIngredientFoodWithOnHand extends CreateIngredientFood {
|
||||
interface IngredientFoodWithOnHand extends IngredientFood {
|
||||
onHand: boolean;
|
||||
}
|
||||
|
||||
const userApi = useUserApi();
|
||||
const i18n = useI18n();
|
||||
const auth = useMealieAuth();
|
||||
@@ -280,14 +274,11 @@ const tableHeaders: TableHeaders[] = [
|
||||
];
|
||||
|
||||
const userHousehold = computed(() => auth.user.value?.householdSlug || "");
|
||||
const userGroup = computed(() => auth.user.value?.groupSlug || "");
|
||||
const foodStore = useFoodStore();
|
||||
const foods = computed(() =>
|
||||
foodStore.store.value.map((food) => {
|
||||
const onHand = food.householdsWithIngredientFood?.includes(userHousehold.value) || false;
|
||||
return { ...food, onHand } as IngredientFoodWithOnHand;
|
||||
}),
|
||||
);
|
||||
const foods = computed(() => foodStore.store.value.map((food) => {
|
||||
const onHand = food.householdsWithIngredientFood?.includes(userHousehold.value) || false;
|
||||
return { ...food, onHand } as IngredientFoodWithOnHand;
|
||||
}));
|
||||
|
||||
// ============================================================
|
||||
// Labels
|
||||
@@ -392,9 +383,6 @@ async function handleBulkAction(event: string, items: IngredientFoodWithOnHand[]
|
||||
if (event === "delete-selected") {
|
||||
const ids = items.map(item => item.id);
|
||||
await foodStore.actions.deleteMany(ids);
|
||||
affectedRecipes.value = [];
|
||||
affectedRecipesTotal.value = 0;
|
||||
affectedRecipesMoreLink.value = "";
|
||||
}
|
||||
else if (event === "assign-selected") {
|
||||
bulkAssignEventHandler(items);
|
||||
@@ -413,26 +401,6 @@ function updateFoodAlias(newAliases: IngredientFoodAlias[]) {
|
||||
aliasManagerDialog.value = false;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Delete Foods
|
||||
|
||||
// fetch affected recipes before confirming deletion
|
||||
const affectedRecipes = ref<{ name: string; slug: string; url: string }[]>([]);
|
||||
const affectedRecipesTotal = ref(0);
|
||||
const affectedRecipesMoreLink = ref("");
|
||||
|
||||
async function onDeleteDialogOpen(items: IngredientFoodWithOnHand[]) {
|
||||
const ids = items.map(item => item.id);
|
||||
const { data } = await userApi.recipes.search({ foods: ids, perPage: 5 });
|
||||
affectedRecipes.value = (data?.items ?? []).map(r => ({
|
||||
name: r.name ?? "",
|
||||
slug: r.slug ?? "",
|
||||
url: `/g/${userGroup.value}/r/${r.slug}`,
|
||||
}));
|
||||
affectedRecipesTotal.value = data?.total ?? 0;
|
||||
affectedRecipesMoreLink.value = `/g/${userGroup.value}?${ids.map(id => `foods=${id}`).join("&")}`;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Merge Foods
|
||||
|
||||
|
||||
@@ -337,8 +337,16 @@ const _content: Record<string, MigrationContent> = {
|
||||
},
|
||||
[MIGRATIONS.plantoeat]: {
|
||||
text: i18n.t("migration.plantoeat.description-long"),
|
||||
acceptedFileType: ".zip,.csv,.txt",
|
||||
tree: false,
|
||||
acceptedFileType: ".zip",
|
||||
tree: [
|
||||
{
|
||||
icon: $globals.icons.zip,
|
||||
title: "plantoeat-recipes-508318_10-13-2023.zip",
|
||||
children: [
|
||||
{ title: "plantoeat-recipes-508318_10-13-2023.csv", icon: $globals.icons.codeJson },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
[MIGRATIONS.recipekeeper]: {
|
||||
text: i18n.t("migration.recipekeeper.description-long"),
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { config } from "@vue/test-utils";
|
||||
import { createI18n } from "vue-i18n";
|
||||
|
||||
function loadEnLocales() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
return require("../lang/messages/en-US.json") as Record<string, string>;
|
||||
}
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: "en-US",
|
||||
messages: {
|
||||
"en-US": loadEnLocales(),
|
||||
},
|
||||
});
|
||||
|
||||
config.global.plugins = [...(config.global.plugins ?? []), i18n];
|
||||
|
||||
export { i18n };
|
||||
@@ -1,4 +1,3 @@
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { createI18n } from "vue-i18n";
|
||||
|
||||
function loadEnLocales() {
|
||||
@@ -15,12 +14,3 @@ export function stubI18n() {
|
||||
});
|
||||
return i18n.global;
|
||||
}
|
||||
|
||||
export const makeWrapper = <T>(setup: () => T) => {
|
||||
const Wrapper = {
|
||||
template: "<div />",
|
||||
setup,
|
||||
};
|
||||
const { vm } = mount(Wrapper);
|
||||
return vm as unknown as ReturnType<typeof Wrapper.setup>;
|
||||
};
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
"lint:log": "yarn lint:js --debug",
|
||||
"test": "vitest",
|
||||
"test:ci": "vitest --watch=false",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"test:ui": "vitest --ui",
|
||||
"cleanup": "nuxt cleanup"
|
||||
},
|
||||
"lint-staged": {
|
||||
@@ -50,9 +48,6 @@
|
||||
"@types/node": "^25.5.2",
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"@vitejs/plugin-vue": "^6.0.7",
|
||||
"@vitest/coverage-v8": "3.2.4",
|
||||
"@vitest/ui": "3.2.4",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"eslint": "^9.22.0",
|
||||
"eslint-config-prettier": "^10.0.2",
|
||||
"eslint-plugin-format": "^1.0.1",
|
||||
@@ -62,9 +57,8 @@
|
||||
"prettier": "^3.5.2",
|
||||
"sass-embedded": "^1.85.1",
|
||||
"typescript": "^5.3",
|
||||
"unplugin-auto-import": "^21.0.0",
|
||||
"vite-plugin-commonjs": "^0.10.4",
|
||||
"vitest": "^4.0.0"
|
||||
"vitest": "^3.0.7"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
|
||||
"resolutions": {
|
||||
|
||||
@@ -1,26 +1,11 @@
|
||||
import path from "path";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import AutoImport from "unplugin-auto-import/vite";
|
||||
|
||||
export default {
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
imports: ["vue", "@vueuse/core", "vue-i18n"],
|
||||
dts: false,
|
||||
}),
|
||||
],
|
||||
plugins: [vue()],
|
||||
test: {
|
||||
globals: true,
|
||||
environment: "jsdom",
|
||||
setupFiles: ["./app/tests/setup.ts"],
|
||||
coverage: {
|
||||
provider: "v8",
|
||||
include: ["app/{lib,components,composables,layouts,pages}/**/*.{ts,tsx,vue}"],
|
||||
exclude: ["**/*.test.*", "node_modules/**", "dist/**", "coverage/**", "**/__tests__/**"],
|
||||
reporter: ["html", "text-summary"],
|
||||
all: true,
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
|
||||
@@ -2,14 +2,6 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@ampproject/remapping@^2.3.0":
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
|
||||
integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@antfu/install-pkg@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@antfu/install-pkg/-/install-pkg-1.1.0.tgz#78fa036be1a6081b5a77a5cf59f50c7752b6ba26"
|
||||
@@ -289,7 +281,7 @@
|
||||
"@babel/template" "^7.29.7"
|
||||
"@babel/types" "^7.29.7"
|
||||
|
||||
"@babel/parser@^7.24.6", "@babel/parser@^7.25.3", "@babel/parser@^7.25.4", "@babel/parser@^7.27.0", "@babel/parser@^7.28.4", "@babel/parser@^7.28.5", "@babel/parser@^7.29.3", "@babel/parser@^7.29.7":
|
||||
"@babel/parser@^7.24.6", "@babel/parser@^7.25.3", "@babel/parser@^7.27.0", "@babel/parser@^7.28.4", "@babel/parser@^7.28.5", "@babel/parser@^7.29.3", "@babel/parser@^7.29.7":
|
||||
version "7.29.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.7.tgz#837b87387cbf5ec5530cb634b3c622f68edb9334"
|
||||
integrity sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==
|
||||
@@ -912,7 +904,7 @@
|
||||
"@babel/types" "^7.29.7"
|
||||
debug "^4.3.1"
|
||||
|
||||
"@babel/types@^7.25.4", "@babel/types@^7.26.8", "@babel/types@^7.28.4", "@babel/types@^7.29.0", "@babel/types@^7.29.7", "@babel/types@^7.4.4":
|
||||
"@babel/types@^7.26.8", "@babel/types@^7.28.4", "@babel/types@^7.29.0", "@babel/types@^7.29.7", "@babel/types@^7.4.4":
|
||||
version "7.29.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.7.tgz#8005e31d82712ee7adaef6e23c63b71a62770a92"
|
||||
integrity sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==
|
||||
@@ -928,11 +920,6 @@
|
||||
"@babel/helper-string-parser" "^8.0.0-rc.6"
|
||||
"@babel/helper-validator-identifier" "^8.0.0-rc.6"
|
||||
|
||||
"@bcoe/v8-coverage@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz#bbe12dca5b4ef983a0d0af4b07b9bc90ea0ababa"
|
||||
integrity sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==
|
||||
|
||||
"@bomb.sh/tab@^0.0.15":
|
||||
version "0.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@bomb.sh/tab/-/tab-0.0.15.tgz#678fd24b3b3ab7e9e426cf541b3ee2cf8fb661ec"
|
||||
@@ -1865,11 +1852,6 @@
|
||||
dependencies:
|
||||
minipass "^7.0.4"
|
||||
|
||||
"@istanbuljs/schema@^0.1.2":
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.6.tgz#8dc9afa2ac1506cb1a58f89940f1c124446c8df3"
|
||||
integrity sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5":
|
||||
version "0.3.13"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
|
||||
@@ -1904,7 +1886,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
|
||||
integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28", "@jridgewell/trace-mapping@^0.3.31":
|
||||
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28", "@jridgewell/trace-mapping@^0.3.31":
|
||||
version "0.3.31"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0"
|
||||
integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
|
||||
@@ -2395,11 +2377,6 @@
|
||||
vue-i18n "^10.0.7"
|
||||
vue-router "^4.5.1"
|
||||
|
||||
"@one-ini/wasm@0.1.1":
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@one-ini/wasm/-/wasm-0.1.1.tgz#6013659736c9dbfccc96e8a9c2b3de317df39323"
|
||||
integrity sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==
|
||||
|
||||
"@oxc-minify/binding-android-arm-eabi@0.131.0":
|
||||
version "0.131.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-minify/binding-android-arm-eabi/-/binding-android-arm-eabi-0.131.0.tgz#031cab3588c2b31d124699d9de3a71d4022e9ee1"
|
||||
@@ -2687,11 +2664,6 @@
|
||||
dependencies:
|
||||
"@oxc-project/types" "^0.60.0"
|
||||
|
||||
"@oxc-project/types@=0.133.0":
|
||||
version "0.133.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.133.0.tgz#2e282ef9e1d26e06b68ccd14b73f310a3b2cf7f8"
|
||||
integrity sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==
|
||||
|
||||
"@oxc-project/types@^0.131.0":
|
||||
version "0.131.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.131.0.tgz#be912efd221a600fdc0f7e127ef1248b9b7beb0a"
|
||||
@@ -3050,86 +3022,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@replit/codemirror-indentation-markers/-/codemirror-indentation-markers-6.5.3.tgz#1cfe5c557c45dd7f2988cbee278f17607010591b"
|
||||
integrity sha512-hL5Sfvw3C1vgg7GolLe/uxX5T3tmgOA3ZzqlMv47zjU1ON51pzNWiVbS22oh6crYhtVhv8b3gdXwoYp++2ilHw==
|
||||
|
||||
"@rolldown/binding-android-arm64@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz#54ce8f8382213f4a314a0c2f7ba83f81ffeae592"
|
||||
integrity sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==
|
||||
|
||||
"@rolldown/binding-darwin-arm64@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz#388fca1566c14c00c4b446fc3928630e7f0d95fc"
|
||||
integrity sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==
|
||||
|
||||
"@rolldown/binding-darwin-x64@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz#53f57de1f599ecf1db13823cfc88c18fb80954ad"
|
||||
integrity sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==
|
||||
|
||||
"@rolldown/binding-freebsd-x64@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz#6f3fdda1b7aeaac9d268a526804b4fb96e4e35f1"
|
||||
integrity sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==
|
||||
|
||||
"@rolldown/binding-linux-arm-gnueabihf@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz#d87a454bf585cc9676849377e91d6e375297326f"
|
||||
integrity sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==
|
||||
|
||||
"@rolldown/binding-linux-arm64-gnu@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz#419fd6bf612cf348f10528cbcd94ebab9607d8d1"
|
||||
integrity sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==
|
||||
|
||||
"@rolldown/binding-linux-arm64-musl@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz#fcc6918696bb76844877e1e4930a18fd0d374069"
|
||||
integrity sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==
|
||||
|
||||
"@rolldown/binding-linux-ppc64-gnu@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz#32aecb7c8dae5d4f2a8cde57a058ec86991542f8"
|
||||
integrity sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==
|
||||
|
||||
"@rolldown/binding-linux-s390x-gnu@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz#bed9346ea81e6bb8b93cf11f5d88b77db890b763"
|
||||
integrity sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==
|
||||
|
||||
"@rolldown/binding-linux-x64-gnu@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz#64c2d26f75dffd9b5a1f97557a00ae77250c8cb7"
|
||||
integrity sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==
|
||||
|
||||
"@rolldown/binding-linux-x64-musl@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz#5a45132e8a47659eeaaf3b540c2954a97c860ff3"
|
||||
integrity sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==
|
||||
|
||||
"@rolldown/binding-openharmony-arm64@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz#290513068c55e849dc8457a32afee1d7b0acb309"
|
||||
integrity sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==
|
||||
|
||||
"@rolldown/binding-wasm32-wasi@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz#3d9972dbf1a953d3c7afaa4a0f20ef2b2e39f31b"
|
||||
integrity sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==
|
||||
dependencies:
|
||||
"@emnapi/core" "1.10.0"
|
||||
"@emnapi/runtime" "1.10.0"
|
||||
"@napi-rs/wasm-runtime" "^1.1.4"
|
||||
|
||||
"@rolldown/binding-win32-arm64-msvc@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz#a004ab607a16d6f03bcb555728ff888af75773ad"
|
||||
integrity sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==
|
||||
|
||||
"@rolldown/binding-win32-x64-msvc@1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz#e2a25b34691a1cc8a1209d7de709063026dd0cdb"
|
||||
integrity sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==
|
||||
|
||||
"@rolldown/pluginutils@^1.0.0", "@rolldown/pluginutils@^1.0.0-rc.2", "@rolldown/pluginutils@^1.0.1":
|
||||
"@rolldown/pluginutils@^1.0.0-rc.2", "@rolldown/pluginutils@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz#e3fcee093fbb5ce765e1ad088ff4de2889f6f9be"
|
||||
integrity sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==
|
||||
@@ -3384,11 +3277,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz#03ecc29279e3c0c832f6185a5bfa3497858ac8ca"
|
||||
integrity sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw==
|
||||
|
||||
"@standard-schema/spec@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.1.0.tgz#a79b55dbaf8604812f52d140b2c9ab41bc150bb8"
|
||||
integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==
|
||||
|
||||
"@stylistic/eslint-plugin@^5.4.0", "@stylistic/eslint-plugin@^5.9.0":
|
||||
version "5.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz#471bbd9f7a27ceaac4a217e7f5b3890855e5640c"
|
||||
@@ -3759,95 +3647,57 @@
|
||||
dependencies:
|
||||
"@rolldown/pluginutils" "^1.0.1"
|
||||
|
||||
"@vitest/coverage-v8@3.2.4":
|
||||
"@vitest/expect@3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz#a2d8d040288c1956a1c7d0a0e2cdcfc7a3319f13"
|
||||
integrity sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==
|
||||
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.2.4.tgz#8362124cd811a5ee11c5768207b9df53d34f2433"
|
||||
integrity sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==
|
||||
dependencies:
|
||||
"@ampproject/remapping" "^2.3.0"
|
||||
"@bcoe/v8-coverage" "^1.0.2"
|
||||
ast-v8-to-istanbul "^0.3.3"
|
||||
debug "^4.4.1"
|
||||
istanbul-lib-coverage "^3.2.2"
|
||||
istanbul-lib-report "^3.0.1"
|
||||
istanbul-lib-source-maps "^5.0.6"
|
||||
istanbul-reports "^3.1.7"
|
||||
magic-string "^0.30.17"
|
||||
magicast "^0.3.5"
|
||||
std-env "^3.9.0"
|
||||
test-exclude "^7.0.1"
|
||||
"@types/chai" "^5.2.2"
|
||||
"@vitest/spy" "3.2.4"
|
||||
"@vitest/utils" "3.2.4"
|
||||
chai "^5.2.0"
|
||||
tinyrainbow "^2.0.0"
|
||||
|
||||
"@vitest/expect@4.1.8":
|
||||
version "4.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-4.1.8.tgz#45154f1f8559f55c5281eb0dcb1ac37b581a87d8"
|
||||
integrity sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==
|
||||
"@vitest/mocker@3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-3.2.4.tgz#4471c4efbd62db0d4fa203e65cc6b058a85cabd3"
|
||||
integrity sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==
|
||||
dependencies:
|
||||
"@standard-schema/spec" "^1.1.0"
|
||||
"@types/chai" "^5.2.2"
|
||||
"@vitest/spy" "4.1.8"
|
||||
"@vitest/utils" "4.1.8"
|
||||
chai "^6.2.2"
|
||||
tinyrainbow "^3.1.0"
|
||||
|
||||
"@vitest/mocker@4.1.8":
|
||||
version "4.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-4.1.8.tgz#d006bfc5894a1af51e74deddef2535d6bd436b16"
|
||||
integrity sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==
|
||||
dependencies:
|
||||
"@vitest/spy" "4.1.8"
|
||||
"@vitest/spy" "3.2.4"
|
||||
estree-walker "^3.0.3"
|
||||
magic-string "^0.30.21"
|
||||
magic-string "^0.30.17"
|
||||
|
||||
"@vitest/pretty-format@3.2.4":
|
||||
"@vitest/pretty-format@3.2.4", "@vitest/pretty-format@^3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-3.2.4.tgz#3c102f79e82b204a26c7a5921bf47d534919d3b4"
|
||||
integrity sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==
|
||||
dependencies:
|
||||
tinyrainbow "^2.0.0"
|
||||
|
||||
"@vitest/pretty-format@4.1.8":
|
||||
version "4.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-4.1.8.tgz#d9d2e248b900d7ad9556c4374fcdf1871c615193"
|
||||
integrity sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==
|
||||
dependencies:
|
||||
tinyrainbow "^3.1.0"
|
||||
|
||||
"@vitest/runner@4.1.8":
|
||||
version "4.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-4.1.8.tgz#4631808f3996359b74ccc3ca262990e14c295d50"
|
||||
integrity sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==
|
||||
dependencies:
|
||||
"@vitest/utils" "4.1.8"
|
||||
pathe "^2.0.3"
|
||||
|
||||
"@vitest/snapshot@4.1.8":
|
||||
version "4.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-4.1.8.tgz#37470135d64ea11bb2a839b1c6b7f5de7018f6ee"
|
||||
integrity sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==
|
||||
dependencies:
|
||||
"@vitest/pretty-format" "4.1.8"
|
||||
"@vitest/utils" "4.1.8"
|
||||
magic-string "^0.30.21"
|
||||
pathe "^2.0.3"
|
||||
|
||||
"@vitest/spy@4.1.8":
|
||||
version "4.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-4.1.8.tgz#3abfe9301d25c39f808dcaa9f10fec0dd370e564"
|
||||
integrity sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==
|
||||
|
||||
"@vitest/ui@3.2.4":
|
||||
"@vitest/runner@3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/ui/-/ui-3.2.4.tgz#df8080537c1dcfeae353b2d3cb3301d9acafe04a"
|
||||
integrity sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==
|
||||
resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-3.2.4.tgz#5ce0274f24a971f6500f6fc166d53d8382430766"
|
||||
integrity sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==
|
||||
dependencies:
|
||||
"@vitest/utils" "3.2.4"
|
||||
fflate "^0.8.2"
|
||||
flatted "^3.3.3"
|
||||
pathe "^2.0.3"
|
||||
sirv "^3.0.1"
|
||||
tinyglobby "^0.2.14"
|
||||
tinyrainbow "^2.0.0"
|
||||
strip-literal "^3.0.0"
|
||||
|
||||
"@vitest/snapshot@3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-3.2.4.tgz#40a8bc0346ac0aee923c0eefc2dc005d90bc987c"
|
||||
integrity sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==
|
||||
dependencies:
|
||||
"@vitest/pretty-format" "3.2.4"
|
||||
magic-string "^0.30.17"
|
||||
pathe "^2.0.3"
|
||||
|
||||
"@vitest/spy@3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-3.2.4.tgz#cc18f26f40f3f028da6620046881f4e4518c2599"
|
||||
integrity sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==
|
||||
dependencies:
|
||||
tinyspy "^4.0.3"
|
||||
|
||||
"@vitest/utils@3.2.4":
|
||||
version "3.2.4"
|
||||
@@ -3858,15 +3708,6 @@
|
||||
loupe "^3.1.4"
|
||||
tinyrainbow "^2.0.0"
|
||||
|
||||
"@vitest/utils@4.1.8":
|
||||
version "4.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-4.1.8.tgz#099ea5255cec08735410cf707edaba2c158c5ad9"
|
||||
integrity sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==
|
||||
dependencies:
|
||||
"@vitest/pretty-format" "4.1.8"
|
||||
convert-source-map "^2.0.0"
|
||||
tinyrainbow "^3.1.0"
|
||||
|
||||
"@vue-macros/common@^1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@vue-macros/common/-/common-1.16.1.tgz#dac7ebc57ded4d6fb19d7f9a83d2973971d9fa65"
|
||||
@@ -4036,14 +3877,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.35.tgz#192eb3d720c40715db79313454c4937432a4e86d"
|
||||
integrity sha512-zSbjL7gRXwks2ZQLRGCajBtBXEOXW9Ddhn/HvSdrGkE2dqGnumzW8XtusRrxrE9LvqtiqDXQ+A60Hp6mvdYxfA==
|
||||
|
||||
"@vue/test-utils@^2.4.6":
|
||||
version "2.4.10"
|
||||
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.4.10.tgz#f3b006e03918e66b5df1f2a6f7f5200663b525d3"
|
||||
integrity sha512-SmoZ5EA1kYiAFs9NkYdiFFQF+cSnUwnvlYEbY+DogWQZUiqOm/Y29eSbc5T6yi75SgSF9863SBeXniIEoPajCA==
|
||||
dependencies:
|
||||
js-beautify "^1.14.9"
|
||||
vue-component-type-helpers "^3.0.0"
|
||||
|
||||
"@vuetify/loader-shared@^2.1.2":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@vuetify/loader-shared/-/loader-shared-2.1.2.tgz#faf27cb8c40ba5a45b930b9a2785e35525e9c96f"
|
||||
@@ -4078,11 +3911,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-14.3.0.tgz#a3e7e6391f9ed7f363cbb28c32c4a278efaacbd0"
|
||||
integrity sha512-bZpge9eSXwa4ToSiqJ7j6KRwhAsneMFoSz3LMWKQDkqimm3D/tbFlrklrs/IOqC8tEcYmXQZJ6N0UrjhBirVCg==
|
||||
|
||||
abbrev@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf"
|
||||
integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==
|
||||
|
||||
abbrev@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-3.0.1.tgz#8ac8b3b5024d31464fe2a5feeea9f4536bf44025"
|
||||
@@ -4267,15 +4095,6 @@ ast-kit@^2.1.2, ast-kit@^2.1.3:
|
||||
"@babel/parser" "^7.28.5"
|
||||
pathe "^2.0.3"
|
||||
|
||||
ast-v8-to-istanbul@^0.3.3:
|
||||
version "0.3.12"
|
||||
resolved "https://registry.yarnpkg.com/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.12.tgz#8eb1b7c86ef8499859be761b17ffd91406c0c36f"
|
||||
integrity sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "^0.3.31"
|
||||
estree-walker "^3.0.3"
|
||||
js-tokens "^10.0.0"
|
||||
|
||||
ast-walker-scope@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/ast-walker-scope/-/ast-walker-scope-0.6.2.tgz#b827e8949c129802f76fe0f142e95fd7efda57dc"
|
||||
@@ -4634,10 +4453,16 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001782, caniuse-lite@^1.0.30001787:
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz#238887ddf5fcfc8c36d872394d0a78a517312a72"
|
||||
integrity sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==
|
||||
|
||||
chai@^6.2.2:
|
||||
version "6.2.2"
|
||||
resolved "https://registry.yarnpkg.com/chai/-/chai-6.2.2.tgz#ae41b52c9aca87734505362717f3255facda360e"
|
||||
integrity sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==
|
||||
chai@^5.2.0:
|
||||
version "5.3.3"
|
||||
resolved "https://registry.yarnpkg.com/chai/-/chai-5.3.3.tgz#dd3da955e270916a4bd3f625f4b919996ada7e06"
|
||||
integrity sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==
|
||||
dependencies:
|
||||
assertion-error "^2.0.1"
|
||||
check-error "^2.1.1"
|
||||
deep-eql "^5.0.1"
|
||||
loupe "^3.1.0"
|
||||
pathval "^2.0.0"
|
||||
|
||||
chalk@^4.0.0:
|
||||
version "4.1.2"
|
||||
@@ -4657,6 +4482,11 @@ change-case@^5.4.4:
|
||||
resolved "https://registry.yarnpkg.com/change-case/-/change-case-5.4.4.tgz#0d52b507d8fb8f204343432381d1a6d7bff97a02"
|
||||
integrity sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==
|
||||
|
||||
check-error@^2.1.1:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.3.tgz#2427361117b70cca8dc89680ead32b157019caf5"
|
||||
integrity sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==
|
||||
|
||||
chokidar@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30"
|
||||
@@ -4778,11 +4608,6 @@ combined-stream@^1.0.8:
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
commander@^10.0.0:
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
|
||||
integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
|
||||
|
||||
commander@^11.1.0:
|
||||
version "11.1.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906"
|
||||
@@ -4849,14 +4674,6 @@ confbox@^0.2.4:
|
||||
resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.2.4.tgz#592e7be71f882a4a874e3c88f0ac1ef6f7da1ce5"
|
||||
integrity sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==
|
||||
|
||||
config-chain@^1.1.13:
|
||||
version "1.1.13"
|
||||
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4"
|
||||
integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==
|
||||
dependencies:
|
||||
ini "^1.3.4"
|
||||
proto-list "~1.2.1"
|
||||
|
||||
consola@^3.2.3, consola@^3.4.2:
|
||||
version "3.4.2"
|
||||
resolved "https://registry.yarnpkg.com/consola/-/consola-3.4.2.tgz#5af110145397bb67afdab77013fdc34cae590ea7"
|
||||
@@ -5109,6 +4926,11 @@ decimal.js@^10.6.0:
|
||||
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a"
|
||||
integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==
|
||||
|
||||
deep-eql@^5.0.1:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341"
|
||||
integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==
|
||||
|
||||
deep-is@^0.1.3:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
|
||||
@@ -5278,16 +5100,6 @@ easy-bem@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/easy-bem/-/easy-bem-1.1.1.tgz#1bfcc10425498090bcfddc0f9c000aba91399e03"
|
||||
integrity sha512-GJRqdiy2h+EXy6a8E6R+ubmqUM08BK0FWNq41k24fup6045biQ8NXxoXimiwegMQvFFV3t1emADdGNL1TlS61A==
|
||||
|
||||
editorconfig@^1.0.4:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-1.0.7.tgz#8d6e178aeb507c206d65e1804c1d7510d110d434"
|
||||
integrity sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==
|
||||
dependencies:
|
||||
"@one-ini/wasm" "0.1.1"
|
||||
commander "^10.0.0"
|
||||
minimatch "^9.0.1"
|
||||
semver "^7.5.3"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
@@ -5425,7 +5237,7 @@ es-errors@^1.3.0:
|
||||
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
|
||||
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
||||
|
||||
es-module-lexer@^1.5.4:
|
||||
es-module-lexer@^1.5.4, es-module-lexer@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a"
|
||||
integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==
|
||||
@@ -5984,7 +5796,7 @@ execa@^8.0.1:
|
||||
signal-exit "^4.1.0"
|
||||
strip-final-newline "^3.0.0"
|
||||
|
||||
expect-type@^1.3.0:
|
||||
expect-type@^1.2.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.3.0.tgz#0d58ed361877a31bbc4dd6cf71bbfef7faf6bd68"
|
||||
integrity sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==
|
||||
@@ -6071,11 +5883,6 @@ fdir@^6.2.0, fdir@^6.5.0:
|
||||
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
|
||||
integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
|
||||
|
||||
fflate@^0.8.2:
|
||||
version "0.8.3"
|
||||
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.3.tgz#bc27d8eb30343d4d512abb03480202ce65d825fc"
|
||||
integrity sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==
|
||||
|
||||
file-entry-cache@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f"
|
||||
@@ -6131,7 +5938,7 @@ flat-cache@^4.0.0:
|
||||
flatted "^3.2.9"
|
||||
keyv "^4.5.4"
|
||||
|
||||
flatted@^3.2.9, flatted@^3.3.3:
|
||||
flatted@^3.2.9:
|
||||
version "3.4.2"
|
||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726"
|
||||
integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==
|
||||
@@ -6347,7 +6154,7 @@ glob-parent@^6.0.2:
|
||||
dependencies:
|
||||
is-glob "^4.0.3"
|
||||
|
||||
glob@^10.0.0, glob@^10.4.1, glob@^10.4.2:
|
||||
glob@^10.0.0:
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c"
|
||||
integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==
|
||||
@@ -6526,11 +6333,6 @@ html-entities@^2.6.0:
|
||||
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.6.0.tgz#7c64f1ea3b36818ccae3d3fb48b6974208e984f8"
|
||||
integrity sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==
|
||||
|
||||
html-escaper@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
|
||||
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
|
||||
|
||||
http-errors@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b"
|
||||
@@ -6659,11 +6461,6 @@ ini@4.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.1.tgz#d95b3d843b1e906e56d6747d5447904ff50ce7a1"
|
||||
integrity sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==
|
||||
|
||||
ini@^1.3.4:
|
||||
version "1.3.8"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
|
||||
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
|
||||
|
||||
internal-slot@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961"
|
||||
@@ -7007,37 +6804,6 @@ isomorphic-dompurify@^3.4.0:
|
||||
dompurify "^3.4.5"
|
||||
jsdom "^29.1.1"
|
||||
|
||||
istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756"
|
||||
integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==
|
||||
|
||||
istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d"
|
||||
integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==
|
||||
dependencies:
|
||||
istanbul-lib-coverage "^3.0.0"
|
||||
make-dir "^4.0.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
istanbul-lib-source-maps@^5.0.6:
|
||||
version "5.0.6"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441"
|
||||
integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "^0.3.23"
|
||||
debug "^4.1.1"
|
||||
istanbul-lib-coverage "^3.0.0"
|
||||
|
||||
istanbul-reports@^3.1.7:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93"
|
||||
integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==
|
||||
dependencies:
|
||||
html-escaper "^2.0.0"
|
||||
istanbul-lib-report "^3.0.0"
|
||||
|
||||
jackspeak@^3.1.2:
|
||||
version "3.4.3"
|
||||
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a"
|
||||
@@ -7073,27 +6839,6 @@ jmespath@^0.16.0:
|
||||
resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076"
|
||||
integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==
|
||||
|
||||
js-beautify@^1.14.9:
|
||||
version "1.15.4"
|
||||
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.15.4.tgz#f579f977ed4c930cef73af8f98f3f0a608acd51e"
|
||||
integrity sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==
|
||||
dependencies:
|
||||
config-chain "^1.1.13"
|
||||
editorconfig "^1.0.4"
|
||||
glob "^10.4.2"
|
||||
js-cookie "^3.0.5"
|
||||
nopt "^7.2.1"
|
||||
|
||||
js-cookie@^3.0.5:
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.8.tgz#444e6f4b27a5d844594fef61c9d6bca5f0787688"
|
||||
integrity sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==
|
||||
|
||||
js-tokens@^10.0.0:
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-10.0.0.tgz#dffe7599b4a8bb7fe30aff8d0235234dffb79831"
|
||||
integrity sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@@ -7287,80 +7032,6 @@ levn@^0.4.1:
|
||||
prelude-ls "^1.2.1"
|
||||
type-check "~0.4.0"
|
||||
|
||||
lightningcss-android-arm64@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz#f033885116dfefd9c6f54787523e3514b61e1968"
|
||||
integrity sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==
|
||||
|
||||
lightningcss-darwin-arm64@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz#50b71871b01c8199584b649e292547faea7af9b5"
|
||||
integrity sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==
|
||||
|
||||
lightningcss-darwin-x64@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz#35f3e97332d130b9ca181e11b568ded6aebc6d5e"
|
||||
integrity sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==
|
||||
|
||||
lightningcss-freebsd-x64@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz#9777a76472b64ed6ff94342ad64c7bafd794a575"
|
||||
integrity sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz#13ae652e1ab73b9135d7b7da172f666c410ad53d"
|
||||
integrity sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz#417858795a94592f680123a1b1f9da8a0e1ef335"
|
||||
integrity sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==
|
||||
|
||||
lightningcss-linux-arm64-musl@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz#6be36692e810b718040802fd809623cffe732133"
|
||||
integrity sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==
|
||||
|
||||
lightningcss-linux-x64-gnu@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz#0b7803af4eb21cfd38dd39fe2abbb53c7dd091f6"
|
||||
integrity sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==
|
||||
|
||||
lightningcss-linux-x64-musl@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz#88dc8ba865ddddb1ac5ef04b0f161804418c163b"
|
||||
integrity sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz#4f30ba3fa5e925f5b79f945e8cc0d176c3b1ab38"
|
||||
integrity sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==
|
||||
|
||||
lightningcss-win32-x64-msvc@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz#141aa5605645064928902bb4af045fa7d9f4220a"
|
||||
integrity sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==
|
||||
|
||||
lightningcss@^1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.32.0.tgz#b85aae96486dcb1bf49a7c8571221273f4f1e4a9"
|
||||
integrity sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==
|
||||
dependencies:
|
||||
detect-libc "^2.0.3"
|
||||
optionalDependencies:
|
||||
lightningcss-android-arm64 "1.32.0"
|
||||
lightningcss-darwin-arm64 "1.32.0"
|
||||
lightningcss-darwin-x64 "1.32.0"
|
||||
lightningcss-freebsd-x64 "1.32.0"
|
||||
lightningcss-linux-arm-gnueabihf "1.32.0"
|
||||
lightningcss-linux-arm64-gnu "1.32.0"
|
||||
lightningcss-linux-arm64-musl "1.32.0"
|
||||
lightningcss-linux-x64-gnu "1.32.0"
|
||||
lightningcss-linux-x64-musl "1.32.0"
|
||||
lightningcss-win32-arm64-msvc "1.32.0"
|
||||
lightningcss-win32-x64-msvc "1.32.0"
|
||||
|
||||
lilconfig@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4"
|
||||
@@ -7497,7 +7168,7 @@ log-update@^6.1.0:
|
||||
strip-ansi "^7.1.0"
|
||||
wrap-ansi "^9.0.0"
|
||||
|
||||
loupe@^3.1.4:
|
||||
loupe@^3.1.0, loupe@^3.1.4:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.2.1.tgz#0095cf56dc5b7a9a7c08ff5b1a8796ec8ad17e76"
|
||||
integrity sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==
|
||||
@@ -7563,15 +7234,6 @@ magic-string@^0.30.11, magic-string@^0.30.12, magic-string@^0.30.17, magic-strin
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec" "^1.5.5"
|
||||
|
||||
magicast@^0.3.5:
|
||||
version "0.3.5"
|
||||
resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.5.tgz#8301c3c7d66704a0771eb1bad74274f0ec036739"
|
||||
integrity sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.25.4"
|
||||
"@babel/types" "^7.25.4"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
magicast@^0.5.2:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.5.3.tgz#1800f6e76dd8b0dbe7257438a2c336aefabbd905"
|
||||
@@ -7581,13 +7243,6 @@ magicast@^0.5.2:
|
||||
"@babel/types" "^7.29.0"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
make-dir@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e"
|
||||
integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==
|
||||
dependencies:
|
||||
semver "^7.5.3"
|
||||
|
||||
marked@^15.0.12:
|
||||
version "15.0.12"
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-15.0.12.tgz#30722c7346e12d0a2d0207ab9b0c4f0102d86c4e"
|
||||
@@ -7691,7 +7346,7 @@ minimatch@^5.0.1, minimatch@^5.1.0:
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
minimatch@^9.0.1, minimatch@^9.0.4:
|
||||
minimatch@^9.0.4:
|
||||
version "9.0.9"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e"
|
||||
integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==
|
||||
@@ -7883,13 +7538,6 @@ node-releases@^2.0.36:
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.46.tgz#d188a129a83f5e03a101aacb58f260f2ee8faaa1"
|
||||
integrity sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==
|
||||
|
||||
nopt@^7.2.1:
|
||||
version "7.2.1"
|
||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7"
|
||||
integrity sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==
|
||||
dependencies:
|
||||
abbrev "^2.0.0"
|
||||
|
||||
nopt@^8.0.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-8.1.0.tgz#b11d38caf0f8643ce885818518064127f602eae3"
|
||||
@@ -8370,6 +8018,11 @@ pathe@^2.0.1, pathe@^2.0.2, pathe@^2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716"
|
||||
integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==
|
||||
|
||||
pathval@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.1.tgz#8855c5a2899af072d6ac05d11e46045ad0dc605d"
|
||||
integrity sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==
|
||||
|
||||
perfect-debounce@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz#9c2e8bc30b169cc984a58b7d5b28049839591d2a"
|
||||
@@ -8715,11 +8368,6 @@ proper-lockfile@^4.1.2:
|
||||
retry "^0.12.0"
|
||||
signal-exit "^3.0.2"
|
||||
|
||||
proto-list@~1.2.1:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
|
||||
integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==
|
||||
|
||||
proxy-from-env@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba"
|
||||
@@ -8956,30 +8604,6 @@ rfdc@^1.4.1:
|
||||
resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca"
|
||||
integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==
|
||||
|
||||
rolldown@1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.0.3.tgz#db88a3008fb0e28230a00423727ce75ba32121ac"
|
||||
integrity sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==
|
||||
dependencies:
|
||||
"@oxc-project/types" "=0.133.0"
|
||||
"@rolldown/pluginutils" "^1.0.0"
|
||||
optionalDependencies:
|
||||
"@rolldown/binding-android-arm64" "1.0.3"
|
||||
"@rolldown/binding-darwin-arm64" "1.0.3"
|
||||
"@rolldown/binding-darwin-x64" "1.0.3"
|
||||
"@rolldown/binding-freebsd-x64" "1.0.3"
|
||||
"@rolldown/binding-linux-arm-gnueabihf" "1.0.3"
|
||||
"@rolldown/binding-linux-arm64-gnu" "1.0.3"
|
||||
"@rolldown/binding-linux-arm64-musl" "1.0.3"
|
||||
"@rolldown/binding-linux-ppc64-gnu" "1.0.3"
|
||||
"@rolldown/binding-linux-s390x-gnu" "1.0.3"
|
||||
"@rolldown/binding-linux-x64-gnu" "1.0.3"
|
||||
"@rolldown/binding-linux-x64-musl" "1.0.3"
|
||||
"@rolldown/binding-openharmony-arm64" "1.0.3"
|
||||
"@rolldown/binding-wasm32-wasi" "1.0.3"
|
||||
"@rolldown/binding-win32-arm64-msvc" "1.0.3"
|
||||
"@rolldown/binding-win32-x64-msvc" "1.0.3"
|
||||
|
||||
rollup-plugin-visualizer@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-7.0.1.tgz#291c10ff4a956d9b2483f8b4147b2bf0aacd3a6e"
|
||||
@@ -9462,7 +9086,7 @@ smob@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/smob/-/smob-1.6.2.tgz#190b94c25530c631a7ccc63de0d4c0087222d21d"
|
||||
integrity sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==
|
||||
|
||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.0, source-map-js@^1.2.1:
|
||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||
@@ -9545,7 +9169,7 @@ std-env@^3.9.0:
|
||||
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.10.0.tgz#d810b27e3a073047b2b5e40034881f5ea6f9c83b"
|
||||
integrity sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==
|
||||
|
||||
std-env@^4.0.0, std-env@^4.0.0-rc.1, std-env@^4.1.0:
|
||||
std-env@^4.0.0, std-env@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/std-env/-/std-env-4.1.0.tgz#45899abc590d86d682e87f0acd1033a75084cd3f"
|
||||
integrity sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==
|
||||
@@ -9723,7 +9347,7 @@ strip-json-comments@^3.1.1:
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
strip-literal@^3.1.0:
|
||||
strip-literal@^3.0.0, strip-literal@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-3.1.0.tgz#222b243dd2d49c0bcd0de8906adbd84177196032"
|
||||
integrity sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==
|
||||
@@ -9889,15 +9513,6 @@ terser@^5.17.4:
|
||||
commander "^2.20.0"
|
||||
source-map-support "~0.5.20"
|
||||
|
||||
test-exclude@^7.0.1:
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-7.0.2.tgz#482392077630bc57d5630c13abe908bb910dfc65"
|
||||
integrity sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==
|
||||
dependencies:
|
||||
"@istanbuljs/schema" "^0.1.2"
|
||||
glob "^10.4.1"
|
||||
minimatch "^10.2.2"
|
||||
|
||||
text-decoder@^1.1.0:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.7.tgz#5d073a9a74b9c0a9d28dfadcab96b604af57d8ba"
|
||||
@@ -9925,16 +9540,16 @@ tinyclip@^0.1.12:
|
||||
resolved "https://registry.yarnpkg.com/tinyclip/-/tinyclip-0.1.13.tgz#aafa818c5378f65fd375b9d3981eadd6c784b919"
|
||||
integrity sha512-8OqlXQ35euK9+e7L68u8UwcODxkHoIkjbGsgXuARKNyQ5G6xt8nw1YPeMbxMLgCPFkToU+UEK5j05t2t8edKpQ==
|
||||
|
||||
tinyexec@^0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2"
|
||||
integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==
|
||||
|
||||
tinyexec@^1.0.1, tinyexec@^1.1.1, tinyexec@^1.1.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-1.2.2.tgz#b66edf362dcad61174bc9ce4f3ee279e2448a721"
|
||||
integrity sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==
|
||||
|
||||
tinyexec@^1.0.2:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-1.2.4.tgz#ae45bb2edebda94c70f4ea897e0f1243e470db71"
|
||||
integrity sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==
|
||||
|
||||
tinyglobby@^0.2.10, tinyglobby@^0.2.13, tinyglobby@^0.2.14, tinyglobby@^0.2.15, tinyglobby@^0.2.16:
|
||||
version "0.2.16"
|
||||
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6"
|
||||
@@ -9943,28 +9558,25 @@ tinyglobby@^0.2.10, tinyglobby@^0.2.13, tinyglobby@^0.2.14, tinyglobby@^0.2.15,
|
||||
fdir "^6.5.0"
|
||||
picomatch "^4.0.4"
|
||||
|
||||
tinyglobby@^0.2.17:
|
||||
version "0.2.17"
|
||||
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.17.tgz#562a9a6c9eb2b3b123d39719f9af5bb44fcd7631"
|
||||
integrity sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==
|
||||
dependencies:
|
||||
fdir "^6.5.0"
|
||||
picomatch "^4.0.4"
|
||||
|
||||
tinypool@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-2.1.0.tgz#303a671d6ef68d03c9512cdc9a47c86b8a85f20c"
|
||||
integrity sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==
|
||||
|
||||
tinypool@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.1.1.tgz#059f2d042bd37567fbc017d3d426bdd2a2612591"
|
||||
integrity sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==
|
||||
|
||||
tinyrainbow@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz#9509b2162436315e80e3eee0fcce4474d2444294"
|
||||
integrity sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==
|
||||
|
||||
tinyrainbow@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-3.1.0.tgz#1d8a623893f95cf0a2ddb9e5d11150e191409421"
|
||||
integrity sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==
|
||||
tinyspy@^4.0.3:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-4.0.4.tgz#d77a002fb53a88aa1429b419c1c92492e0c81f78"
|
||||
integrity sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==
|
||||
|
||||
tldts-core@^7.4.0:
|
||||
version "7.4.0"
|
||||
@@ -10309,18 +9921,6 @@ universalify@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
|
||||
integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
|
||||
|
||||
unplugin-auto-import@^21.0.0:
|
||||
version "21.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unplugin-auto-import/-/unplugin-auto-import-21.0.0.tgz#d1ac3bf95f80fb4182ec8f3d65d3e3aad38a63ac"
|
||||
integrity sha512-vWuC8SwqJmxZFYwPojhOhOXDb5xFhNNcEVb9K/RFkyk/3VnfaOjzitWN7v+8DEKpMjSsY2AEGXNgt6I0yQrhRQ==
|
||||
dependencies:
|
||||
local-pkg "^1.1.2"
|
||||
magic-string "^0.30.21"
|
||||
picomatch "^4.0.3"
|
||||
unimport "^5.6.0"
|
||||
unplugin "^2.3.11"
|
||||
unplugin-utils "^0.3.1"
|
||||
|
||||
unplugin-utils@^0.2.3:
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/unplugin-utils/-/unplugin-utils-0.2.5.tgz#d2fe44566ffffd7f216579bbb01184f6702e379b"
|
||||
@@ -10561,6 +10161,17 @@ vite-hot-client@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/vite-hot-client/-/vite-hot-client-2.2.0.tgz#284fa1afa63cc8781d079515884eb49d2890ac2f"
|
||||
integrity sha512-76Zs9zrHbH7M7wqeyooGQKdX+yg0pQ0xuQ1PbFp4z5a0Lzn2e5IPFoCswnmqZ4GiwqB4Jo3WcDAMO9jARTJl8w==
|
||||
|
||||
vite-node@3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.2.4.tgz#f3676d94c4af1e76898c162c92728bca65f7bb07"
|
||||
integrity sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==
|
||||
dependencies:
|
||||
cac "^6.7.14"
|
||||
debug "^4.4.1"
|
||||
es-module-lexer "^1.7.0"
|
||||
pathe "^2.0.3"
|
||||
vite "^5.0.0 || ^6.0.0 || ^7.0.0-0"
|
||||
|
||||
vite-node@^5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-5.3.0.tgz#fb9f73c2d5bd1f95b3beaad6455e04ad899c6336"
|
||||
@@ -10652,20 +10263,7 @@ vite-plugin-vuetify@^2.1.3:
|
||||
debug "^4.3.3"
|
||||
upath "^2.0.1"
|
||||
|
||||
"vite@^6.0.0 || ^7.0.0 || ^8.0.0":
|
||||
version "8.0.16"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-8.0.16.tgz#ae073866c06563d6634a90169a496e11bd84f1a6"
|
||||
integrity sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==
|
||||
dependencies:
|
||||
lightningcss "^1.32.0"
|
||||
picomatch "^4.0.4"
|
||||
postcss "^8.5.15"
|
||||
rolldown "1.0.3"
|
||||
tinyglobby "^0.2.17"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.3"
|
||||
|
||||
vite@^7.3.1, vite@^7.3.3:
|
||||
"vite@^5.0.0 || ^6.0.0 || ^7.0.0-0", vite@^7.3.1, vite@^7.3.3:
|
||||
version "7.3.3"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-7.3.3.tgz#d7e07a52b5873fb86f902a3f4b3d17410337450f"
|
||||
integrity sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==
|
||||
@@ -10679,30 +10277,33 @@ vite@^7.3.1, vite@^7.3.3:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.3"
|
||||
|
||||
vitest@^4.0.0:
|
||||
version "4.1.8"
|
||||
resolved "https://registry.yarnpkg.com/vitest/-/vitest-4.1.8.tgz#9fed17277bf7350497e54338898a7afd46dfd509"
|
||||
integrity sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==
|
||||
vitest@^3.0.7:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.2.4.tgz#0637b903ad79d1539a25bc34c0ed54b5c67702ea"
|
||||
integrity sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==
|
||||
dependencies:
|
||||
"@vitest/expect" "4.1.8"
|
||||
"@vitest/mocker" "4.1.8"
|
||||
"@vitest/pretty-format" "4.1.8"
|
||||
"@vitest/runner" "4.1.8"
|
||||
"@vitest/snapshot" "4.1.8"
|
||||
"@vitest/spy" "4.1.8"
|
||||
"@vitest/utils" "4.1.8"
|
||||
es-module-lexer "^2.0.0"
|
||||
expect-type "^1.3.0"
|
||||
magic-string "^0.30.21"
|
||||
obug "^2.1.1"
|
||||
"@types/chai" "^5.2.2"
|
||||
"@vitest/expect" "3.2.4"
|
||||
"@vitest/mocker" "3.2.4"
|
||||
"@vitest/pretty-format" "^3.2.4"
|
||||
"@vitest/runner" "3.2.4"
|
||||
"@vitest/snapshot" "3.2.4"
|
||||
"@vitest/spy" "3.2.4"
|
||||
"@vitest/utils" "3.2.4"
|
||||
chai "^5.2.0"
|
||||
debug "^4.4.1"
|
||||
expect-type "^1.2.1"
|
||||
magic-string "^0.30.17"
|
||||
pathe "^2.0.3"
|
||||
picomatch "^4.0.3"
|
||||
std-env "^4.0.0-rc.1"
|
||||
picomatch "^4.0.2"
|
||||
std-env "^3.9.0"
|
||||
tinybench "^2.9.0"
|
||||
tinyexec "^1.0.2"
|
||||
tinyglobby "^0.2.15"
|
||||
tinyrainbow "^3.1.0"
|
||||
vite "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
tinyexec "^0.3.2"
|
||||
tinyglobby "^0.2.14"
|
||||
tinypool "^1.1.1"
|
||||
tinyrainbow "^2.0.0"
|
||||
vite "^5.0.0 || ^6.0.0 || ^7.0.0-0"
|
||||
vite-node "3.2.4"
|
||||
why-is-node-running "^2.3.0"
|
||||
|
||||
vscode-uri@^3.1.0:
|
||||
@@ -10726,11 +10327,6 @@ vue-bundle-renderer@^2.2.0:
|
||||
dependencies:
|
||||
ufo "^1.6.1"
|
||||
|
||||
vue-component-type-helpers@^3.0.0:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/vue-component-type-helpers/-/vue-component-type-helpers-3.3.3.tgz#9f31a7610348de6fa151c0166600f7ed5808beb3"
|
||||
integrity sha512-x4nsFpy5Pe8fqPzp/5vkTPeTTDBpAx4WVtV47Ejt0+2FQrq4pRRsJs7JmYRqMFzTu/LW+pCWEjQ3YVCkPV7f9g==
|
||||
|
||||
vue-demi@^0.14.10:
|
||||
version "0.14.10"
|
||||
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.10.tgz#afc78de3d6f9e11bf78c55e8510ee12814522f04"
|
||||
|
||||
@@ -179,7 +179,7 @@ def validate_file_token(token: str | None = None) -> Path:
|
||||
@contextmanager
|
||||
def get_temporary_zip_path(auto_unlink=True) -> Generator[Path, None, None]:
|
||||
app_dirs.TEMP_DIR.mkdir(exist_ok=True, parents=True)
|
||||
temp_path = app_dirs.TEMP_DIR / f"{uuid4().hex}.zip"
|
||||
temp_path = app_dirs.TEMP_DIR.joinpath("my_zip_archive.zip")
|
||||
try:
|
||||
yield temp_path
|
||||
finally:
|
||||
|
||||
@@ -33,16 +33,6 @@ class FeatureDetails(NamedTuple):
|
||||
return s
|
||||
|
||||
|
||||
DEFAULT_ALLOWED_IFRAME_HOSTS = [
|
||||
"youtube.com",
|
||||
"youtube-nocookie.com",
|
||||
"vimeo.com",
|
||||
"player.vimeo.com",
|
||||
]
|
||||
"""Secure-by-default hostnames permitted as `<iframe>` sources in user content. Limited to
|
||||
well-known video providers. Subdomains of these hosts are also allowed (e.g. `www.youtube.com`)."""
|
||||
|
||||
|
||||
MaskedNoneString = Annotated[
|
||||
str | None,
|
||||
PlainSerializer(lambda x: None if x is None else "*****", return_type=str | None),
|
||||
@@ -160,19 +150,6 @@ class AppSettings(AppLoggingSettings):
|
||||
ALLOW_SIGNUP: bool = False
|
||||
ALLOW_PASSWORD_LOGIN: bool = True
|
||||
|
||||
ALLOWED_IFRAME_HOSTS: str = ""
|
||||
"""Comma-separated list of additional hostnames allowed as `<iframe>` sources in user content
|
||||
(recipe instructions, notes, descriptions). Extends `DEFAULT_ALLOWED_IFRAME_HOSTS`. Subdomains of
|
||||
a listed host are also allowed. Adding hosts is opt-in to riskier behavior; the defaults are
|
||||
limited to well-known video providers."""
|
||||
|
||||
@property
|
||||
def allowed_iframe_hosts(self) -> list[str]:
|
||||
"""The full set of hostnames permitted as `<iframe>` sources, secure defaults plus any
|
||||
admin-configured additions via `ALLOWED_IFRAME_HOSTS`."""
|
||||
extra = [host.strip().lower() for host in self.ALLOWED_IFRAME_HOSTS.split(",") if host.strip()]
|
||||
return list(dict.fromkeys(DEFAULT_ALLOWED_IFRAME_HOSTS + extra))
|
||||
|
||||
DAILY_SCHEDULE_TIME: str = "23:45"
|
||||
"""Local server time, in HH:MM format. See `DAILY_SCHEDULE_TIME_UTC` for the parsed UTC equivalent"""
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ def get_app_info(session: Session = Depends(generate_session)):
|
||||
oidc_provider_name=settings.OIDC_PROVIDER_NAME,
|
||||
allow_password_login=settings.ALLOW_PASSWORD_LOGIN,
|
||||
token_time=settings.TOKEN_TIME,
|
||||
allowed_iframe_hosts=settings.allowed_iframe_hosts,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -58,13 +58,6 @@ async def get_recipe_asset(recipe_id: UUID4, file_name: str):
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if file.exists():
|
||||
# Force download and disable MIME sniffing so uploaded assets cannot be
|
||||
# served as active content in Mealie's origin.
|
||||
return FileResponse(
|
||||
file,
|
||||
filename=file.name,
|
||||
content_disposition_type="attachment",
|
||||
headers={"X-Content-Type-Options": "nosniff"},
|
||||
)
|
||||
return FileResponse(file, filename=file.name, content_disposition_type="attachment")
|
||||
else:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||
|
||||
@@ -22,7 +22,6 @@ class AppInfo(MealieModel):
|
||||
oidc_redirect: bool
|
||||
oidc_provider_name: str
|
||||
token_time: int
|
||||
allowed_iframe_hosts: list[str] = []
|
||||
|
||||
|
||||
class AppTheme(MealieModel):
|
||||
|
||||
@@ -6,7 +6,6 @@ from pathlib import Path
|
||||
from zipfile import ZipFile
|
||||
|
||||
from mealie.core.config import get_app_settings
|
||||
from mealie.core.settings.static import APP_VERSION
|
||||
from mealie.services._base_service import BaseService
|
||||
from mealie.services.backups_v2.alchemy_exporter import AlchemyExporter
|
||||
from mealie.services.backups_v2.backup_file import BackupFile
|
||||
@@ -44,15 +43,8 @@ class BackupV2(BaseService):
|
||||
def backup(self) -> Path:
|
||||
# sourcery skip: merge-nested-ifs, reintroduce-else, remove-redundant-continue
|
||||
timestamp = datetime.datetime.now(datetime.UTC).strftime("%Y.%m.%d.%H.%M.%S")
|
||||
short_hash = self.settings.GIT_COMMIT_HASH[:7]
|
||||
|
||||
if APP_VERSION == "develop":
|
||||
backup_name = f"mealie_dev-{short_hash}_{timestamp}.zip"
|
||||
elif APP_VERSION == "nightly":
|
||||
backup_name = f"mealie_nightly-{short_hash}_{timestamp}.zip"
|
||||
else:
|
||||
backup_name = f"mealie_{APP_VERSION}_{timestamp}.zip"
|
||||
|
||||
backup_name = f"mealie_{timestamp}.zip"
|
||||
backup_file = self.directories.BACKUP_DIR / backup_name
|
||||
|
||||
database_json = self.db_exporter.dump()
|
||||
|
||||
@@ -7,7 +7,6 @@ from pathlib import Path
|
||||
from slugify import slugify
|
||||
|
||||
from mealie.pkgs.cache import cache_key
|
||||
from mealie.schema.reports.reports import ReportEntryCreate
|
||||
from mealie.services.scraper import cleaner
|
||||
|
||||
from ._migration_base import BaseMigrator
|
||||
@@ -16,23 +15,15 @@ from .utils.migration_helpers import scrape_image, split_by_comma
|
||||
|
||||
|
||||
def plantoeat_recipes(file: Path):
|
||||
"""Yields all recipes inside the export file as dict.
|
||||
"""Yields all recipes inside the export file as dict"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
with zipfile.ZipFile(file) as zip_file:
|
||||
zip_file.extractall(tmpdir)
|
||||
|
||||
Accepts a ZIP archive containing a CSV, or a raw CSV/TXT file.
|
||||
"""
|
||||
if zipfile.is_zipfile(file):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
with zipfile.ZipFile(file) as zip_file:
|
||||
zip_file.extractall(tmpdir)
|
||||
|
||||
for name in Path(tmpdir).glob("**/[!.]*.csv"):
|
||||
with open(name, newline="") as csvfile:
|
||||
reader = csv.DictReader(csvfile)
|
||||
yield from reader
|
||||
else:
|
||||
with open(file, newline="", encoding="utf-8", errors="ignore") as csvfile:
|
||||
reader = csv.DictReader(csvfile)
|
||||
yield from reader
|
||||
for name in Path(tmpdir).glob("**/[!.]*.csv"):
|
||||
with open(name, newline="") as csvfile:
|
||||
reader = csv.DictReader(csvfile)
|
||||
yield from reader
|
||||
|
||||
|
||||
def get_value_as_string_or_none(dictionary: dict, key: str):
|
||||
@@ -121,32 +112,7 @@ class PlanToEatMigrator(BaseMigrator):
|
||||
|
||||
return recipe_dict
|
||||
|
||||
def _validate_archive(self) -> bool:
|
||||
"""Returns False and appends a failure report entry if the file is not a ZIP, CSV, or TXT."""
|
||||
if zipfile.is_zipfile(self.archive):
|
||||
return True
|
||||
|
||||
try:
|
||||
with open(self.archive, encoding="utf-8", errors="strict") as f:
|
||||
f.read(512)
|
||||
return True
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
|
||||
self.report_entries.append(
|
||||
ReportEntryCreate(
|
||||
report_id=self.report_id,
|
||||
success=False,
|
||||
message="Unsupported file format. Please upload a ZIP archive, CSV file, or TXT file.",
|
||||
exception="",
|
||||
)
|
||||
)
|
||||
return False
|
||||
|
||||
def _migrate(self) -> None:
|
||||
if not self._validate_archive():
|
||||
return
|
||||
|
||||
recipe_image_urls = {}
|
||||
|
||||
recipes = []
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[build]
|
||||
publish = "docs/site/"
|
||||
command = """
|
||||
pip3 install mkdocs-material &&
|
||||
pip3 install zensical &&
|
||||
cd docs &&
|
||||
mkdocs build
|
||||
zensical build
|
||||
"""
|
||||
|
||||
[[headers]]
|
||||
|
||||
@@ -12,6 +12,8 @@ dependencies = [
|
||||
"SQLAlchemy==2.0.50",
|
||||
"aiofiles==25.1.0",
|
||||
"alembic==1.18.4",
|
||||
"aniso8601==10.0.1",
|
||||
"appdirs==1.4.4",
|
||||
"apprise==1.10.0",
|
||||
"bcrypt==5.0.0",
|
||||
"extruct==0.18.0",
|
||||
@@ -24,7 +26,7 @@ dependencies = [
|
||||
"python-dateutil==2.9.0.post0",
|
||||
"python-dotenv==1.2.2",
|
||||
"python-ldap==3.4.7",
|
||||
"python-multipart==0.0.30",
|
||||
"python-multipart==0.0.29",
|
||||
"python-slugify==8.0.4",
|
||||
"recipe-scrapers==15.11.0",
|
||||
"requests==2.34.2",
|
||||
@@ -59,17 +61,17 @@ pgsql = [
|
||||
|
||||
[dependency-groups]
|
||||
docs = [
|
||||
"mkdocs-material==9.7.6",
|
||||
"zensical==0.0.42",
|
||||
]
|
||||
dev = [
|
||||
"coverage==7.14.1",
|
||||
"coveragepy-lcov==0.1.2",
|
||||
"mkdocs-material==9.7.6",
|
||||
"zensical==0.0.42",
|
||||
"mypy==2.1.0",
|
||||
"pre-commit==4.6.0",
|
||||
"pylint==4.0.5",
|
||||
"pytest==9.0.3",
|
||||
"pytest-asyncio==1.4.0",
|
||||
"pytest-asyncio==1.3.0",
|
||||
"rich==15.0.0",
|
||||
"ruff==0.15.14",
|
||||
"types-PyYAML==6.0.12.20260518",
|
||||
@@ -176,7 +178,3 @@ max-complexity = 24 # Default is 10.
|
||||
|
||||
[tool.uv]
|
||||
add-bounds = "exact"
|
||||
# Cooling period: ignore package releases newer than 5 days to mitigate
|
||||
# supply-chain attacks (compromised releases are usually caught and yanked
|
||||
# within days). Evaluated at resolve time as a rolling window.
|
||||
exclude-newer = "5 days"
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
],
|
||||
"minimumReleaseAge": "5 days",
|
||||
"internalChecksFilter": "strict",
|
||||
"addLabels": [
|
||||
"dependencies"
|
||||
],
|
||||
@@ -53,7 +51,8 @@
|
||||
],
|
||||
"automerge": true,
|
||||
"automergeType": "pr",
|
||||
"automergeStrategy": "squash"
|
||||
"automergeStrategy": "squash",
|
||||
"minimumReleaseAge": "5 days"
|
||||
},
|
||||
{
|
||||
"description": "Auto-merge Docker digest and patch updates",
|
||||
@@ -67,7 +66,8 @@
|
||||
],
|
||||
"automerge": true,
|
||||
"automergeType": "pr",
|
||||
"automergeStrategy": "squash"
|
||||
"automergeStrategy": "squash",
|
||||
"minimumReleaseAge": "5 days"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -45,8 +45,6 @@ migrations_tandoor = CWD / "migrations/tandoor.zip"
|
||||
|
||||
migrations_plantoeat = CWD / "migrations/plantoeat.zip"
|
||||
|
||||
migrations_plantoeat_csv = CWD / "migrations/plantoeat.csv"
|
||||
|
||||
migrations_myrecipebox = CWD / "migrations/myrecipebox.csv"
|
||||
|
||||
migrations_recipekeeper = CWD / "migrations/recipekeeper.zip"
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
Title,Course,Cuisine,Main Ingredient,Description,Source,Url,Url Host,Prep Time,Cook Time,Total Time,Servings,Yield,Ingredients,Directions,Tags,Rating,Public Url,Photo Url,Private,Nutritional Score (generic),Calories,Fat,Saturated Fat,Cholesterol,Sodium,Sugar,Carbohydrate,Fiber,Protein,Cost,Created At,Updated At
|
||||
Test Recipe,Main Course,American,Beans,"This is a description.
|
||||
Here is new line.",Manually entered source,https://eatwithclarity.com/sushi-bowl-with-sesame-tofu/,,75,75,150,7,1 loaf,", Heading
|
||||
2 itm Test, note
|
||||
, Heading2
|
||||
3 pkg Two, note2
|
||||
|
||||
","Directions.
|
||||
Will go here.","Allergen-Friendly, Cheap, Test",3,https://app.plantoeat.com/recipes/38843883,https://plantoeat.s3.amazonaws.com/recipes/29516709/470292506c8d9b71582487a7879ab7b197d06490-large.jpg?1628205591,yes,,13,16,17,18,19,22,20,21,23,,2023-10-13 20:29:29,2023-10-13 20:32:48
|
||||
Test Recipe2,,,,,,,,,,,,,"2 itm Test, note
|
||||
3 pkg Two, note2
|
||||
","Directions.
|
||||
Will go here.",,,,,,,,,,,,,,,,,2023-10-13 20:29:29,2023-10-13 20:32:48
|
||||
|
@@ -94,15 +94,6 @@ test_cases = [
|
||||
"transFatContent",
|
||||
},
|
||||
),
|
||||
MigrationTestData(
|
||||
typ=SupportedMigrations.plantoeat,
|
||||
archive=test_data.migrations_plantoeat_csv,
|
||||
search_slug="test-recipe",
|
||||
nutrition_filter={
|
||||
"unsaturatedFatContent",
|
||||
"transFatContent",
|
||||
},
|
||||
),
|
||||
MigrationTestData(
|
||||
typ=SupportedMigrations.myrecipebox,
|
||||
archive=test_data.migrations_myrecipebox,
|
||||
@@ -133,7 +124,6 @@ test_ids = [
|
||||
"mealie_alpha_archive",
|
||||
"tandoor_archive",
|
||||
"plantoeat_archive",
|
||||
"plantoeat_csv",
|
||||
"myrecipebox_csv",
|
||||
"recipekeeper_archive",
|
||||
"cookn_archive",
|
||||
@@ -200,30 +190,6 @@ def test_recipe_migration(api_client: TestClient, unique_user_fn_scoped: TestUse
|
||||
# TODO: validate other types of content
|
||||
|
||||
|
||||
def test_plantoeat_rejects_invalid_file_type(api_client: TestClient, unique_user: TestUser) -> None:
|
||||
# Simulate uploading a binary file (e.g. PDF) that is neither ZIP nor CSV/TXT
|
||||
binary_content = bytes(range(256)) * 4 # arbitrary binary data that is not valid UTF-8
|
||||
payload = {"migration_type": SupportedMigrations.plantoeat.value}
|
||||
file_payload = {"archive": binary_content}
|
||||
|
||||
response = api_client.post(
|
||||
api_routes.groups_migrations,
|
||||
data=payload,
|
||||
files=file_payload,
|
||||
headers=unique_user.token,
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
report_id = response.json()["id"]
|
||||
|
||||
response = api_client.get(api_routes.groups_reports_item_id(report_id), headers=unique_user.token)
|
||||
assert response.status_code == 200
|
||||
report = response.json()
|
||||
assert report["entries"]
|
||||
assert not report["entries"][0]["success"]
|
||||
assert "ZIP" in report["entries"][0]["message"] or "CSV" in report["entries"][0]["message"]
|
||||
|
||||
|
||||
def test_bad_mealie_alpha_data_is_ignored(api_client: TestClient, unique_user: TestUser):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
with ZipFile(test_data.migrations_mealie) as zf:
|
||||
|
||||
@@ -104,33 +104,6 @@ def test_recipe_asset_dangerous_extension_blocked(
|
||||
assert response.status_code == 400, f"expected 400 for extension={ext}, got {response.status_code}"
|
||||
|
||||
|
||||
def test_recipe_asset_served_as_attachment(
|
||||
api_client: TestClient, unique_user: TestUser, recipe_ingredient_only: Recipe
|
||||
):
|
||||
"""Assets must be served as downloads with MIME sniffing disabled so uploaded files cannot
|
||||
execute as active content in Mealie's origin."""
|
||||
recipe = recipe_ingredient_only
|
||||
payload = {"name": random_string(10), "icon": "mdi-file", "extension": "txt"}
|
||||
file_payload = {"file": b"<script>alert(1)</script>"}
|
||||
|
||||
response = api_client.post(
|
||||
f"/api/recipes/{recipe.slug}/assets",
|
||||
data=payload,
|
||||
files=file_payload,
|
||||
headers=unique_user.token,
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
recipe_response = api_client.get(f"/api/recipes/{recipe.slug}", headers=unique_user.token).json()
|
||||
recipe_id = recipe_response["id"]
|
||||
file_name = recipe_response["assets"][0]["fileName"]
|
||||
|
||||
media_response = api_client.get(f"/api/media/recipes/{recipe_id}/assets/{file_name}")
|
||||
assert media_response.status_code == 200
|
||||
assert "attachment" in media_response.headers["content-disposition"].lower()
|
||||
assert media_response.headers["x-content-type-options"] == "nosniff"
|
||||
|
||||
|
||||
def test_recipe_image_upload(api_client: TestClient, unique_user: TestUser, recipe_ingredient_only: Recipe):
|
||||
data_payload = {"extension": "jpg"}
|
||||
file_payload = {"image": data.images_test_image_1.read_bytes()}
|
||||
|
||||
@@ -27,31 +27,6 @@ def test_non_default_settings(monkeypatch):
|
||||
assert app_settings.DOCS_URL is None
|
||||
|
||||
|
||||
def test_allowed_iframe_hosts_defaults(monkeypatch):
|
||||
monkeypatch.delenv("ALLOWED_IFRAME_HOSTS", raising=False)
|
||||
get_app_settings.cache_clear()
|
||||
app_settings = get_app_settings()
|
||||
|
||||
# Secure defaults are always present and never empty (empty would disable iframe embeds).
|
||||
assert "youtube.com" in app_settings.allowed_iframe_hosts
|
||||
assert "vimeo.com" in app_settings.allowed_iframe_hosts
|
||||
|
||||
|
||||
def test_allowed_iframe_hosts_extends_defaults(monkeypatch):
|
||||
monkeypatch.setenv("ALLOWED_IFRAME_HOSTS", " Example.com , trusted.tld ,, ")
|
||||
get_app_settings.cache_clear()
|
||||
app_settings = get_app_settings()
|
||||
|
||||
hosts = app_settings.allowed_iframe_hosts
|
||||
# Configured hosts are normalized, blanks dropped, and defaults retained.
|
||||
assert "example.com" in hosts
|
||||
assert "trusted.tld" in hosts
|
||||
assert "youtube.com" in hosts
|
||||
assert "" not in hosts
|
||||
# No duplicates.
|
||||
assert len(hosts) == len(set(hosts))
|
||||
|
||||
|
||||
def test_default_connection_args(monkeypatch):
|
||||
monkeypatch.setenv("DB_ENGINE", "sqlite")
|
||||
get_app_settings.cache_clear()
|
||||
|
||||
259
uv.lock
generated
259
uv.lock
generated
@@ -2,10 +2,6 @@ version = 1
|
||||
revision = 3
|
||||
requires-python = "==3.12.*"
|
||||
|
||||
[options]
|
||||
exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
|
||||
exclude-newer-span = "P5D"
|
||||
|
||||
[[package]]
|
||||
name = "aiofiles"
|
||||
version = "25.1.0"
|
||||
@@ -29,6 +25,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/29/6533c317b74f707ea28f8d633734dbda2119bbadfc61b2f3640ba835d0f7/alembic-1.18.4-py3-none-any.whl", hash = "sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a", size = 263893, upload-time = "2026-02-10T16:00:49.997Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aniso8601"
|
||||
version = "10.0.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/52179c4e3f1978d3d9a285f98c706642522750ef343e9738286130423730/aniso8601-10.0.1.tar.gz", hash = "sha256:25488f8663dd1528ae1f54f94ac1ea51ae25b4d531539b8bc707fed184d16845", size = 47190, upload-time = "2025-04-18T17:29:42.995Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/59/75/e0e10dc7ed1408c28e03a6cb2d7a407f99320eb953f229d008a7a6d05546/aniso8601-10.0.1-py2.py3-none-any.whl", hash = "sha256:eb19717fd4e0db6de1aab06f12450ab92144246b257423fe020af5748c0cb89e", size = 52848, upload-time = "2025-04-18T17:29:41.492Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "annotated-doc"
|
||||
version = "0.0.4"
|
||||
@@ -60,6 +65,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "appdirs"
|
||||
version = "1.4.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470, upload-time = "2020-05-11T07:59:51.037Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566, upload-time = "2020-05-11T07:59:49.499Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "apprise"
|
||||
version = "1.10.0"
|
||||
@@ -124,26 +138,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/95/adcb68e20c34162e9135f370d6e31737719c2b6f94bc953fe7ed1f10fe21/authlib-1.7.2-py2.py3-none-any.whl", hash = "sha256:3e1faedc9d87e7d56a164eca3ccb6ace0d61b94abe83e92242f8dc8bba9b4a9f", size = 259548, upload-time = "2026-05-06T08:10:21.436Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "babel"
|
||||
version = "2.18.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backrefs"
|
||||
version = "7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/a7dd63622beef68cc0d3c3c36d472e143dd95443d5ebf14cd1a5b4dfbf11/backrefs-7.0.tar.gz", hash = "sha256:4989bb9e1e99eb23647c7160ed51fb21d0b41b5d200f2d3017da41e023097e82", size = 7012453, upload-time = "2026-04-28T16:28:04.215Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/39/39a31d7eae729ea14ed10c3ccef79371197177b9355a86cb3525709e8502/backrefs-7.0-py310-none-any.whl", hash = "sha256:b57cd227ea556b0aed3dc9b8da4628db4eabc0402c6d7fcfc69283a93955f7e9", size = 380824, upload-time = "2026-04-28T16:27:55.647Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/b5/9302644225ba7dfa934a2ff2b9c7bb85701313a90dddb3dfaf693fa5bae2/backrefs-7.0-py311-none-any.whl", hash = "sha256:a0fa7360c63509e9e077e174ef4e6d3c21c8db94189b9d957289ae6d794b9475", size = 392626, upload-time = "2026-04-28T16:27:57.42Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/da/87912ddec6e06feffbaa3d7aa18fc6352bee2e8f1fee185d7d1690f8f4e8/backrefs-7.0-py312-none-any.whl", hash = "sha256:ca42ce6a49ace3d75684dfa9937f3373902a63284ecb385ce36d15e5dcb41c12", size = 398537, upload-time = "2026-04-28T16:27:58.913Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "5.0.0"
|
||||
@@ -383,6 +377,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/6a/c24df8a4fc22fa84070dcd94abeba43c15e08cc09e35869565c0bad196fd/curl_cffi-0.15.0-cp313-abi3-android_24_arm64_v8a.whl", hash = "sha256:4682dc38d4336e0eb0b185374db90a760efde63cbea994b4e63f3521d44c4c92", size = 7190427, upload-time = "2026-04-03T11:12:12.142Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deepmerge"
|
||||
version = "2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a8/3a/b0ba594708f1ad0bc735884b3ad854d3ca3bdc1d741e56e40bbda6263499/deepmerge-2.0.tar.gz", hash = "sha256:5c3d86081fbebd04dd5de03626a0607b809a98fb6ccba5770b62466fe940ff20", size = 19890, upload-time = "2024-08-30T05:31:50.308Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/82/e5d2c1c67d19841e9edc74954c827444ae826978499bde3dfc1d007c8c11/deepmerge-2.0-py3-none-any.whl", hash = "sha256:6de9ce507115cff0bed95ff0ce9ecc31088ef50cbdf09bc90a09349a318b3d00", size = 13475, upload-time = "2024-08-30T05:31:48.659Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dill"
|
||||
version = "0.4.1"
|
||||
@@ -490,18 +493,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/2e/b41d8a1a917d6581fc27a35d05561037b048e47df50f27f8ac9c7e27a710/freezegun-1.5.5-py3-none-any.whl", hash = "sha256:cd557f4a75cf074e84bc374249b9dd491eaeacd61376b9eb3c423282211619d2", size = 19266, upload-time = "2025-08-09T10:39:06.636Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ghp-import"
|
||||
version = "2.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "python-dateutil" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "3.5.1"
|
||||
@@ -888,6 +879,8 @@ source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiofiles" },
|
||||
{ name = "alembic" },
|
||||
{ name = "aniso8601" },
|
||||
{ name = "appdirs" },
|
||||
{ name = "apprise" },
|
||||
{ name = "authlib" },
|
||||
{ name = "bcrypt" },
|
||||
@@ -939,7 +932,6 @@ dev = [
|
||||
{ name = "coverage" },
|
||||
{ name = "coveragepy-lcov" },
|
||||
{ name = "freezegun" },
|
||||
{ name = "mkdocs-material" },
|
||||
{ name = "mypy" },
|
||||
{ name = "pre-commit" },
|
||||
{ name = "pydantic-to-typescript2" },
|
||||
@@ -953,15 +945,18 @@ dev = [
|
||||
{ name = "types-pyyaml" },
|
||||
{ name = "types-requests" },
|
||||
{ name = "types-urllib3" },
|
||||
{ name = "zensical" },
|
||||
]
|
||||
docs = [
|
||||
{ name = "mkdocs-material" },
|
||||
{ name = "zensical" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "aiofiles", specifier = "==25.1.0" },
|
||||
{ name = "alembic", specifier = "==1.18.4" },
|
||||
{ name = "aniso8601", specifier = "==10.0.1" },
|
||||
{ name = "appdirs", specifier = "==1.4.4" },
|
||||
{ name = "apprise", specifier = "==1.10.0" },
|
||||
{ name = "authlib", specifier = "==1.7.2" },
|
||||
{ name = "bcrypt", specifier = "==5.0.0" },
|
||||
@@ -990,7 +985,7 @@ requires-dist = [
|
||||
{ name = "python-dateutil", specifier = "==2.9.0.post0" },
|
||||
{ name = "python-dotenv", specifier = "==1.2.2" },
|
||||
{ name = "python-ldap", specifier = "==3.4.7" },
|
||||
{ name = "python-multipart", specifier = "==0.0.30" },
|
||||
{ name = "python-multipart", specifier = "==0.0.29" },
|
||||
{ name = "python-slugify", specifier = "==8.0.4" },
|
||||
{ name = "pyyaml", specifier = "==6.0.3" },
|
||||
{ name = "rapidfuzz", specifier = "==3.14.5" },
|
||||
@@ -1010,13 +1005,12 @@ dev = [
|
||||
{ name = "coverage", specifier = "==7.14.1" },
|
||||
{ name = "coveragepy-lcov", specifier = "==0.1.2" },
|
||||
{ name = "freezegun", specifier = "==1.5.5" },
|
||||
{ name = "mkdocs-material", specifier = "==9.7.6" },
|
||||
{ name = "mypy", specifier = "==2.1.0" },
|
||||
{ name = "pre-commit", specifier = "==4.6.0" },
|
||||
{ name = "pydantic-to-typescript2", specifier = "==1.0.6" },
|
||||
{ name = "pylint", specifier = "==4.0.5" },
|
||||
{ name = "pytest", specifier = "==9.0.3" },
|
||||
{ name = "pytest-asyncio", specifier = "==1.4.0" },
|
||||
{ name = "pytest-asyncio", specifier = "==1.3.0" },
|
||||
{ name = "rich", specifier = "==15.0.0" },
|
||||
{ name = "ruff", specifier = "==0.15.14" },
|
||||
{ name = "types-python-dateutil", specifier = "==2.9.0.20260518" },
|
||||
@@ -1024,17 +1018,9 @@ dev = [
|
||||
{ name = "types-pyyaml", specifier = "==6.0.12.20260518" },
|
||||
{ name = "types-requests", specifier = "==2.33.0.20260518" },
|
||||
{ name = "types-urllib3", specifier = "==1.26.25.14" },
|
||||
{ name = "zensical", specifier = "==0.0.42" },
|
||||
]
|
||||
docs = [{ name = "mkdocs-material", specifier = "==9.7.6" }]
|
||||
|
||||
[[package]]
|
||||
name = "mergedeep"
|
||||
version = "1.3.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" },
|
||||
]
|
||||
docs = [{ name = "zensical", specifier = "==0.0.42" }]
|
||||
|
||||
[[package]]
|
||||
name = "mf2py"
|
||||
@@ -1050,75 +1036,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/88/b1d83c9e71cbdaefcec38ea350d2bd6360a9d1e030b090ad4b0fcc421ca1/mf2py-2.0.1-py3-none-any.whl", hash = "sha256:092806e17f1a93db4aafa5e8d3c4124b5e42cd89027e2db48a5248ef4eabde03", size = 25767, upload-time = "2023-12-08T03:41:58.443Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs"
|
||||
version = "1.6.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "ghp-import" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "markdown" },
|
||||
{ name = "markupsafe" },
|
||||
{ name = "mergedeep" },
|
||||
{ name = "mkdocs-get-deps" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pathspec" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "pyyaml-env-tag" },
|
||||
{ name = "watchdog" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-get-deps"
|
||||
version = "0.2.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mergedeep" },
|
||||
{ name = "platformdirs" },
|
||||
{ name = "pyyaml" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ce/25/b3cccb187655b9393572bde9b09261d267c3bf2f2cdabe347673be5976a6/mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1", size = 11047, upload-time = "2026-03-10T02:46:33.632Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650", size = 9555, upload-time = "2026-03-10T02:46:32.256Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-material"
|
||||
version = "9.7.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "babel" },
|
||||
{ name = "backrefs" },
|
||||
{ name = "colorama" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "markdown" },
|
||||
{ name = "mkdocs" },
|
||||
{ name = "mkdocs-material-extensions" },
|
||||
{ name = "paginate" },
|
||||
{ name = "pygments" },
|
||||
{ name = "pymdown-extensions" },
|
||||
{ name = "requests" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/45/29/6d2bcf41ae40802c4beda2432396fff97b8456fb496371d1bc7aad6512ec/mkdocs_material-9.7.6.tar.gz", hash = "sha256:00bdde50574f776d328b1862fe65daeaf581ec309bd150f7bff345a098c64a69", size = 4097959, upload-time = "2026-03-19T15:41:58.161Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl", hash = "sha256:71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba", size = 9305470, upload-time = "2026-03-19T15:41:55.217Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-material-extensions"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "2.1.0"
|
||||
@@ -1254,15 +1171,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paginate"
|
||||
version = "0.5.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paho-mqtt"
|
||||
version = "1.6.1"
|
||||
@@ -1583,15 +1491,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pytest-asyncio"
|
||||
version = "1.4.0"
|
||||
version = "1.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pytest" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1640,11 +1548,11 @@ sdist = { url = "https://files.pythonhosted.org/packages/b2/f4/60edeb794bbc9ed0f
|
||||
|
||||
[[package]]
|
||||
name = "python-multipart"
|
||||
version = "0.0.30"
|
||||
version = "0.0.29"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4b/82/c8cd43a6e0719bf5a3b034f6726dd701f75829c08944c83d4b95d02ed0e8/python_multipart-0.0.30.tar.gz", hash = "sha256:0edfe0475c1f46ddd3ff7785a626f6118af32bdcf359bb21260367313bb32118", size = 46316, upload-time = "2026-05-31T19:24:55.198Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4e/fe/70bd71a6738b09a0bdf6480ca6436b167469ca4578b2a0efbe390b4b0e70/python_multipart-0.0.29.tar.gz", hash = "sha256:643e93849196645e2dbdd81a0f8829a23123ad7f797a84a364c6fb3563f18904", size = 45678, upload-time = "2026-05-17T17:29:47.654Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/fd/0318007beb234790993d3ec5afd051d1dbceb733e81e3afe2b981ece3f37/python_multipart-0.0.30-py3-none-any.whl", hash = "sha256:830964def8c90607ac5daa00514e3987815865713ade8d20febc9177ac0c3c5b", size = 29730, upload-time = "2026-05-31T19:24:53.814Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/cb/769cfc37177252872a45a71f3fbdde9d51b471a3f3c14bfe95dde3407386/python_multipart-0.0.29-py3-none-any.whl", hash = "sha256:2ddcc971cef266225f54f552d8fa10bcfbb1f14446caec199060daac59ff2d69", size = 29640, upload-time = "2026-05-17T17:29:45.69Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1677,18 +1585,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml-env-tag"
|
||||
version = "1.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pyyaml" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rapidfuzz"
|
||||
version = "3.14.5"
|
||||
@@ -1893,6 +1789,24 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154, upload-time = "2019-08-30T21:37:03.543Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomlkit"
|
||||
version = "0.15.0"
|
||||
@@ -2063,27 +1977,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/66/c3/f8b216cbd742e5b84c40f045204c764ccb7524d2aeab021054ec69446b0a/w3lib-2.4.1-py3-none-any.whl", hash = "sha256:40930132907e68de906a5b89331ab8c8ff4f01bd35b5539ef7896017d814138d", size = 21695, upload-time = "2026-03-20T09:50:26.187Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "watchdog"
|
||||
version = "6.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "watchfiles"
|
||||
version = "1.2.0"
|
||||
@@ -2144,3 +2037,33 @@ sdist = { url = "https://files.pythonhosted.org/packages/8b/34/7c6b4e3f89cb6416d
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/13/5093bcb954878e50f7217fd2ab94282b53934022e4e4a03265582da83bf5/yt_dlp-2026.3.17-py3-none-any.whl", hash = "sha256:32992db94303a8a5d211a183f2174834fe7f8c29d83ed2e7a324eae97a8f26d8", size = 3315134, upload-time = "2026-03-17T23:42:57.863Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zensical"
|
||||
version = "0.0.42"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "deepmerge" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "markdown" },
|
||||
{ name = "pygments" },
|
||||
{ name = "pymdown-extensions" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "tomli" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7a/dd/04e89ab92aed1ef9e36c76ef095fb587ffcbe4162aa7f3fe6d63aafade4a/zensical-0.0.42.tar.gz", hash = "sha256:cc346b833868a59412fe8d8498a152be90be9f3d8fb87e1f1a1c2e1146cbae1b", size = 3931093, upload-time = "2026-05-15T10:22:45.354Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/19/2ca4e52769307959f7485d4c5da7b24787339787c1cbc371885cef448e50/zensical-0.0.42-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bffd7a34b570fa3ccadf1d23babb0f7c4851c6b626e4fc8ed9f21c2eaae85968", size = 12705326, upload-time = "2026-05-15T10:22:07.905Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/82/0832b0d2c0c2800174141d5519a017105d3dace9194e2c29730e7a676adf/zensical-0.0.42-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:ee1a79789f9462ef44a4b6ebbfc8b5bf4b2447607da8bc5b35bc9c4ce4ea2370", size = 12568663, upload-time = "2026-05-15T10:22:11.072Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/87/272b3998322958ca38f09323d2347cb121dfc851477c36962b71319242a5/zensical-0.0.42-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e9a5d508ce8d1b07d8417f0623be476f6b37d445ab4356481a71e613a7979d6", size = 12948460, upload-time = "2026-05-15T10:22:13.792Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/1b/e5f153401f162f48cae2d58e96b95fd39ba5bd1728fb5881a60e502f4e1d/zensical-0.0.42-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9fbc0951a676e48afe7df3a9b2a30958dcf9c426ed2480972d3c04d6de485ba3", size = 12913460, upload-time = "2026-05-15T10:22:16.791Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/4f/5186b4204bdfdf132851b7515a37b9602bfc153fb601db5fb244339bae52/zensical-0.0.42-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f0e96e53f39b9e4b929a25d9df70bd7fa8217166a854e2c8f3185983dd01500", size = 13276704, upload-time = "2026-05-15T10:22:19.819Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/df/b57b5fcc631ac7a4b4c6834d8cf0b88d3fca37c9db42fc6bbf9f097200ed/zensical-0.0.42-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7d586e57436d603e88acd856864f99f0771aef24bf6560b2de238417bd3817c", size = 12987069, upload-time = "2026-05-15T10:22:22.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/3a/b326a44a065d98e89b472645ad33037201e3385340c2e6e35627b18ab3fa/zensical-0.0.42-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3c026f023330d67f986a94b68ffd36dc5066882e697e1125c37308d8d684135c", size = 13124195, upload-time = "2026-05-15T10:22:25.543Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/1e/823740a662e357a8826dc8eeb87e06705e64219b2774430bc555f7c53d57/zensical-0.0.42-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:e5908bc09cf5c1c50c9504241e37f89955daf3e89ba1b9d71c17972578b24804", size = 13182981, upload-time = "2026-05-15T10:22:28.89Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/6d/9fe261267ac36a7d57051d790022408e9043bc925c9ad21971a1e5b6c3e8/zensical-0.0.42-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:c0bf96b55f0a44e8716bcb334a16380ed56772b555145da775a7d8ac8678cb6f", size = 13332666, upload-time = "2026-05-15T10:22:32.249Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/57/9b0e4f131a7ad15cf1aca081748ea7336c084fb8e16be202a6bed32f595c/zensical-0.0.42-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:47cd99583738a8ab03fac4080741275c56e741a06dc8edfb541f4c1649a5ae69", size = 13270817, upload-time = "2026-05-15T10:22:35.388Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/fd/bdb85cc444e4146e8970a22e48a903bfed5bf83276ad7d755caa415dda64/zensical-0.0.42-cp310-abi3-win32.whl", hash = "sha256:83090e53fba061967ecb3dff81500b1900f288bae108bf54084a2aeb6648ebd0", size = 12256227, upload-time = "2026-05-15T10:22:38.869Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/b9/09d1f735c8e6d3eb61d176ed5ebcf658b65b126d7d4bbc03a7d366a1e17d/zensical-0.0.42-cp310-abi3-win_amd64.whl", hash = "sha256:2e4304e103f9cd5c637045bbae1ff29de3009ab01b16e99c2fd6d4bbceb7a3ee", size = 12486598, upload-time = "2026-05-15T10:22:42.158Z" },
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user