diff --git a/.claude/agents/playwright-test-generator.md b/.claude/agents/playwright-test-generator.md new file mode 100644 index 0000000..0504c92 --- /dev/null +++ b/.claude/agents/playwright-test-generator.md @@ -0,0 +1,59 @@ +--- +name: playwright-test-generator +description: 'Use this agent when you need to create automated browser tests using Playwright Examples: Context: User wants to generate a test for the test plan item. ' +tools: Glob, Grep, Read, LS, mcp__playwright-test__browser_click, mcp__playwright-test__browser_drag, mcp__playwright-test__browser_evaluate, mcp__playwright-test__browser_file_upload, mcp__playwright-test__browser_handle_dialog, mcp__playwright-test__browser_hover, mcp__playwright-test__browser_navigate, mcp__playwright-test__browser_press_key, mcp__playwright-test__browser_select_option, mcp__playwright-test__browser_snapshot, mcp__playwright-test__browser_type, mcp__playwright-test__browser_verify_element_visible, mcp__playwright-test__browser_verify_list_visible, mcp__playwright-test__browser_verify_text_visible, mcp__playwright-test__browser_verify_value, mcp__playwright-test__browser_wait_for, mcp__playwright-test__generator_read_log, mcp__playwright-test__generator_setup_page, mcp__playwright-test__generator_write_test +model: sonnet +color: blue +--- + +You are a Playwright Test Generator, an expert in browser automation and end-to-end testing. +Your specialty is creating robust, reliable Playwright tests that accurately simulate user interactions and validate +application behavior. + +# For each test you generate +- Obtain the test plan with all the steps and verification specification +- Run the `generator_setup_page` tool to set up page for the scenario +- For each step and verification in the scenario, do the following: + - Use Playwright tool to manually execute it in real-time. + - Use the step description as the intent for each Playwright tool call. +- Retrieve generator log via `generator_read_log` +- Immediately after reading the test log, invoke `generator_write_test` with the generated source code + - File should contain single test + - File name must be fs-friendly scenario name + - Test must be placed in a describe matching the top-level test plan item + - Test title must match the scenario name + - Includes a comment with the step text before each step execution. Do not duplicate comments if step requires + multiple actions. + - Always use best practices from the log when generating tests. + + + For following plan: + + ```markdown file=specs/plan.md + ### 1. Adding New Todos + **Seed:** `tests/seed.spec.ts` + + #### 1.1 Add Valid Todo + **Steps:** + 1. Click in the "What needs to be done?" input field + + #### 1.2 Add Multiple Todos + ... + ``` + + Following file is generated: + + ```ts file=add-valid-todo.spec.ts + // spec: specs/plan.md + // seed: tests/seed.spec.ts + + test.describe('Adding New Todos', () => { + test('Add Valid Todo', async { page } => { + // 1. Click in the "What needs to be done?" input field + await page.click(...); + + ... + }); + }); + ``` + \ No newline at end of file diff --git a/.claude/agents/playwright-test-healer.md b/.claude/agents/playwright-test-healer.md new file mode 100644 index 0000000..b66280f --- /dev/null +++ b/.claude/agents/playwright-test-healer.md @@ -0,0 +1,45 @@ +--- +name: playwright-test-healer +description: Use this agent when you need to debug and fix failing Playwright tests +tools: Glob, Grep, Read, LS, Edit, MultiEdit, Write, mcp__playwright-test__browser_console_messages, mcp__playwright-test__browser_evaluate, mcp__playwright-test__browser_generate_locator, mcp__playwright-test__browser_network_requests, mcp__playwright-test__browser_snapshot, mcp__playwright-test__test_debug, mcp__playwright-test__test_list, mcp__playwright-test__test_run +model: sonnet +color: red +--- + +You are the Playwright Test Healer, an expert test automation engineer specializing in debugging and +resolving Playwright test failures. Your mission is to systematically identify, diagnose, and fix +broken Playwright tests using a methodical approach. + +Your workflow: +1. **Initial Execution**: Run all tests using `test_run` tool to identify failing tests +2. **Debug failed tests**: For each failing test run `test_debug`. +3. **Error Investigation**: When the test pauses on errors, use available Playwright MCP tools to: + - Examine the error details + - Capture page snapshot to understand the context + - Analyze selectors, timing issues, or assertion failures +4. **Root Cause Analysis**: Determine the underlying cause of the failure by examining: + - Element selectors that may have changed + - Timing and synchronization issues + - Data dependencies or test environment problems + - Application changes that broke test assumptions +5. **Code Remediation**: Edit the test code to address identified issues, focusing on: + - Updating selectors to match current application state + - Fixing assertions and expected values + - Improving test reliability and maintainability + - For inherently dynamic data, utilize regular expressions to produce resilient locators +6. **Verification**: Restart the test after each fix to validate the changes +7. **Iteration**: Repeat the investigation and fixing process until the test passes cleanly + +Key principles: +- Be systematic and thorough in your debugging approach +- Document your findings and reasoning for each fix +- Prefer robust, maintainable solutions over quick hacks +- Use Playwright best practices for reliable test automation +- If multiple errors exist, fix them one at a time and retest +- Provide clear explanations of what was broken and how you fixed it +- You will continue this process until the test runs successfully without any failures or errors. +- If the error persists and you have high level of confidence that the test is correct, mark this test as test.fixme() + so that it is skipped during the execution. Add a comment before the failing step explaining what is happening instead + of the expected behavior. +- Do not ask user questions, you are not interactive tool, do the most reasonable thing possible to pass the test. +- Never wait for networkidle or use other discouraged or deprecated apis \ No newline at end of file diff --git a/.claude/agents/playwright-test-planner.md b/.claude/agents/playwright-test-planner.md new file mode 100644 index 0000000..b33d6ba --- /dev/null +++ b/.claude/agents/playwright-test-planner.md @@ -0,0 +1,52 @@ +--- +name: playwright-test-planner +description: Use this agent when you need to create comprehensive test plan for a web application or website +tools: Glob, Grep, Read, LS, mcp__playwright-test__browser_click, mcp__playwright-test__browser_close, mcp__playwright-test__browser_console_messages, mcp__playwright-test__browser_drag, mcp__playwright-test__browser_evaluate, mcp__playwright-test__browser_file_upload, mcp__playwright-test__browser_handle_dialog, mcp__playwright-test__browser_hover, mcp__playwright-test__browser_navigate, mcp__playwright-test__browser_navigate_back, mcp__playwright-test__browser_network_requests, mcp__playwright-test__browser_press_key, mcp__playwright-test__browser_run_code, mcp__playwright-test__browser_select_option, mcp__playwright-test__browser_snapshot, mcp__playwright-test__browser_take_screenshot, mcp__playwright-test__browser_type, mcp__playwright-test__browser_wait_for, mcp__playwright-test__planner_setup_page, mcp__playwright-test__planner_save_plan +model: sonnet +color: green +--- + +You are an expert web test planner with extensive experience in quality assurance, user experience testing, and test +scenario design. Your expertise includes functional testing, edge case identification, and comprehensive test coverage +planning. + +You will: + +1. **Navigate and Explore** + - Invoke the `planner_setup_page` tool once to set up page before using any other tools + - Explore the browser snapshot + - Do not take screenshots unless absolutely necessary + - Use `browser_*` tools to navigate and discover interface + - Thoroughly explore the interface, identifying all interactive elements, forms, navigation paths, and functionality + +2. **Analyze User Flows** + - Map out the primary user journeys and identify critical paths through the application + - Consider different user types and their typical behaviors + +3. **Design Comprehensive Scenarios** + + Create detailed test scenarios that cover: + - Happy path scenarios (normal user behavior) + - Edge cases and boundary conditions + - Error handling and validation + +4. **Structure Test Plans** + + Each scenario must include: + - Clear, descriptive title + - Detailed step-by-step instructions + - Expected outcomes where appropriate + - Assumptions about starting state (always assume blank/fresh state) + - Success criteria and failure conditions + +5. **Create Documentation** + + Submit your test plan using `planner_save_plan` tool. + +**Quality Standards**: +- Write steps that are specific enough for any tester to follow +- Include negative testing scenarios +- Ensure scenarios are independent and can be run in any order + +**Output Format**: Always save the complete test plan as a markdown file with clear headings, numbered steps, and +professional formatting suitable for sharing with development and QA teams. \ No newline at end of file diff --git a/.claude/skills/check/SKILL.md b/.claude/skills/check/SKILL.md new file mode 100644 index 0000000..bad232d --- /dev/null +++ b/.claude/skills/check/SKILL.md @@ -0,0 +1,11 @@ +--- +description: Run lint, type-check, and tests +user_invocable: true +--- + +Run all quality checks for this project in sequence. Stop and fix any issues before moving to the next step. + +1. `npm run lint-fix` +2. `npm run build-types` +3. `npm run check-types` +4. `npm run test` diff --git a/.github/workflows/reuse-quality.yml b/.github/workflows/reuse-quality.yml index 6290868..9b5a584 100644 --- a/.github/workflows/reuse-quality.yml +++ b/.github/workflows/reuse-quality.yml @@ -2,6 +2,7 @@ name: Quality checks on: workflow_call: + workflow_dispatch: jobs: quality: @@ -12,11 +13,26 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 22 + node-version: 24 cache: 'npm' - name: Install dependencies run: npm ci - - name: Run quality checks - run: npm run quality \ No newline at end of file + - name: Lint and type-check + run: npm run lint && npm run build-types && npm run check-types && npm -w ui run build + + - name: Init env + run: bash dev/init-env.sh + + - name: Start dev dependencies + run: docker compose up -d --wait + + - name: Start dev API + run: NODE_ENV=development node api/index.ts & + + - name: Run tests + run: npm run test + + - name: Audit + run: npm audit --omit=dev --audit-level=critical \ No newline at end of file diff --git a/.gitignore b/.gitignore index de81a3a..10855cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,10 @@ data/ node_modules/ api/config/local-* -.type/ \ No newline at end of file +.type/ +.env* +*.log +coverage/ +.DS_Store +test-results/ +dev/logs/ \ No newline at end of file diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..d9dfc12 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "playwright-test": { + "command": "npx", + "args": [ + "playwright", + "run-test-mcp-server" + ] + } + } +} \ No newline at end of file diff --git a/.zellij.kdl b/.zellij.kdl index 210cf82..034fecd 100644 --- a/.zellij.kdl +++ b/.zellij.kdl @@ -7,7 +7,7 @@ layout { } pane name="deps" { command "bash" - args "-ic" "npm run dev-deps && watch -n 4 \"docker compose ps --all --format 'table {{.Name}}\t{{.Status}}'\"" + args "-ic" "npm run dev-deps" } } pane { @@ -23,6 +23,6 @@ layout { } pane size=2 borderless=true { command "bash" - args "-ic" "echo -n -e \"Dev server available at \\e[1;96mhttp://localhost:5600\\033[0m\"" + args "-ic" "echo -n -e \"Dev server available at \\e[1;96mhttp://$DEV_HOST:$NGINX_PORT\\033[0m\"" } } diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3587143 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,71 @@ +# Development notes for events + +## Typing + +Types are mostly managed from JSON schemas (for example @api/types/subscription/schema.js), prepared using `npm run build-types` and imported from #types which is an alias for @api/types/index.ts. + +## Quality checks + +1. Linter: `npm run lint-fix` +2. Type checking: `npm run check-types` +3. Docker build passing: `docker build -t events .` + +## Dev environment + +Development processes (dev-api, dev-ui, docker compose services) are managed by the user, not by you. **Never attempt to start, stop, restart, or kill any dev process or container.** + +### Checking status + +Run `bash dev/status.sh` to see which services are up or down. This probes all known endpoints and reports their status. Always use this when: +- A test fails with a connection error +- You suspect a service might be down +- Before reporting an environment issue to the user + +### Consulting logs + +Log files are in `dev/logs/`: +- `dev/logs/dev-api.log` — API server (nodemon). Check this for startup failures or runtime errors. +- `dev/logs/dev-ui.log` — UI dev server (vite). +- `dev/logs/docker-compose.log` — All docker compose services (nginx, simple-directory, mongo). + +Use `tail -n 50 dev/logs/` to see recent output, or `grep -i error dev/logs/` to find errors. + +### When something is down + +If a service is down, do not try to fix the infrastructure. Instead: +1. Run `bash dev/status.sh` to identify what's down. +2. Check relevant logs in `dev/logs/` for errors. +3. Report the problem clearly to the user with the status output and any relevant log lines. + +Port assignments are in `.env`. Do not modify them. + +## Testing + +Run tests: `npm run test` +Run specific tests: `npm run test tests/events.api.spec.ts` + +If a test fails with a connection error, run `bash dev/status.sh` to diagnose, then stop and ask the user for help. + +Test users are defined in @dev/resources/users.json and organizations in @dev/resources/organizations.json. Modify these as little as possible, but if you do you need to force reload of the simple-directory container `docker compose restart simple-directory`. + +Tests are separated in playwright projects: unit (pure functions), api (stateful API endpoints through HTTP) and e2e (UI with playwright browser instrumentation). When working on the e2e part you can use subagents `playwright-test-generator` and `playwright-test-healer`. + +In case of failures you might find error contexts in @test-results. + +### Debugging e2e failures + +When e2e tests fail, follow this order: +1. Use the Playwright MCP tools (`browser_open`, `browser_navigate`, `browser_snapshot`, `browser_console_messages`) to inspect failing pages — do NOT write custom Playwright scripts +2. Check `test-results/` for traces and screenshots +3. Only then dig into component code + +## Code patterns + +When working on this project, read the following files on a need-to-know basis to understand conventions: + +- API route pattern: @api/src/subscriptions/router.ts +- Vue page pattern: @ui/src/pages/ +- Unit test pattern: @tests/subscriptions.unit.spec.ts +- API test pattern: @tests/subscriptions.api.spec.ts +- E2E test pattern: @tests/subscriptions.e2e.spec.ts +- Type generation from JSON schemas: @api/types/subscription/schema.js diff --git a/api/config/development.cjs b/api/config/development.cjs deleted file mode 100644 index 717b501..0000000 --- a/api/config/development.cjs +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - port: 8082, - privateDirectoryUrl: 'http://localhost:8080', - mongoUrl: 'mongodb://localhost:27017/data-fair-events-development', - observer: { - port: 9092 - }, - secretKeys: { - identities: 'SECRET_IDENTITIES', - events: 'SECRET_EVENTS', - sendMails: 'SECRET_SENDMAILS' - } -} diff --git a/api/config/development.js b/api/config/development.js new file mode 100644 index 0000000..54ecb66 --- /dev/null +++ b/api/config/development.js @@ -0,0 +1,19 @@ +import dotenv from 'dotenv' +dotenv.config({ path: import.meta.resolve('../../.env').replace('file://', '') }) + +if (!process.env.DEV_API_PORT) throw new Error('missing DEV_API_PORT env variable, use "dotenv -- npm run dev" or configure .env file') + +export default { + port: process.env.DEV_API_PORT, + privateDirectoryUrl: `http://localhost:${process.env.SD_PORT}`, + mongoUrl: `mongodb://localhost:${process.env.MONGO_PORT}/data-fair-events-development`, + observer: { + active: false, + port: 9092 + }, + secretKeys: { + identities: 'SECRET_IDENTITIES', + events: 'SECRET_EVENTS', + sendMails: 'SECRET_SENDMAILS' + } +} diff --git a/api/config/test.cjs b/api/config/test.cjs deleted file mode 100644 index e1b4bf3..0000000 --- a/api/config/test.cjs +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - port: 8082, - privateDirectoryUrl: 'http://localhost:8080', - mongoUrl: 'mongodb://localhost:27017/data-fair-events-test', - observer: { - port: 9092, - active: false - }, - secretKeys: { - identities: 'SECRET_IDENTITIES', - events: 'SECRET_EVENTS', - sendMails: 'SECRET_SENDMAILS' - } -} diff --git a/api/index.ts b/api/index.ts index f005ffa..f9051fa 100644 --- a/api/index.ts +++ b/api/index.ts @@ -5,8 +5,8 @@ start().then(() => {}, err => { process.exit(1) }) -process.on('SIGTERM', function onSigterm () { - console.info('Received SIGTERM signal, shutdown gracefully...') +const shutdown = (signal: string) => { + console.info(`Received ${signal} signal, shutdown gracefully...`) stop().then(() => { console.log('shutting down now') process.exit() @@ -14,4 +14,7 @@ process.on('SIGTERM', function onSigterm () { console.error('Failure while stopping service', err) process.exit(1) }) -}) +} + +process.on('SIGTERM', () => shutdown('SIGTERM')) +process.on('SIGINT', () => shutdown('SIGINT')) diff --git a/api/nodemon.json b/api/nodemon.json new file mode 100644 index 0000000..25de8a5 --- /dev/null +++ b/api/nodemon.json @@ -0,0 +1,7 @@ +{ + "ignoreRoot": [], + "ignore": ["node_modules/", "../dev/"], + "execMap": { + "ts": "node" + } +} diff --git a/api/package.json b/api/package.json index ad8c135..be315e2 100644 --- a/api/package.json +++ b/api/package.json @@ -3,7 +3,7 @@ "main": "index.js", "type": "module", "scripts": { - "dev": "NODE_ENV=development DEBUG=upgrade,webpush node --watch --experimental-strip-types index.ts" + "dev": "mkdir -p ../dev/logs && NODE_ENV=development DEBUG=upgrade,webpush nodemon -e js,ts,json index.ts 2>&1 | tee ../dev/logs/dev-api.log" }, "imports": { "#config": "./src/config.ts", @@ -30,7 +30,7 @@ "jwks-rsa": "3", "mongodb": "^6.21.0", "nanoid": "^5.1.7", - "node-pushnotifications": "^3.1.3", + "node-pushnotifications": "^5.0.1", "prom-client": "^15.1.3", "url-template": "^3.1.1", "useragent": "^2.3.0", @@ -39,11 +39,11 @@ }, "devDependencies": { "@types/express": "^5.0.6", - "@types/ws": "^8.18.1", "@types/fs-extra": "^11.0.4", "@types/i18n": "^0.13.12", "@types/node-pushnotifications": "^3.1.1", "@types/useragent": "^2.3.4", - "@types/web-push": "^3.6.4" + "@types/web-push": "^3.6.4", + "@types/ws": "^8.18.1" } } diff --git a/api/src/app.ts b/api/src/app.ts index 33da91a..b2b38af 100644 --- a/api/src/app.ts +++ b/api/src/app.ts @@ -13,6 +13,7 @@ import uiLogsRouter from './ui-logs/router.ts' import adminRouter from './admin/router.ts' import { uiConfig } from '#config' import { internalError } from '@data-fair/lib-node/observer.js' +import eventsMongo from './mongo.ts' const app = express() export default app @@ -33,6 +34,8 @@ app.use(helmet({ app.set('query parser', 'simple') app.use(express.json()) +app.use('/api/ping', (req, res) => res.send('ok')) + app.use(createSiteMiddleware('events')) app.use('/api/events', eventsRouter) @@ -53,6 +56,28 @@ app.use('api/v1', (req, res, next) => { app.use('/api/v1/notifications', notificationsRouter) app.use('/api/v1/subscriptions', subscriptionsRouter) +if (process.env.NODE_ENV === 'development') { + app.delete('/api/test-env', async (req, res) => { + const testFilter = /^test/ + for (const name of ['notifications', 'subscriptions']) { + await eventsMongo.db.collection(name).deleteMany({ 'recipient.id': testFilter }) + } + await eventsMongo.db.collection('events').deleteMany({ 'sender.id': testFilter }) + for (const name of ['webhooks', 'webhook-subscriptions', 'pushSubscriptions']) { + await eventsMongo.db.collection(name).deleteMany({ 'owner.id': testFilter }) + } + res.send() + }) + app.post('/api/test-env/:collection', async (req, res) => { + await eventsMongo.db.collection(req.params.collection).insertOne(req.body) + res.send() + }) + app.get('/api/test-env/:collection/:id', async (req, res) => { + const doc = await eventsMongo.db.collection(req.params.collection).findOne({ _id: req.params.id as any }) + res.json(doc) + }) +} + app.use('/api', (req, res) => res.status(404).send('unknown api endpoint')) app.use('/push-sw.js', (req, res, next) => { diff --git a/api/src/config.ts b/api/src/config.ts index 0c97c0e..976bac1 100644 --- a/api/src/config.ts +++ b/api/src/config.ts @@ -2,9 +2,7 @@ import type { ApiConfig } from '../config/type/index.ts' import { assertValid } from '../config/type/index.ts' import config from 'config' -// we reload the config instead of using the singleton from the config module for testing purposes -// @ts-ignore -const apiConfig = process.env.NODE_ENV === 'test' ? config.util.loadFileConfigs(process.env.NODE_CONFIG_DIR, { skipConfigSources: true }) : config +const apiConfig = config assertValid(apiConfig, { lang: 'en', name: 'config', internal: true }) export default apiConfig as ApiConfig diff --git a/api/src/events/operations.ts b/api/src/events/operations.ts new file mode 100644 index 0000000..500ca36 --- /dev/null +++ b/api/src/events/operations.ts @@ -0,0 +1,84 @@ +// operations.ts — pure functions only +// must not import #mongo, #config, or store state + +import type { Event, FullEvent, SearchableEvent, LocalizedEvent, Subscription } from '#types' +import type { Filter } from 'mongodb' +import type { SessionStateAuthenticated } from '@data-fair/lib-express' + +export const localizeProp = (prop: Event['title'], locale: string, defaultLocale: string): string => { + if (prop && typeof prop === 'object') return prop[locale] || prop[defaultLocale] + return prop +} + +export const localizeEvent = (event: FullEvent, locale: string, defaultLocale: string): LocalizedEvent => { + return { + ...event, + title: localizeProp(event.title, locale, defaultLocale), + body: event.body && localizeProp(event.body, locale, defaultLocale), + htmlBody: event.htmlBody && localizeProp(event.htmlBody, locale, defaultLocale) + } +} + +export const getSubscriptionsFilter = (event: Event): Filter => { + const topicParts = event.topic.key.split(':') + const topicKeys = topicParts.map((part, i) => topicParts.slice(0, i + 1).join(':')) + const subscriptionsFilter: Filter = { 'topic.key': { $in: topicKeys } } + if (event.subscribedRecipient) { + subscriptionsFilter['recipient.id'] = event.subscribedRecipient.id + } else if (event.visibility === 'private') { + subscriptionsFilter.visibility = 'private' + } + if (event.sender) { + subscriptionsFilter['sender.type'] = event.sender.type + subscriptionsFilter['sender.id'] = event.sender.id + if (event.sender.role) subscriptionsFilter['sender.role'] = event.sender.role + if (event.sender.department) { + if (event.sender.department !== '*') { + subscriptionsFilter['sender.department'] = event.sender.department + } + } else { + subscriptionsFilter['sender.department'] = { $exists: false } + } + } else { + subscriptionsFilter.sender = { $exists: false } + } + + return subscriptionsFilter +} + +export const cleanEvent = (event: LocalizedEvent, sessionState: SessionStateAuthenticated) => { + // hide the user if the event is sent from another organization + if (event.originator) { + if (sessionState.account.type === event.sender?.type && sessionState.account.id === event.sender.id) { + if (event.originator.organization) { + if (sessionState.organization?.id !== event.originator.organization.id) { + delete event.originator.user + } + } + } + } + + // anonymize the user if the event is sent from a super admin + if (event.originator?.user?.admin && !sessionState.user?.adminMode) { + event.originator.user = { admin: true } + } + return event +} + +export const buildSearchTexts = (event: SearchableEvent, locales: string[], defaultLocale: string): SearchableEvent['_search'] => { + const search: SearchableEvent['_search'] = [] + for (const locale of locales) { + const localizedEvent = localizeEvent(event, locale, defaultLocale) + const searchParts: (string | undefined)[] = [...event.topic.key.split(':'), event.topic.title, localizedEvent.title, localizedEvent.body, event.sender?.id, event.sender?.name] + if (event.originator) { + if (event.originator.organization) { + searchParts.push(event.originator.organization.name, event.originator.organization.id) + } + if (event.originator.user && (!event.originator.organization || (event.sender?.type === 'organization' && event.sender.id === event.originator.organization.id))) { + searchParts.push(event.originator.user.name, event.originator.user.id) + } + } + search.push({ language: locale, text: searchParts.filter(Boolean).join(' ') }) + } + return search +} diff --git a/api/src/events/router.ts b/api/src/events/router.ts index 0619792..d41fa44 100644 --- a/api/src/events/router.ts +++ b/api/src/events/router.ts @@ -39,7 +39,7 @@ router.get('', async (req, res, next) => { const events = (await mongo.events.find(query).project(project).limit(size).sort(sort).toArray()) as FullEvent[] - const results = events.map(event => cleanEvent(localizeEvent(event, lang), sessionState)) + const results = events.map(event => cleanEvent(localizeEvent(event, lang || config.i18n.defaultLocale, config.i18n.defaultLocale), sessionState)) const response: any = { results } diff --git a/api/src/events/service.ts b/api/src/events/service.ts index 58eb7cc..db74e87 100644 --- a/api/src/events/service.ts +++ b/api/src/events/service.ts @@ -1,55 +1,17 @@ import { MongoBulkWriteError, type Filter } from 'mongodb' -import type { Event, FullEvent, SearchableEvent, LocalizedEvent, Subscription, WebhookSubscription, Notification } from '#types' +import type { Event, SearchableEvent, WebhookSubscription, Notification } from '#types' import config from '#config' import mongo from '#mongo' -import { sendNotification, prepareSubscriptionNotification } from '../notifications/service.ts' +import { prepareSubscriptionNotification } from '../notifications/operations.ts' +import { sendNotification } from '../notifications/service.ts' import { createWebhook } from '../webhooks/service.ts' import { nanoid } from 'nanoid' import debugModule from 'debug' -import { type SessionStateAuthenticated } from '@data-fair/lib-express' +import { localizeEvent, getSubscriptionsFilter, buildSearchTexts } from './operations.ts' const debug = debugModule('events') -const localizeProp = (prop: Event['title'], locale: string): string => { - if (prop && typeof prop === 'object') return prop[locale] || prop[config.i18n.defaultLocale] - return prop -} - -export const localizeEvent = (event: FullEvent, locale: string = config.i18n.defaultLocale): LocalizedEvent => { - return { - ...event, - title: localizeProp(event.title, locale), - body: event.body && localizeProp(event.body, locale), - htmlBody: event.htmlBody && localizeProp(event.htmlBody, locale) - } -} - -export const getSubscriptionsFilter = (event: Event): Filter => { - const topicParts = event.topic.key.split(':') - const topicKeys = topicParts.map((part, i) => topicParts.slice(0, i + 1).join(':')) - const subscriptionsFilter: Filter = { 'topic.key': { $in: topicKeys } } - if (event.subscribedRecipient) { - subscriptionsFilter['recipient.id'] = event.subscribedRecipient.id - } else if (event.visibility === 'private') { - subscriptionsFilter.visibility = 'private' - } - if (event.sender) { - subscriptionsFilter['sender.type'] = event.sender.type - subscriptionsFilter['sender.id'] = event.sender.id - if (event.sender.role) subscriptionsFilter['sender.role'] = event.sender.role - if (event.sender.department) { - if (event.sender.department !== '*') { - subscriptionsFilter['sender.department'] = event.sender.department - } - } else { - subscriptionsFilter['sender.department'] = { $exists: false } - } - } else { - subscriptionsFilter.sender = { $exists: false } - } - - return subscriptionsFilter -} +export { localizeEvent, getSubscriptionsFilter, cleanEvent } from './operations.ts' export const postEvents = async (events: Event[]) => { const eventsBulkOp = mongo.events.initializeUnorderedBulkOp() @@ -58,35 +20,20 @@ export const postEvents = async (events: Event[]) => { for (const rawEvent of events) { debug('post event', rawEvent) - // this logic should work much better on a mongodb version that would support multi-language indexing - // https://www.mongodb.com/docs/manual/core/indexes/index-types/index-text/specify-language-text-index/create-text-index-multiple-languages/ const event: SearchableEvent = { _id: nanoid(), visibility: 'private', ...rawEvent, _search: [] } - for (const locale of config.i18n.locales) { - const localizedEvent = localizeEvent(event, locale) - const searchParts: (string | undefined)[] = [...event.topic.key.split(':'), event.topic.title, localizedEvent.title, localizedEvent.body, event.sender?.id, event.sender?.name] - if (event.originator) { - if (event.originator.organization) { - searchParts.push(event.originator.organization.name, event.originator.organization.id) - } - if (event.originator.user && (!event.originator.organization || (event.sender?.type === 'organization' && event.sender.id === event.originator.organization.id))) { - // do not add the user name if the originator is another organization - searchParts.push(event.originator.user.name, event.originator.user.id) - } - } - if (event) { event._search.push({ language: locale, text: searchParts.filter(Boolean).join(' ') }) } - } + event._search = buildSearchTexts(event, config.i18n.locales, config.i18n.defaultLocale) eventsBulkOp.insert(event) const subscriptionsFilter = getSubscriptionsFilter(event) debug('find matching subscriptions', subscriptionsFilter) for await (const subscription of mongo.subscriptions.find(subscriptionsFilter)) { - const notification = prepareSubscriptionNotification(event, subscription) + const notification = prepareSubscriptionNotification(event, subscription, { notificationIcon: config.theme.notificationIcon }, config.i18n.defaultLocale, nanoid()) debug('send notification to', notification.recipient.id) notifsBulkOp.insert(notification) notifications.push(notification) @@ -95,7 +42,7 @@ export const postEvents = async (events: Event[]) => { const webhookSubscriptionsFilter = subscriptionsFilter as Filter for await (const webhookSubscription of mongo.webhookSubscriptions.find(webhookSubscriptionsFilter)) { // TODO: store a locale on webhooks subscription ? - await createWebhook(localizeEvent(event), webhookSubscription) + await createWebhook(localizeEvent(event, config.i18n.defaultLocale, config.i18n.defaultLocale), webhookSubscription) } } @@ -138,22 +85,3 @@ export const postEvents = async (events: Event[]) => { await sendNotification(notification, true) } } - -export const cleanEvent = (event: LocalizedEvent, sessionState: SessionStateAuthenticated) => { - // hide the user if the event is sent from another organization - if (event.originator) { - if (sessionState.account.type === event.sender?.type && sessionState.account.id === event.sender.id) { - if (event.originator.organization) { - if (sessionState.organization?.id !== event.originator.organization.id) { - delete event.originator.user - } - } - } - } - - // anonymize the user if the event is sent from a super admin - if (event.originator?.user?.admin && !sessionState.user?.adminMode) { - event.originator.user = { admin: true } - } - return event -} diff --git a/api/src/notifications/operations.ts b/api/src/notifications/operations.ts new file mode 100644 index 0000000..cac2178 --- /dev/null +++ b/api/src/notifications/operations.ts @@ -0,0 +1,47 @@ +// operations.ts — pure functions only +// must not import #mongo, #config, or store state + +import type { FullEvent, Notification, Subscription } from '#types' + +import { parseTemplate } from 'url-template' +import microTemplate from '@data-fair/lib-utils/micro-template.js' +import { localizeEvent } from '../events/operations.ts' + +interface PrepareDefaults { + notificationIcon?: string +} + +export const prepareSubscriptionNotification = (event: FullEvent, subscription: Subscription, defaults: PrepareDefaults, defaultLocale: string, id: string): Notification => { + const localizedEvent = localizeEvent(event, subscription.locale || defaultLocale, defaultLocale) + delete localizedEvent.resource + delete localizedEvent.originator + delete localizedEvent.urlParams + const notification: Notification = { + eventId: event._id, + icon: subscription.icon || defaults.notificationIcon || (subscription.origin + '/events/logo-192x192.png'), + locale: subscription.locale, + ...localizedEvent, + _id: id, + recipient: subscription.recipient, + origin: subscription.origin + } + if (subscription.outputs && (!notification.outputs || !notification.outputs.length)) { + notification.outputs = subscription.outputs + } + if (subscription.urlTemplate) { + notification.url = parseTemplate(subscription.urlTemplate).expand(event.urlParams || {}) + if (notification.url.startsWith('/') && subscription.origin) notification.url = subscription.origin + notification.url + } + if (!notification.topic.title && subscription.topic.title) { + notification.topic.title = subscription.topic.title + } + if (!notification.title && notification.topic.title) { + notification.title = notification.topic.title + } + + const templateParams = { origin: subscription.origin, hostname: new URL(subscription.origin).hostname } + if (notification.body) notification.body = microTemplate(notification.body, templateParams) + if (notification.htmlBody) notification.htmlBody = microTemplate(notification.htmlBody, templateParams) + + return notification +} diff --git a/api/src/notifications/service.ts b/api/src/notifications/service.ts index 7b34294..398d488 100644 --- a/api/src/notifications/service.ts +++ b/api/src/notifications/service.ts @@ -1,59 +1,22 @@ -import type { FullEvent, Notification, Subscription } from '#types' +import type { Notification } from '#types' -import { parseTemplate } from 'url-template' -import { nanoid } from 'nanoid' import Debug from 'debug' import i18n from 'i18n' import * as wsEmitter from '@data-fair/lib-node/ws-emitter.js' import { internalError } from '@data-fair/lib-node/observer.js' import axios from '@data-fair/lib-node/axios.js' -import microTemplate from '@data-fair/lib-utils/micro-template.js' import mongo from '#mongo' import config from '#config' import * as metrics from './metrics.js' -import { localizeEvent } from '../events/service.ts' import * as pushService from '../push/service.ts' import { MongoError } from 'mongodb' +export { prepareSubscriptionNotification } from './operations.ts' + const debug = Debug('notifications') const directoryUrl = config.privateDirectoryUrl -export const prepareSubscriptionNotification = (event: FullEvent, subscription: Subscription): Notification => { - const localizedEvent = localizeEvent(event, subscription.locale) - delete localizedEvent.resource - delete localizedEvent.originator - delete localizedEvent.urlParams - const notification: Notification = { - eventId: event._id, - icon: subscription.icon || config.theme.notificationIcon || (subscription.origin + '/events/logo-192x192.png'), - locale: subscription.locale, - ...localizedEvent, - _id: nanoid(), - recipient: subscription.recipient, - origin: subscription.origin - } - if (subscription.outputs && (!notification.outputs || !notification.outputs.length)) { - notification.outputs = subscription.outputs - } - if (subscription.urlTemplate) { - notification.url = parseTemplate(subscription.urlTemplate).expand(event.urlParams || {}) - if (notification.url.startsWith('/') && subscription.origin) notification.url = subscription.origin + notification.url - } - if (!notification.topic.title && subscription.topic.title) { - notification.topic.title = subscription.topic.title - } - if (!notification.title && notification.topic.title) { - notification.title = notification.topic.title - } - - const templateParams = { origin: subscription.origin, hostname: new URL(subscription.origin).hostname } - if (notification.body) notification.body = microTemplate(notification.body, templateParams) - if (notification.htmlBody) notification.htmlBody = microTemplate(notification.htmlBody, templateParams) - - return notification -} - export const sendNotification = async (notification: Notification, skipInsert = false) => { // global.events.emit('saveNotification', notification) if (!skipInsert) { diff --git a/api/src/push/operations.ts b/api/src/push/operations.ts new file mode 100644 index 0000000..3f47f01 --- /dev/null +++ b/api/src/push/operations.ts @@ -0,0 +1,11 @@ +// operations.ts — pure functions only +// must not import #mongo, #config, or store state + +import type { DeviceRegistration } from '#types' + +export function equalDeviceRegistrations (regId1: DeviceRegistration['id'] | null, regId2: DeviceRegistration['id'] | null) { + if (regId1 === null || regId2 === null) return false + if (typeof regId1 === 'string' && typeof regId2 === 'string' && regId1 === regId2) return true + if (typeof regId1 === 'object' && typeof regId2 === 'object' && regId1.endpoint === regId2.endpoint) return true + return false +} diff --git a/api/src/push/router.ts b/api/src/push/router.ts index a197f53..67d178a 100644 --- a/api/src/push/router.ts +++ b/api/src/push/router.ts @@ -13,19 +13,13 @@ import * as postRegistrationReq from '#doc/push/post-registration-req/index.ts' import { nanoid } from 'nanoid' import { session, reqSiteUrl, httpError } from '@data-fair/lib-express/index.js' import { getPushState, push, pushToDevice } from './service.ts' +import { equalDeviceRegistrations } from './operations.ts' const debug = Debug('webpush') const router = Router() export default router -export function equalDeviceRegistrations (regId1: DeviceRegistration['id'] | null, regId2: DeviceRegistration['id'] | null) { - if (regId1 === null || regId2 === null) return false - if (typeof regId1 === 'string' && typeof regId2 === 'string' && regId1 === regId2) return true - if (typeof regId1 === 'object' && typeof regId2 === 'object' && regId1.endpoint === regId2.endpoint) return true - return false -} - router.get('/vapidkey', async (req, res) => { res.send({ publicKey: getPushState().vapidKeys.publicKey }) }) diff --git a/api/src/push/service.ts b/api/src/push/service.ts index 4023bcc..8542c33 100644 --- a/api/src/push/service.ts +++ b/api/src/push/service.ts @@ -10,6 +10,7 @@ import mongo from '#mongo' import * as notificationsMetrics from '../notifications/metrics.js' import * as metrics from './metrics.js' import { internalError } from '@data-fair/lib-node/observer.js' +import { backoffMinutes } from '../shared/operations.ts' const debug = Debug('webpush') @@ -96,7 +97,7 @@ export const push = async (notification: Notification, forceRegistrationIndex: n console.warn('registration returned too many errors, disable it', error, JSON.stringify(registration)) } else { delete registration.disabled - registration.disabledUntil = dayjs().add(Math.ceil(Math.pow(registration.lastErrors.length, 2.5)), 'minute').toISOString() + registration.disabledUntil = dayjs().add(backoffMinutes(registration.lastErrors.length), 'minute').toISOString() console.warn('registration returned an error, progressively backoff', error, JSON.stringify(registration)) } } diff --git a/api/src/server.ts b/api/src/server.ts index 08cba48..ca8144d 100644 --- a/api/src/server.ts +++ b/api/src/server.ts @@ -53,5 +53,6 @@ export const stop = async () => { await webhooksWorker.stop() await wsServer.stop() if (config.observer.active) await stopObserver() + await locks.stop() await mongo.client.close() } diff --git a/api/src/shared/operations.ts b/api/src/shared/operations.ts new file mode 100644 index 0000000..61e7004 --- /dev/null +++ b/api/src/shared/operations.ts @@ -0,0 +1,6 @@ +// operations.ts — pure functions only +// must not import #mongo, #config, or store state + +export const backoffMinutes = (nbErrors: number): number => { + return Math.ceil(Math.pow(nbErrors, 2.5)) +} diff --git a/api/src/subscriptions/operations.ts b/api/src/subscriptions/operations.ts new file mode 100644 index 0000000..0774bd5 --- /dev/null +++ b/api/src/subscriptions/operations.ts @@ -0,0 +1,24 @@ +// operations.ts — pure functions only +// must not import #mongo, #config, or store state + +import type { Subscription } from '#types' +import type { User } from '@data-fair/lib-express/index.js' + +export const canSubscribePrivate = (sender: Subscription['sender'], user: User) => { + // super admin can do whatever he wants + if (user.adminMode) return true + if (!sender) return false + + // user sends to himself ? + if (sender.type === 'user') return sender.id === user.id + + if (sender.type === 'organization') { + let userOrg = user.organizations.find(o => o.id === sender.id && !o.department) + if (sender.department) { + userOrg = user.organizations.find(o => o.id === sender.id && o.department === sender.department) || userOrg + } + if (!userOrg) return false + if (sender.role && sender.role !== userOrg.role && userOrg.role !== 'admin') return false + return true + } +} diff --git a/api/src/subscriptions/router.ts b/api/src/subscriptions/router.ts index 267b624..c9cb3d5 100644 --- a/api/src/subscriptions/router.ts +++ b/api/src/subscriptions/router.ts @@ -1,12 +1,12 @@ import type { Subscription } from '#types' import type { Filter } from 'mongodb' -import type { User } from '@data-fair/lib-express/index.js' import { Router } from 'express' import { nanoid } from 'nanoid' import { session, mongoSort, mongoPagination, httpError, reqSiteUrl } from '@data-fair/lib-express/index.js' import mongo from '#mongo' import * as postReq from '#doc/subscriptions/post-req/index.ts' +import { canSubscribePrivate } from './operations.ts' const router = Router() export default router @@ -53,25 +53,6 @@ router.get('', async (req, res, next) => { res.json({ results, count }) }) -const canSubscribePrivate = (sender: Subscription['sender'], user: User) => { - // super admin can do whatever he wants - if (user.adminMode) return true - if (!sender) return false - - // user sends to himself ? - if (sender.type === 'user') return sender.id === user.id - - if (sender.type === 'organization') { - let userOrg = user.organizations.find(o => o.id === sender.id && !o.department) - if (sender.department) { - userOrg = user.organizations.find(o => o.id === sender.id && o.department === sender.department) || userOrg - } - if (!userOrg) return false - if (sender.role && sender.role !== userOrg.role && userOrg.role !== 'admin') return false - return true - } -} - // Create or update a subscription router.post('', async (req, res, next) => { const { user } = await session.reqAuthenticated(req) diff --git a/api/src/webhooks/worker.ts b/api/src/webhooks/worker.ts index e9b8db0..f4d0d83 100644 --- a/api/src/webhooks/worker.ts +++ b/api/src/webhooks/worker.ts @@ -9,6 +9,7 @@ import mongo from '#mongo' import axios from '@data-fair/lib-node/axios.js' import { internalError } from '@data-fair/lib-node/observer.js' import locks from '@data-fair/lib-node/locks.js' +import { backoffMinutes } from '../shared/operations.ts' const debug = Debug('webhooks-worker') @@ -86,7 +87,7 @@ const loop = async () => { debug('webhook failed 10 times, no more attempts') patch.$unset = { nextAttempt: '' } } else { - patch.$set.nextAttempt = dayjs().add(Math.ceil(Math.pow(webhook.nbAttempts + 1, 2.5)), 'minute').toDate() + patch.$set.nextAttempt = dayjs().add(backoffMinutes(webhook.nbAttempts + 1), 'minute').toDate() debug('webhook failed, progressively backoff', patch.$set.nextAttempt) } await mongo.webhooks.updateOne({ _id: webhook._id }, patch) diff --git a/commitlint.config.cjs b/commitlint.config.cjs deleted file mode 100644 index 4fedde6..0000000 --- a/commitlint.config.cjs +++ /dev/null @@ -1 +0,0 @@ -module.exports = { extends: ['@commitlint/config-conventional'] } diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000..7c4ff4d --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1 @@ +export default { extends: ['@commitlint/config-conventional'] } diff --git a/dev/delete-worktree.sh b/dev/delete-worktree.sh new file mode 100755 index 0000000..2122d66 --- /dev/null +++ b/dev/delete-worktree.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +BRANCH_NAME=$1 + +if [ -z "$BRANCH_NAME" ]; then + echo "Error: Please provide a branch name." + echo "Usage: ./dev/delete-worktree.sh feat-xyz" + exit 1 +fi + +REPO_NAME=$(basename "$PWD") +TARGET_DIR="../${REPO_NAME}_${BRANCH_NAME}" + +echo "Deleting worktree at $TARGET_DIR" +git worktree remove "$TARGET_DIR" --force diff --git a/dev/init-env.sh b/dev/init-env.sh new file mode 100755 index 0000000..4512b10 --- /dev/null +++ b/dev/init-env.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +RANDOM_NB=$((1024 + RANDOM % 48000)) +echo "Use random base port $RANDOM_NB" + +BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | sed 's/[^a-zA-Z0-9-]/-/g') +DEV_HOST="${BRANCH:-events}.localhost" + +cat < ".env" +DEV_HOST=${DEV_HOST} + +NGINX_PORT=$((RANDOM_NB)) + +DEV_API_PORT=$((RANDOM_NB + 1)) +DEV_UI_PORT=$((RANDOM_NB + 2)) +DEV_UI_HMR_PORT=$((RANDOM_NB + 3)) + +MONGO_PORT=$((RANDOM_NB + 10)) + +SD_PORT=$((RANDOM_NB + 20)) +EOF diff --git a/dev/resources/nginx.conf b/dev/resources/nginx.conf deleted file mode 100644 index d684a76..0000000 --- a/dev/resources/nginx.conf +++ /dev/null @@ -1,126 +0,0 @@ -# nginx configuration file for Data Fair development and test environment - -user nginx; -worker_processes auto; - -error_log /var/log/nginx/error.log notice; -pid /var/run/nginx.pid; - -events { - worker_connections 1024; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - # use header origin if referer is empty - map $http_referer $reqref { - default $http_referer; - "" $http_origin; - } - - sendfile on; - #tcp_nopush on; - - keepalive_timeout 65; - - map $http_upgrade $connection_upgrade { - default upgrade; - '' close; - } - - # first origin http://localhost:5600 - server { - listen 5600; - server_name _; - - # Transmit host, protocol and user ip, we use it for routing, rate limiting, etc. - proxy_set_header Host $http_host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Client-IP $remote_addr; - - # web socket support - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - - # hmr - proxy_read_timeout 86400; - - location = / { - return 302 /events/dev; - } - - location = /events/ { - return 302 /events/dev; - } - - location /events/api/ { - proxy_pass http://localhost:8082; - } - - location /events/ { - # port 6220 to use vite dev server - # port 8082 to use built application - proxy_pass http://localhost:6220; - } - - location /simple-directory/ { - proxy_pass http://localhost:8080; - } - - # alternative exposition of services on a subpath - # this time without the vite dev-server - location /built/events/ { - proxy_pass http://localhost:8082; - } - location /built/simple-directory { - rewrite ^/built/simple-directory/(.*) /$1 break; - proxy_pass http://localhost:8080/; - } - } - - # 2nd origin http://localhost:5601 - server { - listen 5601; - server_name _; - - # Transmit host, protocol and user ip, we use it for routing, rate limiting, etc. - proxy_set_header Host $http_host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Client-IP $remote_addr; - - # web socket support - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - - # hmr - proxy_read_timeout 86400; - - location = / { - return 302 /events/dev; - } - - location = /events/ { - return 302 /events/dev; - } - - location /events/api/ { - proxy_pass http://localhost:8082; - } - - location /events/ { - proxy_pass http://localhost:6220; - } - - location /simple-directory/ { - proxy_pass http://localhost:8080; - } - } -} diff --git a/dev/resources/nginx.conf.template b/dev/resources/nginx.conf.template new file mode 100644 index 0000000..3885d32 --- /dev/null +++ b/dev/resources/nginx.conf.template @@ -0,0 +1,62 @@ +# use header origin if referer is empty +map $http_referer $reqref { + default $http_referer; + "" $http_origin; +} + +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +server { + listen ${NGINX_PORT}; + server_name ${DEV_HOST}; + + # Transmit host, protocol and user ip, we use it for routing, rate limiting, etc. + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Client-IP $remote_addr; + + # web socket support + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + + # hmr + proxy_read_timeout 86400; + + location = / { + return 302 /events/dev; + } + + location = /events/ { + return 302 /events/dev; + } + + location /events/api/ { + proxy_pass http://localhost:${DEV_API_PORT}; + } + + location /events/ { + # port ${DEV_UI_PORT} to use vite dev server + # port ${DEV_API_PORT} to use built application + proxy_pass http://localhost:${DEV_UI_PORT}; + } + + location /simple-directory/ { + proxy_pass http://localhost:${SD_PORT}; + } + + # alternative exposition of services on a subpath + # this time without the vite dev-server + location /built/events/ { + proxy_pass http://localhost:${DEV_API_PORT}; + } + location /built/simple-directory { + rewrite ^/built/simple-directory/(.*) /$1 break; + proxy_pass http://localhost:${SD_PORT}/; + } +} diff --git a/dev/resources/organizations.json b/dev/resources/organizations.json index 7e4f473..a918ef4 100644 --- a/dev/resources/organizations.json +++ b/dev/resources/organizations.json @@ -1,28 +1,36 @@ [{ - "id": "orga1", - "name": "Orga 1", + "id": "dev1", + "name": "Dev Organization", "members": [{ "id": "superadmin", "role": "admin" }, { - "id": "admin1", + "id": "dev1-admin1", "role": "admin" }, { - "id": "user1", + "id": "dev1-user1", "role": "user" }] -}, -{ - "id": "orga2", - "name": "Orga 2", +}, { + "id": "test1", + "name": "Test Organization 1", "members": [{ - "id": "user1", + "id": "test1-admin1", + "role": "admin" + }, { + "id": "test-user1", + "role": "user" + }] +}, { + "id": "test2", + "name": "Test Organization 2", + "members": [{ + "id": "test-user1", "role": "user", "department": "dep1" }, { - "id": "user2", + "id": "test-user2", "role": "user", "department": "dep2" }] -} -] +}] diff --git a/dev/resources/users.json b/dev/resources/users.json index 52b9faf..e0909fc 100644 --- a/dev/resources/users.json +++ b/dev/resources/users.json @@ -3,24 +3,42 @@ "email": "superadmin@test.com", "isAdmin": true, "password": { - "clear": "superpasswd" + "clear": "superpasswd" } }, { - "id": "user1", - "email": "user1@test.com", + "id": "dev-user1", + "email": "dev-user1@test.com", "password": { - "clear": "passwd" + "clear": "passwd" } }, { - "id": "user2", - "email": "user2@test.com", + "id": "dev1-admin1", + "email": "dev1-admin1@test.com", "password": { - "clear": "passwd" + "clear": "passwd" } }, { - "id": "admin1", - "email": "admin1@test.com", + "id": "dev1-user1", + "email": "dev1-user1@test.com", "password": { - "clear": "passwd" + "clear": "passwd" + } +}, { + "id": "test-user1", + "email": "test-user1@test.com", + "password": { + "clear": "passwd" + } +}, { + "id": "test-user2", + "email": "test-user2@test.com", + "password": { + "clear": "passwd" + } +}, { + "id": "test1-admin1", + "email": "test1-admin1@test.com", + "password": { + "clear": "passwd" } }] diff --git a/dev/scripts/send-notifications.ts b/dev/scripts/send-notifications.ts index 0b8e3c4..0ada3c6 100644 --- a/dev/scripts/send-notifications.ts +++ b/dev/scripts/send-notifications.ts @@ -9,13 +9,13 @@ const postEvent = async (events: PostEventReq['body']) => { await postEvent([{ title: 'A notification ' + new Date().toISOString(), - sender: { type: 'organization', id: 'orga1' }, + sender: { type: 'organization', id: 'dev1' }, topic: { key: 'topic1', title: 'Topic 1' }, date: new Date().toISOString(), resource: { type: 'dataset', id: 'dataset1', title: 'Dataset 1' }, originator: { - user: { id: 'user1', name: 'User 2' }, - organization: { id: 'orga1', name: 'Organization 1' } + user: { id: 'dev-user1', name: 'Dev User 1' }, + organization: { id: 'dev1', name: 'Dev Organization' } } }]) diff --git a/dev/status.sh b/dev/status.sh new file mode 100755 index 0000000..eda80fe --- /dev/null +++ b/dev/status.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +# Check the status of all dev environment services. +# Read-only — never starts, stops, or restarts anything. +# Safe to run from sandbox (only needs curl and .env). + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +# Load port configuration +if [ -f "$PROJECT_DIR/.env" ]; then + set -a + source "$PROJECT_DIR/.env" + set +a +else + echo "ERROR: .env file not found at $PROJECT_DIR/.env" + exit 1 +fi + +NGINX="http://${DEV_HOST}:${NGINX_PORT}" + +# Colors (disabled if not a terminal) +if [ -t 1 ]; then + GREEN='\033[0;32m' + RED='\033[0;31m' + YELLOW='\033[0;33m' + BOLD='\033[1m' + RESET='\033[0m' +else + GREEN='' RED='' YELLOW='' BOLD='' RESET='' +fi + +check_http() { + local name="$1" url="$2" + local http_code + http_code=$(curl -s -L --max-time 2 -o /dev/null -w "%{http_code}" "$url" 2>&1) || http_code="000" + if [ "$http_code" = "000" ]; then + printf "${RED}%-20s DOWN %s (connection refused)${RESET}\n" "$name" "$url" + elif [ "$http_code" -ge 200 ] && [ "$http_code" -lt 400 ]; then + printf "${GREEN}%-20s UP %s${RESET}\n" "$name" "$url" + else + printf "${YELLOW}%-20s ERROR %s (HTTP %s)${RESET}\n" "$name" "$url" "$http_code" + fi +} + +check_tcp() { + local name="$1" host="$2" port="$3" + if (echo > /dev/tcp/"$host"/"$port") 2>/dev/null; then + printf "${GREEN}%-20s UP %s:%s${RESET}\n" "$name" "$host" "$port" + else + printf "${RED}%-20s DOWN %s:%s${RESET}\n" "$name" "$host" "$port" + fi +} + +echo -e "${BOLD}Dev environment status${RESET}" +echo "" + +# --- Nginx (gateway to everything) --- +echo -e "${BOLD}Nginx proxy:${RESET}" +check_http "nginx" "$NGINX" +echo "" + +# --- Dev processes (probed through nginx) --- +echo -e "${BOLD}Dev processes:${RESET}" +check_http "dev-api" "$NGINX/events/api/ping" +check_http "dev-ui" "$NGINX/events" +echo "" + +# --- Docker compose services (probed through nginx where possible) --- +echo -e "${BOLD}Docker compose services:${RESET}" +check_http "simple-directory" "$NGINX/simple-directory/" +check_tcp "mongo" "localhost" "${MONGO_PORT}" +echo "" + +# --- Docker compose status (if docker/podman available) --- +if command -v docker &> /dev/null && docker compose version &> /dev/null; then + echo -e "${BOLD}Container details:${RESET}" + (cd "$PROJECT_DIR" && docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null) || echo "(docker compose not available)" + echo "" +fi + +# --- Log files --- +echo -e "${BOLD}Log files:${RESET}" +found_logs=false +for log in "$PROJECT_DIR"/dev/logs/*.log; do + [ -f "$log" ] || continue + found_logs=true + name=$(basename "$log") + size=$(wc -c < "$log" 2>/dev/null || echo 0) + mod=$(date -r "$log" "+%H:%M:%S" 2>/dev/null || echo "unknown") + printf " %-25s %6s bytes (last modified: %s)\n" "$name" "$size" "$mod" +done +$found_logs || echo " (no log files found)" diff --git a/dev/worktree.sh b/dev/worktree.sh new file mode 100755 index 0000000..649c169 --- /dev/null +++ b/dev/worktree.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +BRANCH_NAME=$1 + +if [ -z "$BRANCH_NAME" ]; then + echo "Error: Please provide a branch name." + echo "Usage: ./dev/worktree.sh feat-xyz" + exit 1 +fi + +SOURCE_BRANCH=$(git branch --show-current) +REPO_NAME=$(basename "$PWD") +TARGET_DIR="../${REPO_NAME}_${BRANCH_NAME}" + +echo "Creating worktree at $TARGET_DIR from branch $SOURCE_BRANCH" +git worktree add -b "$BRANCH_NAME" "$TARGET_DIR" $SOURCE_BRANCH + +cd $TARGET_DIR + +echo "Create .env file" +./dev/init-env.sh + +echo "npm ci" +npm ci + +echo "npm run build-types" +npm run build-types + +echo "-----------------------------------------------" +echo "Setup Complete!" +echo "Location: $TARGET_DIR" +echo "Branch: $BRANCH_NAME" +echo "-----------------------------------------------" +echo "Next step:" +echo " cd $TARGET_DIR" +echo "" diff --git a/docker-compose.yml b/docker-compose.yml index baa7061..316edb2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3' services: ##### @@ -6,10 +5,16 @@ services: ##### nginx: - image: nginx:1.23.1-alpine + image: nginx:1.29.4-alpine network_mode: host + environment: + DEV_HOST: ${DEV_HOST} + NGINX_PORT: ${NGINX_PORT} + DEV_API_PORT: ${DEV_API_PORT} + DEV_UI_PORT: ${DEV_UI_PORT} + SD_PORT: ${SD_PORT} volumes: - - ./dev/resources/nginx.conf:/etc/nginx/nginx.conf:ro + - ./dev/resources/nginx.conf.template:/etc/nginx/templates/default.conf.template:ro - ./dev/data/:/data ##### @@ -19,25 +24,21 @@ services: simple-directory: image: ghcr.io/data-fair/simple-directory:8 network_mode: host - # ports: - # - 6221:8080 - # - 1080:1080 - # - 1025:1025 depends_on: - mongo environment: - - PORT=8080 - - DEBUG=session - - ADMINS=["superadmin@test.com"] - - PUBLIC_URL=http://localhost:5600/simple-directory - - MAILDEV_ACTIVE=true - - STORAGE_TYPE=file - - ROLES_DEFAULTS=["admin", "contrib", "user"] - - OBSERVER_ACTIVE=false - - AUTHRATELIMIT_ATTEMPTS=1000 - - CIPHER_PASSWORD=dev - - CONTACT=contact@test.com - - MONGO_URL=mongodb://localhost:27017/simple-directory + PORT: ${SD_PORT} + DEBUG: session + ADMINS: '["superadmin@test.com"]' + PUBLIC_URL: http://${DEV_HOST}:${NGINX_PORT}/simple-directory + MAILDEV_ACTIVE: "true" + STORAGE_TYPE: file + ROLES_DEFAULTS: '["admin", "contrib", "user"]' + OBSERVER_ACTIVE: "false" + AUTHRATELIMIT_ATTEMPTS: "1000" + CIPHER_PASSWORD: dev + CONTACT: contact@test.com + MONGO_URL: mongodb://localhost:${MONGO_PORT}/simple-directory volumes: - ./dev/resources/users.json:/app/data/users.json - ./dev/resources/organizations.json:/app/data/organizations.json @@ -47,12 +48,11 @@ services: ##### mongo: - image: mongo:4.2 + image: mongo:7 ports: - - 27017:27017 + - ${MONGO_PORT}:27017 volumes: - mongo-data:/data/db volumes: - mongo-data: - elasticsearch-data: \ No newline at end of file + mongo-data: \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index 382ac56..19b0e4f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -2,7 +2,7 @@ import neostandard from 'neostandard' import dfLibRecommended from '@data-fair/lib-utils/eslint/recommended.js' export default [ - { ignores: ['ui/*', '**/.type/'] }, + { ignores: ['ui/*', '**/.type/', 'dev/*', 'node_modules/*'] }, ...dfLibRecommended, ...neostandard({ ts: true }) ] diff --git a/package-lock.json b/package-lock.json index 3a21955..2846674 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,9 +18,12 @@ "devDependencies": { "@commitlint/config-conventional": "^19.8.1", "@data-fair/lib-node": "^2.12.1", + "@playwright/test": "^1.58.2", "@types/config": "^3.3.5", "@types/debug": "^4.1.13", "commitlint": "^19.8.1", + "dotenv": "^17.3.1", + "dotenv-cli": "^11.0.0", "eslint": "^9.39.4", "eslint-plugin-vue": "^9.33.0", "eslint-plugin-vuetify": "github:albanm/eslint-plugin-vuetify", @@ -28,8 +31,10 @@ "json-schema-to-typescript": "^11.0.5", "neostandard": "^0.12.2", "nock": "^13.5.6", + "nodemon": "^3.1.9", "tough-cookie": "^5.1.2", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "ws": "^8.20.0" } }, "api": { @@ -50,7 +55,7 @@ "jwks-rsa": "3", "mongodb": "^6.21.0", "nanoid": "^5.1.7", - "node-pushnotifications": "^3.1.3", + "node-pushnotifications": "^5.0.1", "prom-client": "^15.1.3", "url-template": "^3.1.1", "useragent": "^2.3.0", @@ -85,15 +90,6 @@ "node": "^18 || >=20" } }, - "node_modules/@antfu/utils": { - "version": "0.7.10", - "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz", - "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/@apidevtools/json-schema-ref-parser": { "version": "11.9.3", "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz", @@ -161,9 +157,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", "license": "MIT", "dependencies": { "@babel/types": "^7.29.0" @@ -234,6 +230,16 @@ "node": ">=v18" } }, + "node_modules/@commitlint/cli/node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@commitlint/config-conventional": { "version": "19.8.1", "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.8.1.tgz", @@ -398,6 +404,16 @@ "node": ">=v18" } }, + "node_modules/@commitlint/read/node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@commitlint/resolve-extends": { "version": "19.8.1", "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.8.1.tgz", @@ -470,9 +486,9 @@ } }, "node_modules/@data-fair/frame": { - "version": "0.18.4", - "resolved": "https://registry.npmjs.org/@data-fair/frame/-/frame-0.18.4.tgz", - "integrity": "sha512-ETsE/Vzd/Vr3vfDJNS3bWIh05cG6S3kuJLBsRAHo7N25Nj1Z+hnoBMMDwJXjgM+UT3nHxH+OMp6MOQqDTpdtyg==", + "version": "0.17.7", + "resolved": "https://registry.npmjs.org/@data-fair/frame/-/frame-0.17.7.tgz", + "integrity": "sha512-JxBQ8NXKiq32zaAmr9wKvCIyhdSieOikJtVyRh0byxFmeoOHTh/gwLWx/MbHC3kNCwfkV5S63e+BzZ4tIb0pUw==", "license": "MIT" }, "node_modules/@data-fair/lib-common-types": { @@ -519,9 +535,9 @@ } }, "node_modules/@data-fair/lib-express/node_modules/nanoid": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.7.tgz", - "integrity": "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", "funding": [ { "type": "github", @@ -641,9 +657,9 @@ } }, "node_modules/@data-fair/lib-utils": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@data-fair/lib-utils/-/lib-utils-1.10.1.tgz", - "integrity": "sha512-1rkWJO8YE2bPqFvzp/V0/rpsK5LkDtw/ytcVtNQNGtEsfah+reokLYFH8GFPeUxRxk1pKt3GN6XczEKbJe/Aeg==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@data-fair/lib-utils/-/lib-utils-1.10.0.tgz", + "integrity": "sha512-ucMXQxS3LuQJhjF0cAmMTl+pysU7LBq2VUnikf/+w9Y6QmTpDylmj9LdtoSxNyjJ9fpUVdglirJwXOOMAbfvtQ==", "license": "MIT", "peerDependencies": { "dayjs": "1" @@ -665,9 +681,9 @@ } }, "node_modules/@data-fair/lib-vue": { - "version": "1.27.1", - "resolved": "https://registry.npmjs.org/@data-fair/lib-vue/-/lib-vue-1.27.1.tgz", - "integrity": "sha512-vpUu7GXvdy3fNYiX6Y/heka+FBLrltcy/9ksCfVn0yTsOiXrS3tFwaD3RUMQUF+Q72KsGNI47A6TN9fgpOb/5w==", + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@data-fair/lib-vue/-/lib-vue-1.27.0.tgz", + "integrity": "sha512-9yCj9cSXo2lrU8X3AWURjODuSQY+MIS2Wf8we7rx5HG/k3qEV8PKLS1WS2dGiuhyh5to4hXeLMOOCgne599dAw==", "license": "MIT", "peer": true, "dependencies": { @@ -681,8 +697,7 @@ "dayjs": "1", "ofetch": "1", "reconnecting-websocket": "4", - "vue": "3", - "vue-router": "4 || 5" + "vue": "3" }, "peerDependenciesMeta": { "dayjs": { @@ -690,34 +705,13 @@ }, "reconnecting-websocket": { "optional": true - }, - "vue-router": { - "optional": true } } }, - "node_modules/@data-fair/lib-vuetify": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/@data-fair/lib-vuetify/-/lib-vuetify-1.13.4.tgz", - "integrity": "sha512-QRbY9QLfulRN1Q4Ehk2yN5CTcI8yVYEB/QMn3kWJ/QKKAyC2njAfjwuk6mqdAt21N0S3CYnNM0+fJ3gWnnEx9g==", - "license": "MIT", - "dependencies": { - "@data-fair/lib-common-types": "^1.10.4", - "@mdi/js": "^7.4.47", - "@vueuse/core": "^14.0.0" - }, - "peerDependencies": { - "@data-fair/lib-vue": "^1.15.0", - "ofetch": "1", - "vue-i18n": "10 || 11", - "vuetify": "3" - } - }, "node_modules/@emnapi/core": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", - "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", - "dev": true, + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", + "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==", "license": "MIT", "optional": true, "dependencies": { @@ -726,10 +720,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", - "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", - "dev": true, + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", + "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", "license": "MIT", "optional": true, "dependencies": { @@ -740,7 +733,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1285,9 +1277,9 @@ } }, "node_modules/@firebase/app-check-interop-types": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz", - "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==", "license": "Apache-2.0" }, "node_modules/@firebase/app-types": { @@ -1596,23 +1588,23 @@ } }, "node_modules/@intlify/bundle-utils": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-9.0.0.tgz", - "integrity": "sha512-19dunbgM4wuCvi2xSai2PKhXkcKGjlbJhNWm9BCQWkUYcPmXwzptNWOE0O7OSrhNlEDxwpkHsJzZ/vLbCkpElw==", + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-11.0.7.tgz", + "integrity": "sha512-fEO3CJGPymxieGh8BHox7d6stgajDQae7wgpH6YYw7WX+cdW6jTTXyljZqz7OV3JcwlS9M9UHSoO+YwiO56IhA==", "license": "MIT", "dependencies": { - "@intlify/message-compiler": "next", - "@intlify/shared": "next", + "@intlify/message-compiler": "^11.1.12", + "@intlify/shared": "^11.1.12", "acorn": "^8.8.2", + "esbuild": "^0.25.4", "escodegen": "^2.1.0", "estree-walker": "^2.0.2", "jsonc-eslint-parser": "^2.3.0", - "mlly": "^1.2.0", - "source-map-js": "^1.0.1", + "source-map-js": "^1.2.1", "yaml-eslint-parser": "^1.2.2" }, "engines": { - "node": ">= 18" + "node": ">= 20" }, "peerDependenciesMeta": { "petite-vue-i18n": { @@ -1623,198 +1615,736 @@ } } }, - "node_modules/@intlify/bundle-utils/node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" + "node": ">=18" } }, - "node_modules/@intlify/core-base": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.8.tgz", - "integrity": "sha512-FoHslNWSoHjdUBLy35bpm9PV/0LVI/DSv9L6Km6J2ad8r/mm0VaGg06C40FqlE8u2ADcGUM60lyoU7Myo4WNZQ==", + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], "license": "MIT", - "dependencies": { - "@intlify/message-compiler": "10.0.8", - "@intlify/shared": "10.0.8" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" + "node": ">=18" } }, - "node_modules/@intlify/core-base/node_modules/@intlify/message-compiler": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.8.tgz", - "integrity": "sha512-DV+sYXIkHVd5yVb2mL7br/NEUwzUoLBsMkV3H0InefWgmYa34NLZUvMCGi5oWX+Hqr2Y2qUxnVrnOWF4aBlgWg==", + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@intlify/shared": "10.0.8", - "source-map-js": "^1.0.2" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" + "node": ">=18" } }, - "node_modules/@intlify/core-base/node_modules/@intlify/shared": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.8.tgz", - "integrity": "sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==", + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" + "node": ">=18" } }, - "node_modules/@intlify/message-compiler": { - "version": "12.0.0-alpha.3", - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-12.0.0-alpha.3.tgz", - "integrity": "sha512-mDDTN3gfYOHhBnpnlby19UHyvMaOnzdlpsIrxUfs44R/vCATfn8pMOkE8PXD2t410xkocEj3FpDcC9XC/0v4Dg==", + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "@intlify/shared": "12.0.0-alpha.3", - "source-map-js": "^1.0.2" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" + "node": ">=18" } }, - "node_modules/@intlify/message-compiler/node_modules/@intlify/shared": { - "version": "12.0.0-alpha.3", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-12.0.0-alpha.3.tgz", - "integrity": "sha512-ryaNYBvxQjyJUmVuBBg+HHUsmGnfxcEUPR0NCeG4/K9N2qtyFE35C80S15IN6iYFE2MGWLN7HfOSyg0MXZIc9w==", + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" + "node": ">=18" } }, - "node_modules/@intlify/shared": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.3.0.tgz", - "integrity": "sha512-LC6P/uay7rXL5zZ5+5iRJfLs/iUN8apu9tm8YqQVmW3Uq3X4A0dOFUIDuAmB7gAC29wTHOS3EiN/IosNSz0eNQ==", + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" + "node": ">=18" } }, - "node_modules/@intlify/unplugin-vue-i18n": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-5.3.1.tgz", - "integrity": "sha512-76huP8TpMOtBMLsYYIMLNbqMPXJ7+Q6xcjP6495h/pmbOQ7sw/DB8E0OFvDFeIZ2571a4ylzJnz+KMuYbAs1xA==", + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@intlify/bundle-utils": "^9.0.0", - "@intlify/shared": "latest", - "@intlify/vue-i18n-extensions": "^7.0.0", - "@rollup/pluginutils": "^5.1.0", - "@typescript-eslint/scope-manager": "^8.13.0", - "@typescript-eslint/typescript-estree": "^8.13.0", - "debug": "^4.3.3", - "fast-glob": "^3.2.12", - "js-yaml": "^4.1.0", - "json5": "^2.2.3", - "pathe": "^1.0.0", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2", - "unplugin": "^1.1.0", - "vue": "^3.4" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "petite-vue-i18n": "*", - "vue": "^3.2.25", - "vue-i18n": "*" - }, - "peerDependenciesMeta": { - "petite-vue-i18n": { - "optional": true - }, - "vue-i18n": { - "optional": true - } + "node": ">=18" } }, - "node_modules/@intlify/vue-i18n-extensions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@intlify/vue-i18n-extensions/-/vue-i18n-extensions-7.0.0.tgz", - "integrity": "sha512-MtvfJnb4aklpCU5Q/dkWkBT/vGsp3qERiPIwtTq5lX4PCLHtUprAJZp8wQj5ZcwDaFCU7+yVMjYbeXpIf927cA==", + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], "license": "MIT", - "dependencies": { - "@babel/parser": "^7.24.6", - "@intlify/shared": "^10.0.0", - "@vue/compiler-dom": "^3.2.45", - "vue-i18n": "^10.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@intlify/shared": "^9.0.0 || ^10.0.0", - "@vue/compiler-dom": "^3.0.0", - "vue": "^3.0.0", - "vue-i18n": "^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "@intlify/shared": { - "optional": true - }, - "@vue/compiler-dom": { - "optional": true - }, - "vue": { - "optional": true - }, - "vue-i18n": { - "optional": true - } + "node": ">=18" } }, - "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/shared": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.8.tgz", - "integrity": "sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==", + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], "license": "MIT", - "engines": { - "node": ">= 16" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/@intlify/bundle-utils/node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/@intlify/core-base": { + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.8.tgz", + "integrity": "sha512-FoHslNWSoHjdUBLy35bpm9PV/0LVI/DSv9L6Km6J2ad8r/mm0VaGg06C40FqlE8u2ADcGUM60lyoU7Myo4WNZQ==", + "license": "MIT", + "dependencies": { + "@intlify/message-compiler": "10.0.8", + "@intlify/shared": "10.0.8" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/core-base/node_modules/@intlify/message-compiler": { + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.8.tgz", + "integrity": "sha512-DV+sYXIkHVd5yVb2mL7br/NEUwzUoLBsMkV3H0InefWgmYa34NLZUvMCGi5oWX+Hqr2Y2qUxnVrnOWF4aBlgWg==", + "license": "MIT", + "dependencies": { + "@intlify/shared": "10.0.8", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/core-base/node_modules/@intlify/shared": { + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.8.tgz", + "integrity": "sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.3.0.tgz", + "integrity": "sha512-RAJp3TMsqohg/Wa7bVF3cChRhecSYBLrTCQSj7j0UtWVFLP+6iEJoE2zb7GU5fp+fmG5kCbUdzhmlAUCWXiUJw==", + "license": "MIT", + "dependencies": { + "@intlify/shared": "11.3.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/shared": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.3.0.tgz", + "integrity": "sha512-LC6P/uay7rXL5zZ5+5iRJfLs/iUN8apu9tm8YqQVmW3Uq3X4A0dOFUIDuAmB7gAC29wTHOS3EiN/IosNSz0eNQ==", + "license": "MIT", + "engines": { + "node": ">= 16" }, "funding": { "url": "https://github.com/sponsors/kazupon" } }, + "node_modules/@intlify/unplugin-vue-i18n": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-11.0.7.tgz", + "integrity": "sha512-wswKprS1D8VfnxxVhKxug5wa3MbDSOcCoXOBjnzhMK+6NfP6h6UI8pFqSBIvcW8nPDuzweTc0Sk3PeBCcubfoQ==", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@intlify/bundle-utils": "11.0.7", + "@intlify/shared": "^11.1.12", + "@intlify/vue-i18n-extensions": "^8.0.0", + "@rollup/pluginutils": "^5.1.0", + "@typescript-eslint/scope-manager": "^8.13.0", + "@typescript-eslint/typescript-estree": "^8.13.0", + "debug": "^4.3.3", + "fast-glob": "^3.2.12", + "pathe": "^2.0.3", + "picocolors": "^1.0.0", + "unplugin": "^2.3.4", + "vue": "^3.5.21" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "petite-vue-i18n": "*", + "vue": "^3.2.25", + "vue-i18n": "*" + }, + "peerDependenciesMeta": { + "petite-vue-i18n": { + "optional": true + }, + "vue-i18n": { + "optional": true + } + } + }, + "node_modules/@intlify/vue-i18n-extensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@intlify/vue-i18n-extensions/-/vue-i18n-extensions-8.0.0.tgz", + "integrity": "sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.24.6", + "@intlify/shared": "^10.0.0", + "@vue/compiler-dom": "^3.2.45", + "vue-i18n": "^10.0.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@intlify/shared": "^9.0.0 || ^10.0.0 || ^11.0.0", + "@vue/compiler-dom": "^3.0.0", + "vue": "^3.0.0", + "vue-i18n": "^9.0.0 || ^10.0.0 || ^11.0.0" + }, + "peerDependenciesMeta": { + "@intlify/shared": { + "optional": true + }, + "@vue/compiler-dom": { + "optional": true + }, + "vue": { + "optional": true + }, + "vue-i18n": { + "optional": true + } + } + }, + "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/shared": { + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.8.tgz", + "integrity": "sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -2030,6 +2560,24 @@ "node": ">=8.0.0" } }, + "node_modules/@oxc-project/runtime": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz", + "integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz", + "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, "node_modules/@package-json/types": { "version": "0.0.12", "resolved": "https://registry.npmjs.org/@package-json/types/-/types-0.0.12.tgz", @@ -2293,201 +2841,441 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", - "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parse/node-apn": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-7.1.0.tgz", + "integrity": "sha512-a40P5nScLDi9Pf7koKKkbwI73px0q+iLaKYNrr7kyKJebq/4duGOy3mMevZS0zltn171k3jB5BWCC27dPGsMmw==", + "license": "MIT", + "dependencies": { + "debug": "4.4.3", + "jsonwebtoken": "9.0.3", + "node-forge": "1.3.2", + "verror": "1.10.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@parse/node-apn/node_modules/node-forge": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz", + "integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@playwright/test": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", + "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz", + "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==", "cpu": [ - "ia32" + "x64" ], "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", - "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@parcel/watcher/node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT", - "optional": true - }, - "node_modules/@parse/node-apn": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-6.5.0.tgz", - "integrity": "sha512-ktIgD8ElZf23G04+W4ufvSBFJyqHeyPZ9AcMNBh2bGnkj6bMcV3QGKavxOxOn7OTr8heOMuvFkzv09zkrA0G2A==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "debug": "4.4.0", - "jsonwebtoken": "9.0.2", - "node-forge": "1.3.1", - "verror": "1.10.1" - }, + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">= 14" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@parse/node-apn/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz", + "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==", + "cpu": [ + "wasm32" + ], "license": "MIT", + "optional": true, "dependencies": { - "ms": "^2.1.3" + "@napi-rs/wasm-runtime": "^1.1.1" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=14.0.0" } }, - "node_modules/@parse/node-apn/node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", "license": "MIT", + "optional": true, "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" }, - "engines": { - "node": ">=12", - "npm": ">=6" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, - "node_modules/@parse/node-apn/node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==", + "cpu": [ + "arm64" + ], "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@parse/node-apn/node_modules/jws": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", - "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "jwa": "^1.4.2", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "license": "BSD-3-Clause", "optional": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause", - "optional": true + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz", + "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==", + "license": "MIT" }, "node_modules/@rollup/pluginutils": { "version": "5.3.0", @@ -2512,9 +3300,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", - "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", "cpu": [ "arm" ], @@ -2525,9 +3313,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", - "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", "cpu": [ "arm64" ], @@ -2538,9 +3326,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", - "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", "cpu": [ "arm64" ], @@ -2551,9 +3339,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", - "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", "cpu": [ "x64" ], @@ -2564,9 +3352,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", - "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", "cpu": [ "arm64" ], @@ -2577,9 +3365,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", - "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", "cpu": [ "x64" ], @@ -2590,9 +3378,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", - "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", "cpu": [ "arm" ], @@ -2603,9 +3391,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", - "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", "cpu": [ "arm" ], @@ -2616,9 +3404,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", - "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", "cpu": [ "arm64" ], @@ -2629,9 +3417,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", - "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", "cpu": [ "arm64" ], @@ -2642,9 +3430,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", - "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", "cpu": [ "loong64" ], @@ -2655,9 +3443,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", - "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", "cpu": [ "loong64" ], @@ -2668,9 +3456,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", - "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", "cpu": [ "ppc64" ], @@ -2681,9 +3469,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", - "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", "cpu": [ "ppc64" ], @@ -2694,9 +3482,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", - "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", "cpu": [ "riscv64" ], @@ -2707,9 +3495,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", - "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", "cpu": [ "riscv64" ], @@ -2720,9 +3508,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", - "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", "cpu": [ "s390x" ], @@ -2733,9 +3521,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", - "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", "cpu": [ "x64" ], @@ -2746,9 +3534,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", - "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", "cpu": [ "x64" ], @@ -2759,9 +3547,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", - "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", "cpu": [ "x64" ], @@ -2772,9 +3560,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", - "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", "cpu": [ "arm64" ], @@ -2785,9 +3573,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", - "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", "cpu": [ "arm64" ], @@ -2798,9 +3586,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", - "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", "cpu": [ "ia32" ], @@ -2811,9 +3599,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", - "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", "cpu": [ "x64" ], @@ -2824,9 +3612,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", - "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", "cpu": [ "x64" ], @@ -2870,7 +3658,6 @@ "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -3029,14 +3816,6 @@ "@types/node": "*" } }, - "node_modules/@types/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/@types/lodash": { "version": "4.17.24", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", @@ -3050,14 +3829,6 @@ "license": "MIT", "optional": true }, - "node_modules/@types/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", @@ -3234,17 +4005,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", - "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.0.tgz", + "integrity": "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/type-utils": "8.57.2", - "@typescript-eslint/utils": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/type-utils": "8.57.0", + "@typescript-eslint/utils": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -3257,7 +4028,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.57.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -3273,17 +4044,17 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", - "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.0.tgz", + "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3" }, "engines": { @@ -3299,13 +4070,13 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", - "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", + "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.2", - "@typescript-eslint/types": "^8.57.2", + "@typescript-eslint/tsconfig-utils": "^8.57.0", + "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "engines": { @@ -3320,13 +4091,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", - "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", + "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2" + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3337,9 +4108,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", - "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", + "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3353,15 +4124,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", - "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.0.tgz", + "integrity": "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/utils": "8.57.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -3378,9 +4149,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", + "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3391,15 +4162,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", - "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", + "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.2", - "@typescript-eslint/tsconfig-utils": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/project-service": "8.57.0", + "@typescript-eslint/tsconfig-utils": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -3454,16 +4225,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", - "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz", + "integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2" + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3478,12 +4249,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", - "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", + "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -3776,31 +4547,34 @@ ] }, "node_modules/@vitejs/plugin-vue": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", - "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.5.tgz", + "integrity": "sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg==", "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.2" + }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "vue": "^3.2.25" } }, "node_modules/@volar/language-core": { - "version": "2.4.28", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", - "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.15.tgz", + "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==", "license": "MIT", "dependencies": { - "@volar/source-map": "2.4.28" + "@volar/source-map": "2.4.15" } }, "node_modules/@volar/source-map": { - "version": "2.4.28", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", - "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.15.tgz", + "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==", "license": "MIT" }, "node_modules/@volar/typescript": { @@ -3814,21 +4588,6 @@ "vscode-uri": "^3.0.8" } }, - "node_modules/@volar/typescript/node_modules/@volar/language-core": { - "version": "2.4.15", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.15.tgz", - "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==", - "license": "MIT", - "dependencies": { - "@volar/source-map": "2.4.15" - } - }, - "node_modules/@volar/typescript/node_modules/@volar/source-map": { - "version": "2.4.15", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.15.tgz", - "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==", - "license": "MIT" - }, "node_modules/@vue-macros/common": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-3.1.2.tgz", @@ -3837,63 +4596,23 @@ "dependencies": { "@vue/compiler-sfc": "^3.5.22", "ast-kit": "^2.1.2", - "local-pkg": "^1.1.2", - "magic-string-ast": "^1.0.2", - "unplugin-utils": "^0.3.0" - }, - "engines": { - "node": ">=20.19.0" - }, - "funding": { - "url": "https://github.com/sponsors/vue-macros" - }, - "peerDependencies": { - "vue": "^2.7.0 || ^3.2.25" - }, - "peerDependenciesMeta": { - "vue": { - "optional": true - } - } - }, - "node_modules/@vue-macros/common/node_modules/confbox": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", - "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", - "license": "MIT" - }, - "node_modules/@vue-macros/common/node_modules/local-pkg": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", - "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==", - "license": "MIT", - "dependencies": { - "mlly": "^1.7.4", - "pkg-types": "^2.3.0", - "quansync": "^0.2.11" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vue-macros/common/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT" - }, - "node_modules/@vue-macros/common/node_modules/pkg-types": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", - "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", - "license": "MIT", - "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", - "pathe": "^2.0.3" + "local-pkg": "^1.1.2", + "magic-string-ast": "^1.0.2", + "unplugin-utils": "^0.3.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/vue-macros" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.2.25" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } } }, "node_modules/@vue/compiler-core": { @@ -3924,7 +4643,6 @@ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz", "integrity": "sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==", "license": "MIT", - "peer": true, "dependencies": { "@babel/parser": "^7.29.0", "@vue/compiler-core": "3.5.30", @@ -3963,19 +4681,70 @@ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", "license": "MIT" }, + "node_modules/@vue/devtools-kit": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.1.0.tgz", + "integrity": "sha512-/NZlS4WtGIB54DA/z10gzk+n/V7zaqSzYZOVlg2CfdnpIKdB61bd7JDIMxf/zrtX41zod8E2/bbEBoW/d7x70Q==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.1.0", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "perfect-debounce": "^2.0.0" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.1.0.tgz", + "integrity": "sha512-h8uCb4Qs8UT8VdTT5yjY6tOJ//qH7EpxToixR0xqejR55t5OdISIg7AJ7eBkhBs8iu1qG5gY3QQNN1DF1EelAA==", + "license": "MIT" + }, "node_modules/@vue/language-core": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.2.6.tgz", - "integrity": "sha512-xYYYX3/aVup576tP/23sEUpgiEnujrENaoNRbaozC1/MA9I6EGFQRJb4xrt/MmUCAGlxTKL2RmT8JLTPqagCkg==", + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.12.tgz", + "integrity": "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==", "license": "MIT", "dependencies": { - "@volar/language-core": "2.4.28", + "@volar/language-core": "2.4.15", "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.5.0", - "alien-signals": "^3.0.0", + "alien-signals": "^1.0.3", + "minimatch": "^9.0.3", "muggle-string": "^0.4.1", - "path-browserify": "^1.0.1", - "picomatch": "^4.0.2" + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@vue/language-core/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@vue/reactivity": { @@ -4230,16 +4999,15 @@ } }, "node_modules/alien-signals": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.1.2.tgz", - "integrity": "sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz", + "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==", "license": "MIT" }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -4271,6 +5039,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -4281,9 +5050,10 @@ } }, "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -4494,12 +5264,6 @@ "url": "https://github.com/sponsors/sxzz" } }, - "node_modules/ast-kit/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT" - }, "node_modules/ast-walker-scope": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/ast-walker-scope/-/ast-walker-scope-0.8.3.tgz", @@ -4600,7 +5364,6 @@ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", "license": "MIT", - "optional": true, "engines": { "node": "*" } @@ -4609,6 +5372,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4623,15 +5387,13 @@ "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==", "license": "MIT" }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "funding": { + "url": "https://github.com/sponsors/antfu" } }, "node_modules/bluebird": { @@ -4710,30 +5472,6 @@ "node": ">=16.20.1" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -4870,12 +5608,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" - }, "node_modules/cli-color": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.4.tgz", @@ -5226,6 +5958,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -5310,30 +6051,6 @@ } } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -5447,6 +6164,64 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-cli": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-11.0.0.tgz", + "integrity": "sha512-r5pA8idbk7GFWuHEU7trSTflWcdBpQEK+Aw17UrSHjS6CReuhrrPcyC3zcQBPQvhArRHnBo/h6eLH1fkCvNlww==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.6", + "dotenv": "^17.1.0", + "dotenv-expand": "^12.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "dotenv": "cli.js" + } + }, + "node_modules/dotenv-expand": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz", + "integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -5474,6 +6249,12 @@ "stream-shift": "^1.0.2" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -5493,7 +6274,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "devOptional": true, "license": "MIT" }, "node_modules/encodeurl": { @@ -5510,14 +6290,15 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "license": "MIT", + "optional": true, "dependencies": { "once": "^1.4.0" } }, "node_modules/enhanced-resolve": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", - "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", + "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5813,6 +6594,7 @@ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -6642,15 +7424,6 @@ "node": ">=6" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, "node_modules/express": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", @@ -6740,8 +7513,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/extsprintf": { "version": "1.4.1", @@ -6752,18 +7524,13 @@ ], "license": "MIT" }, - "node_modules/farmhash": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/farmhash/-/farmhash-3.3.1.tgz", - "integrity": "sha512-XUizHanzlr/v7suBr/o85HSakOoWh6HKXZjFYl5C2+Gj0f0rkw+XTUZzrd9odDsgI9G5tRUcF4wSbKaX04T0DQ==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "node-addon-api": "^5.1.0", - "prebuild-install": "^7.1.2" - }, + "node_modules/farmhash-modern": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", + "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18.0.0" } }, "node_modules/fast-deep-equal": { @@ -6846,9 +7613,9 @@ "license": "BSD-3-Clause" }, "node_modules/fast-xml-builder": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", - "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.3.tgz", + "integrity": "sha512-1o60KoFw2+LWKQu3IdcfcFlGTW4dpqEWmjhYec6H82AYZU2TVBXep6tMl8Z1Y+wM+ZrzCwe3BZ9Vyd9N2rIvmg==", "funding": [ { "type": "github", @@ -6862,9 +7629,9 @@ } }, "node_modules/fast-xml-parser": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.5.tgz", - "integrity": "sha512-cK9c5I/DwIOI7/Q7AlGN3DuTdwN61gwSfL8rvuVPK+0mcCNHHGxRrpiFtaZZRfRMJL3Gl8B2AFlBG6qXf03w9A==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.4.tgz", + "integrity": "sha512-jE8ugADnYOBsu1uaoayVl1tVKAMNOXyjwvv2U6udEA2ORBhDooJDWoGxTkhd4Qn4yh59JVVt/pKXtjPwx9OguQ==", "dev": true, "funding": [ { @@ -6919,6 +7686,29 @@ } } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -7020,9 +7810,9 @@ } }, "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", + "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", "license": "ISC" }, "node_modules/follow-redirects": { @@ -7061,6 +7851,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", @@ -7077,6 +7883,18 @@ "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -7095,12 +7913,6 @@ "node": ">= 0.8" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" - }, "node_modules/fs-extra": { "version": "11.3.4", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", @@ -7302,9 +8114,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.13.7", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", - "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", "dev": true, "license": "MIT", "dependencies": { @@ -7333,12 +8145,6 @@ "node": ">=16" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "license": "MIT" - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -7527,11 +8333,10 @@ } }, "node_modules/google-logging-utils": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", - "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", "license": "Apache-2.0", - "optional": true, "engines": { "node": ">=14" } @@ -7696,6 +8501,12 @@ "node": ">=18.0.0" } }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, "node_modules/html-entities": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", @@ -7847,31 +8658,11 @@ }, "engines": { "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, "node_modules/iframe-resizer": { "version": "4.4.5", @@ -7897,6 +8688,13 @@ "node": ">= 4" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, "node_modules/immutable": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", @@ -8065,6 +8863,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -8193,7 +8992,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -8510,12 +9308,28 @@ "node": ">= 0.4" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "devOptional": true, "license": "MIT", + "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -8618,7 +9432,6 @@ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", "license": "MIT", - "optional": true, "dependencies": { "bignumber.js": "^9.0.0" } @@ -8899,6 +9712,255 @@ "node": ">= 0.8.0" } }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "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" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/limiter": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", @@ -8923,13 +9985,14 @@ } }, "node_modules/local-pkg": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", - "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", + "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==", "license": "MIT", "dependencies": { - "mlly": "^1.7.3", - "pkg-types": "^1.2.1" + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" }, "engines": { "node": ">=14" @@ -8938,6 +10001,23 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/local-pkg/node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "license": "MIT" + }, + "node_modules/local-pkg/node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, "node_modules/locate-path": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", @@ -9067,7 +10147,8 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "optional": true }, "node_modules/loose-envify": { "version": "1.4.0", @@ -9312,9 +10393,9 @@ } }, "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -9357,18 +10438,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -9396,6 +10465,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -9407,18 +10485,12 @@ }, "engines": { "node": ">=10" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "license": "MIT" + } }, "node_modules/mlly": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", - "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.1.tgz", + "integrity": "sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==", "license": "MIT", "dependencies": { "acorn": "^8.16.0", @@ -9427,12 +10499,6 @@ "ufo": "^1.6.3" } }, - "node_modules/mlly/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT" - }, "node_modules/mongodb": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.21.0.tgz", @@ -9546,12 +10612,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "license": "MIT" - }, "node_modules/napi-postinstall": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", @@ -9735,23 +10795,12 @@ "node": ">= 10.13" } }, - "node_modules/node-abi": { - "version": "3.89.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz", - "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==", - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", - "license": "MIT" + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT", + "optional": true }, "node_modules/node-adm": { "version": "0.9.1", @@ -9761,6 +10810,26 @@ "node": ">= 0.6.0" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-exports-info": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", @@ -9843,62 +10912,35 @@ } }, "node_modules/node-forge": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz", - "integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", + "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" } }, - "node_modules/node-gcm": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/node-gcm/-/node-gcm-1.1.4.tgz", - "integrity": "sha512-6Z3Ksmum3xsux/Ejwg2pn+yELvL13nIP5ZbdJDZupnipfP10xyPvJGt5jlB3pCrKkIzcTKL87OI0xsbbz8YkpA==", - "license": "MIT", - "dependencies": { - "axios": "~1.6.8", - "debug": "^3.1.0", - "lodash": "^4.17.21" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/node-gcm/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/node-pushnotifications": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/node-pushnotifications/-/node-pushnotifications-3.1.3.tgz", - "integrity": "sha512-TVjmP/W8Z+Aucc0llKjntp+DHIG690imnH9xuKBvXLEa+mO3EO9sRxdm2JUrISX2QmHXvpuTwJyucSILywNKog==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/node-pushnotifications/-/node-pushnotifications-5.0.1.tgz", + "integrity": "sha512-yf3AcmUduO4w0ydT9m5dVol/Q2Kwnrm4YU5NOttPCAROs1MZIIc+lhdwPI5yLJeK2ppR9am5tIsZ4Jh38kFABw==", "license": "MIT", "dependencies": { - "@parse/node-apn": "6.5.0", - "firebase-admin": "12.1.1", + "@parse/node-apn": "7.1.0", + "firebase-admin": "13.7.0", "node-adm": "0.9.1", - "node-gcm": "1.1.4", "web-push": "3.6.7", "wns": "0.5.4" }, "engines": { - "node": ">=14.x.x" + "node": ">=18.x.x" } }, "node_modules/node-pushnotifications/node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "license": "MIT", - "engines": { - "node": ">=14" - } + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", + "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", + "license": "MIT" }, "node_modules/node-pushnotifications/node_modules/@firebase/app-types": { "version": "0.9.3", @@ -9907,117 +10949,92 @@ "license": "Apache-2.0" }, "node_modules/node-pushnotifications/node_modules/@firebase/auth-interop-types": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz", - "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==", "license": "Apache-2.0" }, "node_modules/node-pushnotifications/node_modules/@firebase/component": { - "version": "0.6.10", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.10.tgz", - "integrity": "sha512-OsNbEKyz9iLZSmMUhsl6+kCADzte00iisJIRUspnUqvDCX+RSGZOBIqekukv/jN177ovjApBQNFaxSYIDc/SyQ==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.7.1.tgz", + "integrity": "sha512-mFzsm7CLHR60o08S23iLUY8m/i6kLpOK87wdEFPLhdlCahaxKmWOwSVGiWoENYSmFJJoDhrR3gKSCxz7ENdIww==", "license": "Apache-2.0", "dependencies": { - "@firebase/util": "1.10.1", + "@firebase/util": "1.14.0", "tslib": "^2.1.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/node-pushnotifications/node_modules/@firebase/database": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.9.tgz", - "integrity": "sha512-EkiPSKSu2TJJGtOjyISASf3UFpFJDil1lMbfqnxilfbmIsilvC8DzgjuLoYD+eOitcug4wtU9Fh1tt2vgBhskA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.1.1.tgz", + "integrity": "sha512-LwIXe8+mVHY5LBPulWECOOIEXDiatyECp/BOlu0gOhe+WOcKjWHROaCbLlkFTgHMY7RHr5MOxkLP/tltWAH3dA==", "license": "Apache-2.0", "dependencies": { - "@firebase/app-check-interop-types": "0.3.2", - "@firebase/auth-interop-types": "0.2.3", - "@firebase/component": "0.6.10", - "@firebase/logger": "0.4.3", - "@firebase/util": "1.10.1", + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.7.1", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", "faye-websocket": "0.11.4", "tslib": "^2.1.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/node-pushnotifications/node_modules/@firebase/database-compat": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.10.tgz", - "integrity": "sha512-x3baGMzEKG5BE5orwFRg+Zpaa33N9lZkcOFXoZSeN9Muw/Mx37stePZpa1YMpcAPqX3aDx1cSv55Nxh4ObgpUQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.1.tgz", + "integrity": "sha512-heAEVZ9Z8c8PnBUcmGh91JHX0cXcVa1yESW/xkLuwaX7idRFyLiN8sl73KXpR8ZArGoPXVQDanBnk6SQiekRCQ==", "license": "Apache-2.0", "dependencies": { - "@firebase/component": "0.6.10", - "@firebase/database": "1.0.9", - "@firebase/database-types": "1.0.6", - "@firebase/logger": "0.4.3", - "@firebase/util": "1.10.1", + "@firebase/component": "0.7.1", + "@firebase/database": "1.1.1", + "@firebase/database-types": "1.0.17", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.14.0", "tslib": "^2.1.0" - } - }, - "node_modules/node-pushnotifications/node_modules/@firebase/database-compat/node_modules/@firebase/app-types": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz", - "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==", - "license": "Apache-2.0" - }, - "node_modules/node-pushnotifications/node_modules/@firebase/database-compat/node_modules/@firebase/database-types": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.6.tgz", - "integrity": "sha512-sMI7IynSZBsyGbUugc8PKE1jwKbnvaieAz/RxuM57PZQNCi6Rteiviwcw/jqZOX6igqYJwXWZ3UzKOZo2nUDRA==", - "license": "Apache-2.0", - "dependencies": { - "@firebase/app-types": "0.9.2", - "@firebase/util": "1.10.1" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/node-pushnotifications/node_modules/@firebase/database-types": { - "version": "1.0.18", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.18.tgz", - "integrity": "sha512-yOY8IC2go9lfbVDMiy2ATun4EB2AFwocPaQADwMN/RHRUAZSM4rlAV7PGbWPSG/YhkJ2A9xQAiAENgSua9G5Fg==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.17.tgz", + "integrity": "sha512-4eWaM5fW3qEIHjGzfi3cf0Jpqi1xQsAdT6rSDE1RZPrWu8oGjgrq6ybMjobtyHQFgwGCykBm4YM89qDzc+uG/w==", "license": "Apache-2.0", "dependencies": { "@firebase/app-types": "0.9.3", - "@firebase/util": "1.15.0" - } - }, - "node_modules/node-pushnotifications/node_modules/@firebase/database-types/node_modules/@firebase/util": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.15.0.tgz", - "integrity": "sha512-AmWf3cHAOMbrCPG4xdPKQaj5iHnyYfyLKZxwz+Xf55bqKbpAmcYifB4jQinT2W9XhDRHISOoPyBOariJpCG6FA==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": ">=20.0.0" + "@firebase/util": "1.14.0" } }, "node_modules/node-pushnotifications/node_modules/@firebase/logger": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.3.tgz", - "integrity": "sha512-Th42bWJg18EF5bJwhRosn2M/eYxmbWCwXZr4hHX7ltO0SE3QLrpgiMKeRBR/NW7vJke7i0n3i8esbCW2s93qBw==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.0.tgz", + "integrity": "sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/node-pushnotifications/node_modules/@firebase/util": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.1.tgz", - "integrity": "sha512-AIhFnCCjM8FmCqSNlNPTuOk3+gpHC1RkeNUBLtPbcqGYpN5MxI5q7Yby+rxycweOZOCboDzfIj8WyaY4tpQG/g==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.14.0.tgz", + "integrity": "sha512-/gnejm7MKkVIXnSJGpc9L2CvvvzJvtDPeAEq5jAwgVlf/PeNxot+THx/bpD20wQ8uL5sz0xqgXy1nisOYMU+mw==", + "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/node-pushnotifications/node_modules/@google-cloud/firestore": { @@ -10088,6 +11105,49 @@ "node": ">=14" } }, + "node_modules/node-pushnotifications/node_modules/@google-cloud/storage/node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/node-pushnotifications/node_modules/@google-cloud/storage/node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/node-pushnotifications/node_modules/@google-cloud/storage/node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/node-pushnotifications/node_modules/@google-cloud/storage/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -10131,29 +11191,19 @@ "node": ">=6" } }, - "node_modules/node-pushnotifications/node_modules/@types/node": { - "version": "20.19.37", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", - "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, "node_modules/node-pushnotifications/node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", - "optional": true, "engines": { "node": ">= 14" } }, "node_modules/node-pushnotifications/node_modules/fast-xml-parser": { - "version": "5.5.9", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.9.tgz", - "integrity": "sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g==", + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.5.tgz", + "integrity": "sha512-NLY+V5NNbdmiEszx9n14mZBseJTC50bRq1VHsaxOmR72JDuZt+5J1Co+dC/4JPnyq+WrIHNM69r0sqf7BMb3Mg==", "funding": [ { "type": "github", @@ -10163,37 +11213,37 @@ "license": "MIT", "optional": true, "dependencies": { - "fast-xml-builder": "^1.1.4", - "path-expression-matcher": "^1.2.0", - "strnum": "^2.2.2" + "fast-xml-builder": "^1.1.3", + "path-expression-matcher": "^1.1.3", + "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "node_modules/node-pushnotifications/node_modules/firebase-admin": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.1.1.tgz", - "integrity": "sha512-Nuoxk//gaYrspS7TvwBINdGvFhh2QeiaWpRW6+PJ+tWyn2/CugBc7jKa1NaBg0AvhGSOXFOCIsXhzCzHA47Rew==", + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.7.0.tgz", + "integrity": "sha512-o3qS8zCJbApe7aKzkO2Pa380t9cHISqeSd3blqYTtOuUUUua3qZTLwNWgGUOss3td6wbzrZhiHIj3c8+fC046Q==", "license": "Apache-2.0", "dependencies": { - "@fastify/busboy": "^2.1.0", - "@firebase/database-compat": "^1.0.2", - "@firebase/database-types": "^1.0.0", - "@types/node": "^20.10.3", - "farmhash": "^3.3.1", + "@fastify/busboy": "^3.0.0", + "@firebase/database-compat": "^2.0.0", + "@firebase/database-types": "^1.0.6", + "farmhash-modern": "^1.1.0", + "fast-deep-equal": "^3.1.1", + "google-auth-library": "^10.6.1", "jsonwebtoken": "^9.0.0", "jwks-rsa": "^3.1.0", - "long": "^5.2.3", "node-forge": "^1.3.1", - "uuid": "^9.0.0" + "uuid": "^11.0.2" }, "engines": { - "node": ">=14" + "node": ">=18" }, "optionalDependencies": { - "@google-cloud/firestore": "^7.7.0", - "@google-cloud/storage": "^7.7.0" + "@google-cloud/firestore": "^7.11.0", + "@google-cloud/storage": "^7.19.0" } }, "node_modules/node-pushnotifications/node_modules/gaxios": { @@ -10220,14 +11270,174 @@ "license": "MIT", "optional": true, "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/node-pushnotifications/node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/node-pushnotifications/node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-pushnotifications/node_modules/gcp-metadata/node_modules/gaxios": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", + "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-pushnotifications/node_modules/gcp-metadata/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/node-pushnotifications/node_modules/gcp-metadata/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-pushnotifications/node_modules/google-auth-library": { + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.1.tgz", + "integrity": "sha512-5awwuLrzNol+pFDmKJd0dKtZ0fPLAtoA5p7YO4ODsDu6ONJUVqbYwvv8y2ZBO5MBNp9TJXigB19710kYpBPdtA==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "7.1.3", + "gcp-metadata": "8.1.2", + "google-logging-utils": "1.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-pushnotifications/node_modules/google-auth-library/node_modules/gaxios": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2", + "rimraf": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-pushnotifications/node_modules/google-auth-library/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/node-pushnotifications/node_modules/google-auth-library/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-pushnotifications/node_modules/google-gax": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz", + "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" }, "engines": { - "node": ">= 14" + "node": ">=14" } }, - "node_modules/node-pushnotifications/node_modules/gcp-metadata": { + "node_modules/node-pushnotifications/node_modules/google-gax/node_modules/gcp-metadata": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", @@ -10242,7 +11452,7 @@ "node": ">=14" } }, - "node_modules/node-pushnotifications/node_modules/google-auth-library": { + "node_modules/node-pushnotifications/node_modules/google-gax/node_modules/google-auth-library": { "version": "9.15.1", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", @@ -10260,30 +11470,30 @@ "node": ">=14" } }, - "node_modules/node-pushnotifications/node_modules/google-gax": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz", - "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==", + "node_modules/node-pushnotifications/node_modules/google-gax/node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", "license": "Apache-2.0", "optional": true, - "dependencies": { - "@grpc/grpc-js": "^1.10.9", - "@grpc/proto-loader": "^0.7.13", - "@types/long": "^4.0.0", - "abort-controller": "^3.0.0", - "duplexify": "^4.0.0", - "google-auth-library": "^9.3.0", - "node-fetch": "^2.7.0", - "object-hash": "^3.0.0", - "proto3-json-serializer": "^2.0.2", - "protobufjs": "^7.3.2", - "retry-request": "^7.0.0", - "uuid": "^9.0.1" - }, "engines": { "node": ">=14" } }, + "node_modules/node-pushnotifications/node_modules/google-gax/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/node-pushnotifications/node_modules/gtoken": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", @@ -10343,9 +11553,9 @@ } }, "node_modules/node-pushnotifications/node_modules/strnum": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.2.tgz", - "integrity": "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz", + "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==", "funding": [ { "type": "github", @@ -10372,11 +11582,32 @@ "node": ">=14" } }, - "node_modules/node-pushnotifications/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" + "node_modules/node-pushnotifications/node_modules/teeny-request/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/node-pushnotifications/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } }, "node_modules/node-pushnotifications/node_modules/yocto-queue": { "version": "0.1.0", @@ -10391,10 +11622,166 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/nodemon": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz", + "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^10.2.1", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/nodemon/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/nodemon/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nodemon/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/nodemon/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10530,6 +11917,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/ofetch": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", @@ -10675,6 +12072,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -10732,9 +12135,9 @@ } }, "node_modules/path-expression-matcher": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.0.tgz", - "integrity": "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", + "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==", "funding": [ { "type": "github", @@ -10773,6 +12176,28 @@ "dev": true, "license": "MIT" }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/path-to-regexp": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", @@ -10784,9 +12209,9 @@ } }, "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "license": "MIT" }, "node_modules/peowly": { @@ -10800,6 +12225,12 @@ "typescript": ">=5.8" } }, + "node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -10807,33 +12238,74 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/playwright": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", + "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" } }, - "node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" + "node_modules/playwright-core": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", + "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" } }, - "node_modules/pkg-types/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT" + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, "node_modules/possible-typed-array-names": { "version": "1.1.0", @@ -10887,33 +12359,6 @@ "node": ">=4" } }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -11141,15 +12586,12 @@ "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "license": "ISC" }, - "node_modules/pump": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", - "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" }, "node_modules/punycode": { "version": "1.4.1", @@ -11245,36 +12687,6 @@ "node": ">= 0.10" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -11287,6 +12699,7 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", + "optional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -11310,6 +12723,13 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/reconnecting-websocket": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz", + "integrity": "sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==", + "license": "MIT", + "peer": true + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -11473,6 +12893,66 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/roarr": { "version": "7.21.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-7.21.4.tgz", @@ -11487,10 +12967,49 @@ "node": ">=18.0" } }, + "node_modules/rolldown": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz", + "integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.115.0", + "@rolldown/pluginutils": "1.0.0-rc.9" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-x64": "1.0.0-rc.9", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.9", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.9", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.9", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.9", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz", + "integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==", + "license": "MIT" + }, "node_modules/rollup": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", - "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "license": "MIT", "peer": true, "dependencies": { @@ -11504,31 +13023,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.0", - "@rollup/rollup-android-arm64": "4.60.0", - "@rollup/rollup-darwin-arm64": "4.60.0", - "@rollup/rollup-darwin-x64": "4.60.0", - "@rollup/rollup-freebsd-arm64": "4.60.0", - "@rollup/rollup-freebsd-x64": "4.60.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", - "@rollup/rollup-linux-arm-musleabihf": "4.60.0", - "@rollup/rollup-linux-arm64-gnu": "4.60.0", - "@rollup/rollup-linux-arm64-musl": "4.60.0", - "@rollup/rollup-linux-loong64-gnu": "4.60.0", - "@rollup/rollup-linux-loong64-musl": "4.60.0", - "@rollup/rollup-linux-ppc64-gnu": "4.60.0", - "@rollup/rollup-linux-ppc64-musl": "4.60.0", - "@rollup/rollup-linux-riscv64-gnu": "4.60.0", - "@rollup/rollup-linux-riscv64-musl": "4.60.0", - "@rollup/rollup-linux-s390x-gnu": "4.60.0", - "@rollup/rollup-linux-x64-gnu": "4.60.0", - "@rollup/rollup-linux-x64-musl": "4.60.0", - "@rollup/rollup-openbsd-x64": "4.60.0", - "@rollup/rollup-openharmony-arm64": "4.60.0", - "@rollup/rollup-win32-arm64-msvc": "4.60.0", - "@rollup/rollup-win32-ia32-msvc": "4.60.0", - "@rollup/rollup-win32-x64-gnu": "4.60.0", - "@rollup/rollup-win32-x64-msvc": "4.60.0", + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" } }, @@ -11682,32 +13201,12 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, - "node_modules/sass": { - "version": "1.98.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.98.0.tgz", - "integrity": "sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A==", - "license": "MIT", - "optional": true, - "dependencies": { - "chokidar": "^4.0.0", - "immutable": "^5.1.5", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=14.0.0" - }, - "optionalDependencies": { - "@parcel/watcher": "^2.4.1" - } - }, "node_modules/sass-embedded": { "version": "1.98.0", "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.98.0.tgz", "integrity": "sha512-Do7u6iRb6K+lrllcTkB1BXcHwOxcKe3rEfOF/GcCLE2w3WpddakRAosJOHFUR37DpsvimQXEt5abs3NzUjEIqg==", "license": "MIT", + "peer": true, "dependencies": { "@bufbuild/protobuf": "^2.5.0", "colorjs.io": "^0.5.0", @@ -12298,49 +13797,29 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, "license": "MIT", "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" } }, "node_modules/source-map": { @@ -12443,6 +13922,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", + "optional": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -12451,7 +13931,21 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "devOptional": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -12564,7 +14058,19 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "devOptional": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -12586,9 +14092,9 @@ } }, "node_modules/strip-literal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", - "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", "license": "MIT", "dependencies": { "js-tokens": "^9.0.1" @@ -12671,9 +14177,9 @@ } }, "node_modules/tapable": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", - "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", "engines": { @@ -12684,34 +14190,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/tar-fs": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", - "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/tdigest": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", @@ -12803,16 +14281,6 @@ "node": ">=0.12" } }, - "node_modules/tinyexec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", - "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -12881,6 +14349,16 @@ "node": ">=0.6" } }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, "node_modules/tough-cookie": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", @@ -12923,9 +14401,9 @@ "license": "MIT" }, "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "license": "MIT", "engines": { "node": ">=18.12" @@ -12963,18 +14441,6 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/type": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", @@ -13138,16 +14604,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz", - "integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.0.tgz", + "integrity": "sha512-W8GcigEMEeB07xEZol8oJ26rigm3+bfPHxHvwbYUlu1fUDsGuQ7Hiskx5xGW/xM4USc9Ephe3jtv7ZYPQntHeA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.57.2", - "@typescript-eslint/parser": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2" + "@typescript-eslint/eslint-plugin": "8.57.0", + "@typescript-eslint/parser": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/utils": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -13212,6 +14678,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, "node_modules/underscore": { "version": "1.13.8", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", @@ -13240,25 +14713,28 @@ } }, "node_modules/unimport": { - "version": "3.14.6", - "resolved": "https://registry.npmjs.org/unimport/-/unimport-3.14.6.tgz", - "integrity": "sha512-CYvbDaTT04Rh8bmD8jz3WPmHYZRG/NnvYVzwD6V1YAlvvKROlAeNDUBhkBGzNav2RKaeuXvlWYaa1V4Lfi/O0g==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/unimport/-/unimport-5.7.0.tgz", + "integrity": "sha512-njnL6sp8lEA8QQbZrt+52p/g4X0rw3bnGGmUcJnt1jeG8+iiqO779aGz0PirCtydAIVcuTBRlJ52F0u46z309Q==", "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^5.1.4", - "acorn": "^8.14.0", + "acorn": "^8.16.0", "escape-string-regexp": "^5.0.0", "estree-walker": "^3.0.3", - "fast-glob": "^3.3.3", - "local-pkg": "^1.0.0", - "magic-string": "^0.30.17", - "mlly": "^1.7.4", - "pathe": "^2.0.1", - "picomatch": "^4.0.2", - "pkg-types": "^1.3.0", + "local-pkg": "^1.1.2", + "magic-string": "^0.30.21", + "mlly": "^1.8.0", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "pkg-types": "^2.3.0", "scule": "^1.3.0", - "strip-literal": "^2.1.1", - "unplugin": "^1.16.1" + "strip-literal": "^3.1.0", + "tinyglobby": "^0.2.15", + "unplugin": "^2.3.11", + "unplugin-utils": "^0.3.1" + }, + "engines": { + "node": ">=18.12.0" } }, "node_modules/unimport/node_modules/confbox": { @@ -13288,24 +14764,7 @@ "@types/estree": "^1.0.0" } }, - "node_modules/unimport/node_modules/local-pkg": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", - "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==", - "license": "MIT", - "dependencies": { - "mlly": "^1.7.4", - "pkg-types": "^2.3.0", - "quansync": "^0.2.11" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/unimport/node_modules/local-pkg/node_modules/pkg-types": { + "node_modules/unimport/node_modules/pkg-types": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", @@ -13316,12 +14775,6 @@ "pathe": "^2.0.3" } }, - "node_modules/unimport/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT" - }, "node_modules/universal-cookie": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.2.2.tgz", @@ -13351,40 +14804,41 @@ } }, "node_modules/unplugin": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz", - "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", + "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", "license": "MIT", "dependencies": { - "acorn": "^8.14.0", + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.12.0" } }, "node_modules/unplugin-auto-import": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/unplugin-auto-import/-/unplugin-auto-import-0.19.0.tgz", - "integrity": "sha512-W97gTDEWu/L1EcKCXY5Ni8bsMW1E9kv12wYQv3mYpd7zcFctXYlLKsqeva6sbCQbzS8t9AG/XdU5/WkEJKPlFw==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/unplugin-auto-import/-/unplugin-auto-import-21.0.0.tgz", + "integrity": "sha512-vWuC8SwqJmxZFYwPojhOhOXDb5xFhNNcEVb9K/RFkyk/3VnfaOjzitWN7v+8DEKpMjSsY2AEGXNgt6I0yQrhRQ==", "license": "MIT", "dependencies": { - "@antfu/utils": "^0.7.10", - "@rollup/pluginutils": "^5.1.3", - "local-pkg": "^0.5.1", - "magic-string": "^0.30.15", - "picomatch": "^4.0.2", - "unimport": "^3.14.5", - "unplugin": "^2.1.0" + "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" }, "engines": { - "node": ">=14" + "node": ">=20.19.0" }, "funding": { "url": "https://github.com/sponsors/antfu" }, "peerDependencies": { - "@nuxt/kit": "^3.2.2", + "@nuxt/kit": "^4.0.0", "@vueuse/core": "*" }, "peerDependenciesMeta": { @@ -13396,21 +14850,6 @@ } } }, - "node_modules/unplugin-auto-import/node_modules/unplugin": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", - "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "acorn": "^8.15.0", - "picomatch": "^4.0.3", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": ">=18.12.0" - } - }, "node_modules/unplugin-utils": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.1.tgz", @@ -13427,184 +14866,39 @@ "url": "https://github.com/sponsors/sxzz" } }, - "node_modules/unplugin-utils/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT" - }, "node_modules/unplugin-vue-components": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.28.0.tgz", - "integrity": "sha512-jiTGtJ3JsRFBjgvyilfrX7yUoGKScFgbdNw+6p6kEXU+Spf/rhxzgvdfuMcvhCcLmflB/dY3pGQshYBVGOUx7Q==", - "license": "MIT", - "dependencies": { - "@antfu/utils": "^0.7.10", - "@rollup/pluginutils": "^5.1.4", - "chokidar": "^3.6.0", - "debug": "^4.4.0", - "fast-glob": "^3.3.2", - "local-pkg": "^0.5.1", - "magic-string": "^0.30.15", - "minimatch": "^9.0.5", - "mlly": "^1.7.3", - "unplugin": "^2.1.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@babel/parser": "^7.15.8", - "@nuxt/kit": "^3.2.2", - "vue": "2 || 3" - }, - "peerDependenciesMeta": { - "@babel/parser": { - "optional": true - }, - "@nuxt/kit": { - "optional": true - } - } - }, - "node_modules/unplugin-vue-components/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/unplugin-vue-components/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/unplugin-vue-components/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/unplugin-vue-components/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/unplugin-vue-components/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/unplugin-vue-components/node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/unplugin-vue-components/node_modules/unplugin": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", - "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "acorn": "^8.15.0", - "picomatch": "^4.0.3", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/unplugin-vue-router": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/unplugin-vue-router/-/unplugin-vue-router-0.19.2.tgz", - "integrity": "sha512-u5dgLBarxE5cyDK/hzJGfpCTLIAyiTXGlo85COuD4Nssj6G7NxS+i9mhCWz/1p/ud1eMwdcUbTXehQe41jYZUA==", - "deprecated": "Merged into vuejs/router. Migrate: https://router.vuejs.org/guide/migration/v4-to-v5.html", + "version": "31.0.0", + "resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-31.0.0.tgz", + "integrity": "sha512-4ULwfTZTLuWJ7+S9P7TrcStYLsSRkk6vy2jt/WTfgUEUb0nW9//xxmrfhyHUEVpZ2UKRRwfRb8Yy15PDbVZf+Q==", "license": "MIT", "dependencies": { - "@babel/generator": "^7.28.5", - "@vue-macros/common": "^3.1.1", - "@vue/language-core": "^3.2.1", - "ast-walker-scope": "^0.8.3", "chokidar": "^5.0.0", - "json5": "^2.2.3", "local-pkg": "^1.1.2", "magic-string": "^0.30.21", "mlly": "^1.8.0", - "muggle-string": "^0.4.1", - "pathe": "^2.0.3", + "obug": "^2.1.1", "picomatch": "^4.0.3", - "scule": "^1.3.0", "tinyglobby": "^0.2.15", "unplugin": "^2.3.11", - "unplugin-utils": "^0.3.1", - "yaml": "^2.8.2" + "unplugin-utils": "^0.3.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" }, "peerDependencies": { - "@vue/compiler-sfc": "^3.5.17", - "vue-router": "^4.6.0" + "@nuxt/kit": "^3.2.2 || ^4.0.0", + "vue": "^3.0.0" }, "peerDependenciesMeta": { - "vue-router": { + "@nuxt/kit": { "optional": true } } }, - "node_modules/unplugin-vue-router/node_modules/chokidar": { + "node_modules/unplugin-vue-components/node_modules/chokidar": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", @@ -13619,47 +14913,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/unplugin-vue-router/node_modules/confbox": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", - "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", - "license": "MIT" - }, - "node_modules/unplugin-vue-router/node_modules/local-pkg": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", - "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==", - "license": "MIT", - "dependencies": { - "mlly": "^1.7.4", - "pkg-types": "^2.3.0", - "quansync": "^0.2.11" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/unplugin-vue-router/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT" - }, - "node_modules/unplugin-vue-router/node_modules/pkg-types": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", - "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", - "license": "MIT", - "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", - "pathe": "^2.0.3" - } - }, - "node_modules/unplugin-vue-router/node_modules/readdirp": { + "node_modules/unplugin-vue-components/node_modules/readdirp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", @@ -13672,21 +14926,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/unplugin-vue-router/node_modules/unplugin": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", - "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "acorn": "^8.15.0", - "picomatch": "^4.0.3", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": ">=18.12.0" - } - }, "node_modules/unrs-resolver": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", @@ -13801,12 +15040,14 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "devOptional": true, "license": "MIT" }, "node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -13910,6 +15151,7 @@ "resolved": "https://registry.npmjs.org/vite-plugin-vuetify/-/vite-plugin-vuetify-2.1.3.tgz", "integrity": "sha512-Q4SC/4TqbNvaZIFb9YsfBqkGlYHbJJJ6uU3CnRBZqLUF3s5eCMVZAaV4GkTbehIH/bhSj42lMXztOwc71u6rVw==", "license": "MIT", + "peer": true, "dependencies": { "@vuetify/loader-shared": "^2.1.2", "debug": "^4.3.3", @@ -14055,24 +15297,8 @@ "engines": { "node": ">= 16" }, - "funding": { - "url": "https://github.com/sponsors/kazupon" - } - }, - "node_modules/vue-router": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", - "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@vue/devtools-api": "^6.6.4" - }, - "funding": { - "url": "https://github.com/sponsors/posva" - }, - "peerDependencies": { - "vue": "^3.5.0" + "funding": { + "url": "https://github.com/sponsors/kazupon" } }, "node_modules/vue-tsc": { @@ -14091,75 +15317,6 @@ "typescript": ">=5.0.0" } }, - "node_modules/vue-tsc/node_modules/@volar/language-core": { - "version": "2.4.15", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.15.tgz", - "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==", - "license": "MIT", - "dependencies": { - "@volar/source-map": "2.4.15" - } - }, - "node_modules/vue-tsc/node_modules/@volar/source-map": { - "version": "2.4.15", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.15.tgz", - "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==", - "license": "MIT" - }, - "node_modules/vue-tsc/node_modules/@vue/language-core": { - "version": "2.2.12", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.12.tgz", - "integrity": "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==", - "license": "MIT", - "dependencies": { - "@volar/language-core": "2.4.15", - "@vue/compiler-dom": "^3.5.0", - "@vue/compiler-vue2": "^2.7.16", - "@vue/shared": "^3.5.0", - "alien-signals": "^1.0.3", - "minimatch": "^9.0.3", - "muggle-string": "^0.4.1", - "path-browserify": "^1.0.1" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/vue-tsc/node_modules/alien-signals": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz", - "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==", - "license": "MIT" - }, - "node_modules/vue-tsc/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/vue-tsc/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/vuetify": { "version": "3.12.3", "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.12.3.tgz", @@ -14229,6 +15386,15 @@ "node": ">= 14" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -14419,6 +15585,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -14482,9 +15666,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -14569,30 +15753,249 @@ "ui": { "version": "0.0.0", "dependencies": { - "@data-fair/frame": "^0.18.4", - "@data-fair/lib-vue": "^1.27.1", - "@data-fair/lib-vuetify": "^1.13.4", - "@intlify/unplugin-vue-i18n": "^5.3.1", + "@data-fair/frame": "^0.17.7", + "@data-fair/lib-vue": "^1.26.0", + "@data-fair/lib-vuetify": "^2.0.0", + "@intlify/unplugin-vue-i18n": "^11.0.7", "@koumoul/v-iframe": "^2.4.5", "@mdi/js": "^7.4.47", "@types/config": "^3.3.5", - "@types/debug": "^4.1.13", - "@vitejs/plugin-vue": "^5.2.4", + "@types/debug": "^4.1.12", + "@vitejs/plugin-vue": "^6.0.5", "debug": "^4.4.3", "iframe-resizer": "^4.4.5", "ofetch": "^1.5.1", - "sass-embedded": "^1.98.0", + "reconnecting-websocket": "^4.4.0", + "sass-embedded": "^1.97.3", "truncate-middle": "^1.0.6", - "unplugin-auto-import": "^0.19.0", - "unplugin-vue-components": "^0.28.0", - "unplugin-vue-router": "^0.19.2", - "vite": "^5.4.21", + "unplugin-auto-import": "^21.0.0", + "unplugin-vue-components": "^31.0.0", + "vite": "^8.0.0", "vite-plugin-vuetify": "^2.1.3", - "vue": "^3.5.30", - "vue-i18n": "^10.0.8", - "vue-router": "^4.6.4", - "vue-tsc": "^2.2.12", - "vuetify": "^3.12.3" + "vue": "^3.5.28", + "vue-i18n": "^10.0.3", + "vue-router": "^5.0.3", + "vue-tsc": "^2.0.29", + "vuetify": "^4.0.2" + } + }, + "ui/node_modules/@data-fair/lib-vuetify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@data-fair/lib-vuetify/-/lib-vuetify-2.0.0.tgz", + "integrity": "sha512-xfI15er5F/8FN+//iKHc8ZsaQmuI8XrpF1MH3FqBGOoPxSzjzNWW7HvGL4NvY5SHmTI5mrvCVcyCIteW+Ljgbw==", + "license": "MIT", + "dependencies": { + "@data-fair/lib-common-types": "^1.10.4", + "@mdi/js": "^7.4.47", + "@vueuse/core": "^14.0.0" + }, + "peerDependencies": { + "@data-fair/lib-vue": "^1.15.0", + "ofetch": "1", + "vue-i18n": "10 || 11", + "vuetify": "4" + } + }, + "ui/node_modules/@vue/devtools-api": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.1.0.tgz", + "integrity": "sha512-O44X57jjkLKbLEc4OgL/6fEPOOanRJU8kYpCE8qfKlV96RQZcdzrcLI5mxMuVRUeXhHKIHGhCpHacyCk0HyO4w==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.1.0" + } + }, + "ui/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "ui/node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "ui/node_modules/unplugin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.0.0.tgz", + "integrity": "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "ui/node_modules/vite": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz", + "integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==", + "license": "MIT", + "dependencies": { + "@oxc-project/runtime": "0.115.0", + "lightningcss": "^1.32.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.9", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.0.0-alpha.31", + "esbuild": "^0.27.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "ui/node_modules/vue-router": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.3.tgz", + "integrity": "sha512-nG1c7aAFac7NYj8Hluo68WyWfc41xkEjaR0ViLHCa3oDvTQ/nIuLJlXJX1NUPw/DXzx/8+OKMng045HHQKQKWw==", + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.28.6", + "@vue-macros/common": "^3.1.1", + "@vue/devtools-api": "^8.0.6", + "ast-walker-scope": "^0.8.3", + "chokidar": "^5.0.0", + "json5": "^2.2.3", + "local-pkg": "^1.1.2", + "magic-string": "^0.30.21", + "mlly": "^1.8.0", + "muggle-string": "^0.4.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "scule": "^1.3.0", + "tinyglobby": "^0.2.15", + "unplugin": "^3.0.0", + "unplugin-utils": "^0.3.1", + "yaml": "^2.8.2" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@pinia/colada": ">=0.21.2", + "@vue/compiler-sfc": "^3.5.17", + "pinia": "^3.0.4", + "vue": "^3.5.0" + }, + "peerDependenciesMeta": { + "@pinia/colada": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "pinia": { + "optional": true + } + } + }, + "ui/node_modules/vuetify": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-4.0.2.tgz", + "integrity": "sha512-klgSGmfXoLajdTuuxreilzDQjp0ojzL2U5v6Z3ZbMYtpihPPXT9rkd/FxWL3dIGevnWdgaP2Kpwoz6aS/MISDA==", + "license": "MIT", + "peer": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/johnleider" + }, + "peerDependencies": { + "typescript": ">=4.7", + "vite-plugin-vuetify": ">=2.1.0", + "vue": "^3.5.0", + "webpack-plugin-vuetify": ">=3.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vite-plugin-vuetify": { + "optional": true + }, + "webpack-plugin-vuetify": { + "optional": true + } } } } diff --git a/package.json b/package.json index 8c80cef..06180f7 100644 --- a/package.json +++ b/package.json @@ -7,18 +7,17 @@ "scripts": { "build-types": "df-build-types ./", "dev-api": "npm -w api run dev", - "dev-deps": "docker compose up -d --wait", + "dev-deps": "mkdir -p dev/logs && docker compose up 2>&1 | tee dev/logs/docker-compose.log", "stop-dev-deps": "docker compose stop", "dev-ui": "npm -w ui run dev", - "dev-zellij": "export DEV_SHELL=$(basename \"$SHELL\") && zellij --layout .zellij.kdl && rm data/zellij-deps-ready", + "dev-zellij": "dotenv -- zellij --layout .zellij.kdl", "dev-send-notifications": "NODE_ENV=development node --experimental-strip-types dev/scripts/send-notifications.ts", "lint": "eslint . && npm -w ui run lint", "lint-fix": "eslint --fix . && npm -w ui run lint-fix", - "test": "npm run test-base -- test-it/*.ts", - "test-base": "NODE_ENV=test node --experimental-strip-types --test-force-exit --test-concurrency=1 --test", + "test": "playwright test --max-failures=1", "check-types": "tsc && npm -w ui run check-types", "prepare": "husky || true", - "quality": "npm run dev-deps && npm run lint && npm run build-types && npm run check-types && npm -w ui run build && npm run test && npm audit --omit=dev --audit-level=critical" + "quality": "npm run lint && npm run build-types && npm run check-types && npm -w ui run build && npm run test && npm audit --omit=dev --audit-level=critical" }, "repository": { "type": "git", @@ -40,9 +39,12 @@ "devDependencies": { "@commitlint/config-conventional": "^19.8.1", "@data-fair/lib-node": "^2.12.1", + "@playwright/test": "^1.58.2", "@types/config": "^3.3.5", "@types/debug": "^4.1.13", "commitlint": "^19.8.1", + "dotenv": "^17.3.1", + "dotenv-cli": "^11.0.0", "eslint": "^9.39.4", "eslint-plugin-vue": "^9.33.0", "eslint-plugin-vuetify": "github:albanm/eslint-plugin-vuetify", @@ -50,12 +52,15 @@ "json-schema-to-typescript": "^11.0.5", "neostandard": "^0.12.2", "nock": "^13.5.6", + "nodemon": "^3.1.9", "tough-cookie": "^5.1.2", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "ws": "^8.20.0" }, "overrides": { "node-forge": "1.3.2", - "axios": "1.13.5" + "axios": "1.13.5", + "tinyexec": "1.0.2" }, "relativeDependencies": { "@data-fair/lib-express": "../lib/packages/express", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..8b6bb67 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,28 @@ +import { defineConfig, devices } from '@playwright/test' +import 'dotenv/config' + +export default defineConfig({ + testDir: './tests', + forbidOnly: !!process.env.CI, + retries: 0, + workers: 1, + reporter: process.env.CI ? 'github' : 'list', + + use: { + baseURL: `http://${process.env.DEV_HOST}:${process.env.NGINX_PORT}`, + trace: 'on-first-retry', + }, + + projects: [ + { name: 'unit', testMatch: /.*\.unit\.spec\.ts/ }, + { name: 'state-setup', testMatch: /state-setup\.ts/, teardown: 'state-teardown' }, + { name: 'state-teardown', testMatch: /state-teardown\.ts/ }, + { name: 'api', testMatch: /.*\.api\.spec\.ts/, dependencies: ['state-setup'] }, + { + name: 'e2e', + testMatch: /.*\.e2e\.spec\.ts/, + use: { ...devices['Desktop Chrome'] }, + dependencies: ['state-setup'], + }, + ] +}) diff --git a/security/vapid.json b/security/vapid.json deleted file mode 100644 index 2fef8cf..0000000 --- a/security/vapid.json +++ /dev/null @@ -1 +0,0 @@ -{"publicKey":"BKoAuesun71THjw9uURwnk_RmQux6IhjyPxWxDl0TEMD5O5XP-bjDmcdE9O6_4w7r0CHFfXe_7B2-0ULUMyRvIs","privateKey":"H4XjFOPjJ-jWGiQpMRW2ci-7GQ2QjbIa8c8XQpK55XQ"} diff --git a/test-it/01-events.ts b/test-it/01-events.ts deleted file mode 100644 index 350fee7..0000000 --- a/test-it/01-events.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { strict as assert } from 'node:assert' -import { it, describe, before, beforeEach, after } from 'node:test' -import { axios, axiosAuth, clean, startApiServer, stopApiServer, baseURL } from './utils/index.ts' - -const axAno = axios() -const axPush = axios({ params: { key: 'SECRET_EVENTS' }, baseURL: 'http://localhost:8082/events' }) -const user1 = await axiosAuth('user1@test.com') -const admin1 = await axiosAuth('admin1@test.com') - -describe('events', () => { - before(startApiServer) - beforeEach(clean) - after(stopApiServer) - - it('should reject posting from exterior', async () => { - await assert.rejects(axAno.post('/api/events', {}), { status: 421 }) - }) - - it('should reject posting with bad secret key', async () => { - await assert.rejects(axPush.post('/api/events', {}, { params: { key: 'badkey' } }), { status: 401 }) - }) - - it('should reject anonymous user', async () => { - await assert.rejects(axAno.get('/api/events'), { status: 401 }) - }) - - it('should send an event', async () => { - let res = await axPush.post('/api/events', [{ - date: new Date().toISOString(), - topic: { key: 'topic1' }, - title: 'a notification', - sender: { type: 'user', id: 'user1', name: 'User 1' } - }, { - date: new Date().toISOString(), - topic: { key: 'topic1' }, - title: 'another notification', - sender: { type: 'user', id: 'user1', name: 'User 1' } - }, { - date: new Date().toISOString(), - topic: { key: 'topic1' }, - title: 'anotherone', - sender: { type: 'user', id: 'user1', name: 'User 1' } - }]) - res = await admin1.get('/api/events') - assert.equal(res.data.results.length, 0) - res = await user1.get('/api/events') - assert.equal(res.data.results.length, 3) - res = await user1.get('/api/events?q=notification') - assert.equal(res.data.results.length, 2) - res = await user1.get('/api/events?q=test') - assert.equal(res.data.results.length, 0) - res = await user1.get('/api/events', { params: { size: 2 } }) - assert.equal(res.data.results.length, 2) - assert.ok(res.data.next) - const id1 = res.data.results[0]._id - res = await user1.get(res.data.next) - assert.equal(res.data.results.length, 1) - assert.notEqual(res.data.results[0]._id, id1) - assert.ok(!res.data.next) - }) - - it('should send an internationalized event', async () => { - let res = await axPush.post('/api/events', [{ - date: new Date().toISOString(), - topic: { key: 'topic1' }, - title: { en: 'an english notification', fr: 'une notification française' }, - sender: { type: 'user', id: 'user1', name: 'User 1' } - }]) - res = await admin1.get('/api/events') - assert.equal(res.data.results.length, 0) - res = await user1.get('/api/events') - assert.equal(res.data.results.length, 1) - assert.equal(res.data.results[0].title, 'une notification française') - res = await user1.get('/api/events?q=française') - assert.equal(res.data.results.length, 1) - user1.cookieJar.setCookie('i18n_lang=en', baseURL) - res = await user1.get('/api/events') - assert.equal(res.data.results.length, 1) - assert.equal(res.data.results[0].title, 'an english notification') - }) - - it('should send an event with same id twice', async () => { - let res = await axPush.post('/api/events', [{ - _id: 'test', - date: new Date().toISOString(), - topic: { key: 'topic1' }, - title: 'notif 1', - sender: { type: 'user', id: 'user1', name: 'User 1' } - }]) - res = await axPush.post('/api/events', [{ - _id: 'test', - date: new Date().toISOString(), - topic: { key: 'topic1' }, - title: 'notif 2', - sender: { type: 'user', id: 'user1', name: 'User 1' } - }]) - res = await user1.get('/api/events') - assert.equal(res.data.results.length, 1) - assert.equal(res.data.results[0].title, 'notif 1') - }) -}) diff --git a/test-it/03-identities-webhook.ts b/test-it/03-identities-webhook.ts deleted file mode 100644 index 6a4ced6..0000000 --- a/test-it/03-identities-webhook.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type { Subscription } from '../api/types/index.js' - -import { strict as assert } from 'node:assert' -import { describe, it, before, beforeEach, after } from 'node:test' -import { axios, axiosAuth, clean, startApiServer, stopApiServer } from './utils/index.ts' - -const axIdentities = axios({ params: { key: 'SECRET_IDENTITIES' }, baseURL: 'http://localhost:8082/events' }) -const user1 = await axiosAuth('user1@test.com') -const admin1 = await axiosAuth('admin1@test.com') - -describe('identities webhooks', () => { - before(startApiServer) - beforeEach(clean) - after(stopApiServer) - - it('should update recipient and sender name', async () => { - let subscription = (await user1.post('/api/subscriptions', { - topic: { key: 'topic1' }, - sender: { type: 'user', id: 'user1', name: 'User1' }, - visibility: 'public' - })).data - - await axIdentities.post('/api/identities/user/user1', { name: 'New name' }) - subscription = (await user1.get('/api/subscriptions/' + subscription._id)).data - assert.equal(subscription.recipient.name, 'New name') - assert.equal(subscription.sender.name, 'New name') - }) - - it('should remove deprecated private subscriptions', async () => { - const privateSubscription = (await user1.post('/api/subscriptions', { - topic: { key: 'topic1' }, - sender: { type: 'organization', id: 'orga1', name: 'Orga 1' }, - visibility: 'private' - })).data - assert.equal(privateSubscription.visibility, 'private') - - const publicSubscription = (await user1.post('/api/subscriptions', { - topic: { key: 'topic2' }, - sender: { type: 'organization', id: 'orga1', name: 'Orga 1' }, - visibility: 'public' - })).data - - const org2Subscription = (await user1.post('/api/subscriptions', { - topic: { key: 'topic3' }, - sender: { type: 'organization', id: 'orga2', name: 'Orga 2', department: 'dep1' }, - visibility: 'private' - })).data - assert.equal(org2Subscription.visibility, 'private') - - await axIdentities.post('/api/identities/user/user1', { name: 'New name', organizations: [{ id: 'orga2', role: 'user' }] }) - - let subscriptions = (await user1.get('/api/subscriptions')).data.results as Subscription[] - assert.ok(!subscriptions.find(s => s._id === privateSubscription._id)) - assert.ok(subscriptions.find(s => s._id === publicSubscription._id)) - assert.ok(subscriptions.find(s => s._id === org2Subscription._id)) - - await axIdentities.post('/api/identities/user/user1', { name: 'New name', organizations: [{ id: 'orga2', role: 'user', department: 'dep2' }] }) - subscriptions = (await user1.get('/api/subscriptions')).data.results - assert.ok(!subscriptions.find(s => s._id === org2Subscription._id)) - - const allRolesSubscription = (await admin1.post('/api/subscriptions', { - topic: { key: 'topic1' }, - sender: { type: 'organization', id: 'orga1', name: 'Orga 1' }, - visibility: 'private' - })).data - const adminSubscription = (await admin1.post('/api/subscriptions', { - topic: { key: 'topic1' }, - sender: { type: 'organization', id: 'orga1', name: 'Orga 1', role: 'admin' }, - visibility: 'private' - })).data - assert.equal(adminSubscription.visibility, 'private') - await axIdentities.post('/api/identities/user/admin1', { name: 'New name', organizations: [{ id: 'orga1', role: 'user' }] }) - subscriptions = (await admin1.get('/api/subscriptions')).data.results - assert.ok(subscriptions.find(s => s._id === allRolesSubscription._id)) - assert.ok(!subscriptions.find(s => s._id === adminSubscription._id)) - }) -}) diff --git a/test-it/utils/index.ts b/test-it/utils/index.ts deleted file mode 100644 index 252e0ec..0000000 --- a/test-it/utils/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { AxiosAuthOptions } from '@data-fair/lib-node/axios-auth.js' - -import { axiosBuilder } from '@data-fair/lib-node/axios.js' -import { axiosAuth as _axiosAuth } from '@data-fair/lib-node/axios-auth.js' -import mongo from '@data-fair/lib-node/mongo.js' - -const directoryUrl = 'http://localhost:5600/simple-directory' - -export const baseURL = 'http://localhost:5600/events' - -const axiosOpts = { baseURL } - -export const axios = (opts = {}) => axiosBuilder({ ...axiosOpts, ...opts }) - -export const axiosAuth = (opts: string | Omit) => { - opts = typeof opts === 'string' ? { email: opts } : opts - const password = opts.email === 'superadmin@test.com' ? 'superpasswd' : 'passwd' - return _axiosAuth({ ...opts, password, axiosOpts, directoryUrl }) -} - -export const clean = async () => { - for (const name of ['notifications', 'subscriptions', 'events', 'webhooks', 'webhook-subscriptions', 'pushSubscriptions']) { - await mongo.db.collection(name).deleteMany({}) - } -} - -export const startApiServer = async () => { - // Before tests - process.env.SUPPRESS_NO_CONFIG_WARNING = '1' - process.env.NODE_CONFIG_DIR = 'api/config/' - const apiServer = await import('../../api/src/server.ts') - await apiServer.start() -} - -export const stopApiServer = async () => { - const apiServer = await import('../../api/src/server.ts') - await apiServer.stop() -} diff --git a/tests/error-cases.api.spec.ts b/tests/error-cases.api.spec.ts new file mode 100644 index 0000000..1c65a3f --- /dev/null +++ b/tests/error-cases.api.spec.ts @@ -0,0 +1,141 @@ +import { test, expect } from '@playwright/test' +import { axios, axiosAuth, clean, devBaseURL } from './support/axios.ts' + +const axAno = axios() +const axPush = axios({ params: { key: 'SECRET_EVENTS' }, baseURL: devBaseURL }) +const user1 = await axiosAuth('test-user1') +const admin1 = await axiosAuth('test1-admin1') + +test.describe('error cases', () => { + test.beforeEach(clean) + + // --- Authentication errors --- + + test('should reject anonymous access to notifications', async () => { + await expect(axAno.get('/api/notifications')).rejects.toMatchObject({ status: 401 }) + }) + + test('should reject anonymous access to subscriptions', async () => { + await expect(axAno.get('/api/subscriptions')).rejects.toMatchObject({ status: 401 }) + }) + + test('should reject anonymous access to webhook-subscriptions', async () => { + await expect(axAno.get('/api/webhook-subscriptions')).rejects.toMatchObject({ status: 401 }) + }) + + test('should reject anonymous access to webhooks', async () => { + await expect(axAno.get('/api/webhooks')).rejects.toMatchObject({ status: 401 }) + }) + + test('should reject posting events without secret key', async () => { + await expect(axAno.post('/api/events', [])).rejects.toMatchObject({ status: 421 }) + }) + + test('should reject posting notifications without secret key', async () => { + await expect(axAno.post('/api/notifications', {})).rejects.toMatchObject({ status: 421 }) + }) + + // --- Validation errors --- + + test('should reject malformed events', async () => { + // missing required fields + try { + await axPush.post('/api/events', [{ foo: 'bar' }]) + expect(true).toBe(false) // should not reach here + } catch (err: any) { + expect(err.status).toBeGreaterThanOrEqual(400) + expect(err.status).toBeLessThan(500) + } + }) + + test('should reject events that are not an array', async () => { + try { + await axPush.post('/api/events', { topic: { key: 'topic1' }, title: 'test' }) + expect(true).toBe(false) // should not reach here + } catch (err: any) { + expect(err.status).toBeGreaterThanOrEqual(400) + expect(err.status).toBeLessThan(500) + } + }) + + test('should reject subscription with wrong recipient', async () => { + await expect(user1.post('/api/subscriptions', { + topic: { key: 'topic1' }, + recipient: { id: 'someone-else' }, + sender: { type: 'user', id: 'test-user1' } + })).rejects.toMatchObject({ status: 403 }) + }) + + // --- Event deduplication edge cases --- + + test('should handle rapid duplicate events gracefully', async () => { + // send the same event ID multiple times concurrently + const event = { + _id: 'dedup-test', + date: new Date().toISOString(), + topic: { key: 'topic1' }, + title: 'dedup test', + sender: { type: 'user', id: 'test-user1', name: 'User 1' } + } + await Promise.all([ + axPush.post('/api/events', [event]), + axPush.post('/api/events', [event]), + axPush.post('/api/events', [event]) + ]) + + const res = await user1.get('/api/events') + expect(res.data.results.length).toBe(1) + }) + + // --- Empty and boundary cases --- + + test('should handle empty events array', async () => { + const res = await axPush.post('/api/events', []) + expect(res.status).toBe(201) + }) + + test('should return empty results for user with no events', async () => { + const res = await user1.get('/api/events') + expect(res.data.results.length).toBe(0) + }) + + test('should return empty results for user with no notifications', async () => { + const res = await user1.get('/api/notifications') + expect(res.data.results.length).toBe(0) + expect(res.data.count).toBe(0) + }) + + test('should return empty results for admin with no subscriptions', async () => { + const res = await admin1.get('/api/subscriptions') + expect(res.data.results.length).toBe(0) + expect(res.data.count).toBe(0) + }) + + // --- Duplicate subscription --- + + test('should reject duplicate subscriptions', async () => { + const subscription = { + topic: { key: 'topic1' }, + sender: { type: 'user', id: 'test-user1' }, + visibility: 'public' + } + await user1.post('/api/subscriptions', subscription) + await expect(user1.post('/api/subscriptions', subscription)).rejects.toMatchObject({ status: 409 }) + }) + + // --- Notification deduplication --- + + test('should deduplicate notifications with same eventId', async () => { + const notif = { + eventId: 'notif-dedup', + topic: { key: 'topic1' }, + title: 'dedup notification', + recipient: { id: 'test-user1' } + } + await axPush.post('/api/notifications', notif) + await axPush.post('/api/notifications', notif) + + const res = await user1.get('/api/notifications') + expect(res.data.count).toBe(1) + }) +}) diff --git a/tests/events.api.spec.ts b/tests/events.api.spec.ts new file mode 100644 index 0000000..0f95899 --- /dev/null +++ b/tests/events.api.spec.ts @@ -0,0 +1,98 @@ +import { test, expect } from '@playwright/test' +import { axios, axiosAuth, clean, baseURL, devBaseURL } from './support/axios.ts' + +const axAno = axios() +const axPush = axios({ params: { key: 'SECRET_EVENTS' }, baseURL: devBaseURL }) +const user1 = await axiosAuth('test-user1') +const admin1 = await axiosAuth('test1-admin1') + +test.describe('events', () => { + test.beforeEach(clean) + + test('should reject posting from exterior', async () => { + await expect(axAno.post('/api/events', {})).rejects.toMatchObject({ status: 421 }) + }) + + test('should reject posting with bad secret key', async () => { + await expect(axPush.post('/api/events', {}, { params: { key: 'badkey' } })).rejects.toMatchObject({ status: 401 }) + }) + + test('should reject anonymous user', async () => { + await expect(axAno.get('/api/events')).rejects.toMatchObject({ status: 401 }) + }) + + test('should send an event', async () => { + let res = await axPush.post('/api/events', [{ + date: new Date().toISOString(), + topic: { key: 'topic1' }, + title: 'a notification', + sender: { type: 'user', id: 'test-user1', name: 'User 1' } + }, { + date: new Date().toISOString(), + topic: { key: 'topic1' }, + title: 'another notification', + sender: { type: 'user', id: 'test-user1', name: 'User 1' } + }, { + date: new Date().toISOString(), + topic: { key: 'topic1' }, + title: 'anotherone', + sender: { type: 'user', id: 'test-user1', name: 'User 1' } + }]) + res = await admin1.get('/api/events') + expect(res.data.results.length).toBe(0) + res = await user1.get('/api/events') + expect(res.data.results.length).toBe(3) + res = await user1.get('/api/events?q=notification') + expect(res.data.results.length).toBe(2) + res = await user1.get('/api/events?q=nonexistent') + expect(res.data.results.length).toBe(0) + res = await user1.get('/api/events', { params: { size: 2 } }) + expect(res.data.results.length).toBe(2) + expect(res.data.next).toBeTruthy() + const id1 = res.data.results[0]._id + res = await user1.get(res.data.next) + expect(res.data.results.length).toBe(1) + expect(res.data.results[0]._id).not.toBe(id1) + expect(res.data.next).toBeFalsy() + }) + + test('should send an internationalized event', async () => { + let res = await axPush.post('/api/events', [{ + date: new Date().toISOString(), + topic: { key: 'topic1' }, + title: { en: 'an english notification', fr: 'une notification française' }, + sender: { type: 'user', id: 'test-user1', name: 'User 1' } + }]) + res = await admin1.get('/api/events') + expect(res.data.results.length).toBe(0) + res = await user1.get('/api/events') + expect(res.data.results.length).toBe(1) + expect(res.data.results[0].title).toBe('une notification française') + res = await user1.get('/api/events?q=française') + expect(res.data.results.length).toBe(1) + user1.cookieJar.setCookie('i18n_lang=en', baseURL) + res = await user1.get('/api/events') + expect(res.data.results.length).toBe(1) + expect(res.data.results[0].title).toBe('an english notification') + }) + + test('should send an event with same id twice', async () => { + let res = await axPush.post('/api/events', [{ + _id: 'test', + date: new Date().toISOString(), + topic: { key: 'topic1' }, + title: 'notif 1', + sender: { type: 'user', id: 'test-user1', name: 'User 1' } + }]) + res = await axPush.post('/api/events', [{ + _id: 'test', + date: new Date().toISOString(), + topic: { key: 'topic1' }, + title: 'notif 2', + sender: { type: 'user', id: 'test-user1', name: 'User 1' } + }]) + res = await user1.get('/api/events') + expect(res.data.results.length).toBe(1) + expect(res.data.results[0].title).toBe('notif 1') + }) +}) diff --git a/tests/events.e2e.spec.ts b/tests/events.e2e.spec.ts new file mode 100644 index 0000000..aeab6d2 --- /dev/null +++ b/tests/events.e2e.spec.ts @@ -0,0 +1,26 @@ +import { expect } from '@playwright/test' +import { test } from './fixtures/login.ts' +import { axios, clean, devBaseURL } from './support/axios.ts' + +const axPush = axios({ headers: { 'x-secret-key': 'SECRET_EVENTS' }, baseURL: devBaseURL }) + +test.describe('Events UI', () => { + test.beforeEach(clean) + + test('shows empty state when no events', async ({ page, goToWithAuth }) => { + await goToWithAuth('/events/embed/events', 'test-user1') + await expect(page.getByText('Aucun résultat')).toBeVisible() + }) + + test('shows events after they are posted', async ({ page, goToWithAuth }) => { + await axPush.post('/api/events', [{ + date: new Date().toISOString(), + topic: { key: 'topic1' }, + title: 'My test event', + sender: { type: 'user', id: 'test-user1', name: 'User 1' } + }]) + + await goToWithAuth('/events/embed/events', 'test-user1') + await expect(page.getByText('My test event')).toBeVisible() + }) +}) diff --git a/tests/events.unit.spec.ts b/tests/events.unit.spec.ts new file mode 100644 index 0000000..6020909 --- /dev/null +++ b/tests/events.unit.spec.ts @@ -0,0 +1,238 @@ +import { test, expect } from '@playwright/test' +import { localizeProp, localizeEvent, getSubscriptionsFilter, cleanEvent, buildSearchTexts } from '../api/src/events/operations.ts' + +test.describe('localizeProp', () => { + test('returns string prop as-is', () => { + expect(localizeProp('hello', 'fr', 'fr')).toBe('hello') + }) + + test('returns matching locale from object', () => { + expect(localizeProp({ fr: 'bonjour', en: 'hello' }, 'en', 'fr')).toBe('hello') + }) + + test('falls back to defaultLocale when locale not found', () => { + expect(localizeProp({ fr: 'bonjour' }, 'en', 'fr')).toBe('bonjour') + }) +}) + +test.describe('localizeEvent', () => { + test('localizes title, body and htmlBody', () => { + const event: any = { + _id: 'e1', + title: { fr: 'Titre', en: 'Title' }, + body: { fr: 'Corps', en: 'Body' }, + htmlBody: { fr: 'Corps', en: 'Body' }, + topic: { key: 'k' }, + date: '2024-01-01', + visibility: 'private' + } + const localized = localizeEvent(event, 'en', 'fr') + expect(localized.title).toBe('Title') + expect(localized.body).toBe('Body') + expect(localized.htmlBody).toBe('Body') + }) + + test('handles string title', () => { + const event: any = { + _id: 'e1', + title: 'Simple', + topic: { key: 'k' }, + date: '2024-01-01', + visibility: 'private' + } + const localized = localizeEvent(event, 'en', 'fr') + expect(localized.title).toBe('Simple') + }) +}) + +test.describe('getSubscriptionsFilter', () => { + test('builds topic hierarchy filter', () => { + const event: any = { + topic: { key: 'a:b:c' }, + } + const filter = getSubscriptionsFilter(event) + expect(filter['topic.key']).toEqual({ $in: ['a', 'a:b', 'a:b:c'] }) + }) + + test('filters by sender', () => { + const event: any = { + topic: { key: 't' }, + sender: { type: 'organization', id: 'org1', name: 'Org' } + } + const filter = getSubscriptionsFilter(event) + expect(filter['sender.type']).toBe('organization') + expect(filter['sender.id']).toBe('org1') + expect(filter['sender.department']).toEqual({ $exists: false }) + }) + + test('filters by sender with department', () => { + const event: any = { + topic: { key: 't' }, + sender: { type: 'organization', id: 'org1', department: 'dep1' } + } + const filter = getSubscriptionsFilter(event) + expect(filter['sender.department']).toBe('dep1') + }) + + test('wildcard department does not filter', () => { + const event: any = { + topic: { key: 't' }, + sender: { type: 'organization', id: 'org1', department: '*' } + } + const filter = getSubscriptionsFilter(event) + expect(filter['sender.department']).toBeUndefined() + }) + + test('filters by sender role', () => { + const event: any = { + topic: { key: 't' }, + sender: { type: 'organization', id: 'org1', role: 'admin' } + } + const filter = getSubscriptionsFilter(event) + expect(filter['sender.role']).toBe('admin') + }) + + test('no sender requires sender absent', () => { + const event: any = { topic: { key: 't' } } + const filter = getSubscriptionsFilter(event) + expect(filter.sender).toEqual({ $exists: false }) + }) + + test('private visibility sets visibility filter', () => { + const event: any = { topic: { key: 't' }, visibility: 'private' } + const filter = getSubscriptionsFilter(event) + expect(filter.visibility).toBe('private') + }) + + test('subscribedRecipient sets recipient filter', () => { + const event: any = { + topic: { key: 't' }, + subscribedRecipient: { id: 'test-user1' } + } + const filter = getSubscriptionsFilter(event) + expect(filter['recipient.id']).toBe('test-user1') + }) +}) + +test.describe('cleanEvent', () => { + test('hides originator user from another org', () => { + const event: any = { + sender: { type: 'organization', id: 'org1' }, + originator: { + organization: { id: 'org-other', name: 'Other' }, + user: { id: 'u1', name: 'User 1' } + } + } + const session: any = { + account: { type: 'organization', id: 'org1' }, + organization: { id: 'org1' } + } + const result = cleanEvent(event, session) + expect(result.originator!.user).toBeUndefined() + }) + + test('keeps originator user from same org', () => { + const event: any = { + sender: { type: 'organization', id: 'org1' }, + originator: { + organization: { id: 'org1', name: 'Org' }, + user: { id: 'u1', name: 'User 1' } + } + } + const session: any = { + account: { type: 'organization', id: 'org1' }, + organization: { id: 'org1' } + } + const result = cleanEvent(event, session) + expect(result.originator!.user).toEqual({ id: 'u1', name: 'User 1' }) + }) + + test('anonymizes admin originator for non-admin viewer', () => { + const event: any = { + originator: { + user: { id: 'u1', name: 'Admin', admin: true } + } + } + const session: any = { + account: { type: 'user', id: 'u2' }, + user: { adminMode: false } + } + const result = cleanEvent(event, session) + expect(result.originator!.user).toEqual({ admin: true }) + }) + + test('keeps admin originator for admin viewer', () => { + const event: any = { + originator: { + user: { id: 'u1', name: 'Admin', admin: true } + } + } + const session: any = { + account: { type: 'user', id: 'u2' }, + user: { adminMode: true } + } + const result = cleanEvent(event, session) + expect(result.originator!.user).toEqual({ id: 'u1', name: 'Admin', admin: true }) + }) +}) + +test.describe('buildSearchTexts', () => { + test('builds search text for each locale', () => { + const event: any = { + _id: 'e1', + title: { fr: 'Titre', en: 'Title' }, + body: { fr: 'Corps', en: 'Body' }, + topic: { key: 'a:b', title: 'Topic' }, + date: '2024-01-01', + visibility: 'private', + _search: [], + sender: { type: 'user', id: 'test-user1', name: 'User 1' } + } + const result = buildSearchTexts(event, ['fr', 'en'], 'fr') + expect(result).toHaveLength(2) + expect(result[0].language).toBe('fr') + expect(result[0].text).toContain('Titre') + expect(result[0].text).toContain('Topic') + expect(result[0].text).toContain('test-user1') + expect(result[1].language).toBe('en') + expect(result[1].text).toContain('Title') + }) + + test('includes originator info when same org as sender', () => { + const event: any = { + _id: 'e1', + title: 'T', + topic: { key: 'k' }, + date: '2024-01-01', + visibility: 'private', + _search: [], + sender: { type: 'organization', id: 'org1' }, + originator: { + organization: { id: 'org1', name: 'Org 1' }, + user: { id: 'u1', name: 'User 1' } + } + } + const result = buildSearchTexts(event, ['fr'], 'fr') + expect(result[0].text).toContain('Org 1') + expect(result[0].text).toContain('User 1') + }) + + test('excludes user when originator is from different org', () => { + const event: any = { + _id: 'e1', + title: 'T', + topic: { key: 'k' }, + date: '2024-01-01', + visibility: 'private', + _search: [], + sender: { type: 'organization', id: 'org1' }, + originator: { + organization: { id: 'org-other', name: 'Other Org' }, + user: { id: 'u1', name: 'User 1' } + } + } + const result = buildSearchTexts(event, ['fr'], 'fr') + expect(result[0].text).toContain('Other Org') + expect(result[0].text).not.toContain('User 1') + }) +}) diff --git a/tests/fixtures/login.ts b/tests/fixtures/login.ts new file mode 100644 index 0000000..a88ba48 --- /dev/null +++ b/tests/fixtures/login.ts @@ -0,0 +1,35 @@ +import { test as base } from '@playwright/test' +import { withQuery } from 'ufo' + +const cookieCache = new Map>>() + +export const test = base.extend<{ + goToWithAuth: (url: string, user: string) => Promise +}>({ + page: async ({ page, context }, use) => { + await context.addCookies([{ + name: 'i18n_lang', + value: 'en', + domain: process.env.DEV_HOST || 'localhost', + path: '/', + }]) + await use(page) + }, + goToWithAuth: async ({ page, context }, use) => { + await use(async (url: string, user: string) => { + const cached = cookieCache.get(user) + if (cached) { + await context.addCookies(cached) + await page.goto(url) + } else { + await page.goto(withQuery('/simple-directory/login', { redirect: `http://${process.env.DEV_HOST}:${process.env.NGINX_PORT}${url}` })) + await page.fill('input[name="email"]', user + '@test.com') + await page.fill('input[name="password"]', 'passwd') + await page.getByText('login', { exact: true }).click() + await page.waitForURL(url) + const cookies = await context.cookies() + cookieCache.set(user, cookies) + } + }) + }, + }) diff --git a/tests/identities-webhook.api.spec.ts b/tests/identities-webhook.api.spec.ts new file mode 100644 index 0000000..b351ae6 --- /dev/null +++ b/tests/identities-webhook.api.spec.ts @@ -0,0 +1,74 @@ +import type { Subscription } from '../api/types/index.js' + +import { test, expect } from '@playwright/test' +import { axios, axiosAuth, clean, devBaseURL } from './support/axios.ts' + +const axIdentities = axios({ params: { key: 'SECRET_IDENTITIES' }, baseURL: devBaseURL }) +const user1 = await axiosAuth('test-user1') +const admin1 = await axiosAuth('test1-admin1') + +test.describe('identities webhooks', () => { + test.beforeEach(clean) + + test('should update recipient and sender name', async () => { + let subscription = (await user1.post('/api/subscriptions', { + topic: { key: 'topic1' }, + sender: { type: 'user', id: 'test-user1', name: 'User1' }, + visibility: 'public' + })).data + + await axIdentities.post('/api/identities/user/test-user1', { name: 'New name' }) + subscription = (await user1.get('/api/subscriptions/' + subscription._id)).data + expect(subscription.recipient.name).toBe('New name') + expect(subscription.sender.name).toBe('New name') + }) + + test('should remove deprecated private subscriptions', async () => { + const privateSubscription = (await user1.post('/api/subscriptions', { + topic: { key: 'topic1' }, + sender: { type: 'organization', id: 'test1', name: 'Test Organization 1' }, + visibility: 'private' + })).data + expect(privateSubscription.visibility).toBe('private') + + const publicSubscription = (await user1.post('/api/subscriptions', { + topic: { key: 'topic2' }, + sender: { type: 'organization', id: 'test1', name: 'Test Organization 1' }, + visibility: 'public' + })).data + + const org2Subscription = (await user1.post('/api/subscriptions', { + topic: { key: 'topic3' }, + sender: { type: 'organization', id: 'test2', name: 'Test Organization 2', department: 'dep1' }, + visibility: 'private' + })).data + expect(org2Subscription.visibility).toBe('private') + + await axIdentities.post('/api/identities/user/test-user1', { name: 'New name', organizations: [{ id: 'test2', role: 'user' }] }) + + let subscriptions = (await user1.get('/api/subscriptions')).data.results as Subscription[] + expect(subscriptions.find(s => s._id === privateSubscription._id)).toBeFalsy() + expect(subscriptions.find(s => s._id === publicSubscription._id)).toBeTruthy() + expect(subscriptions.find(s => s._id === org2Subscription._id)).toBeTruthy() + + await axIdentities.post('/api/identities/user/test-user1', { name: 'New name', organizations: [{ id: 'test2', role: 'user', department: 'dep2' }] }) + subscriptions = (await user1.get('/api/subscriptions')).data.results + expect(subscriptions.find(s => s._id === org2Subscription._id)).toBeFalsy() + + const allRolesSubscription = (await admin1.post('/api/subscriptions', { + topic: { key: 'topic1' }, + sender: { type: 'organization', id: 'test1', name: 'Test Organization 1' }, + visibility: 'private' + })).data + const adminSubscription = (await admin1.post('/api/subscriptions', { + topic: { key: 'topic1' }, + sender: { type: 'organization', id: 'test1', name: 'Test Organization 1', role: 'admin' }, + visibility: 'private' + })).data + expect(adminSubscription.visibility).toBe('private') + await axIdentities.post('/api/identities/user/test1-admin1', { name: 'New name', organizations: [{ id: 'test1', role: 'user' }] }) + subscriptions = (await admin1.get('/api/subscriptions')).data.results + expect(subscriptions.find(s => s._id === allRolesSubscription._id)).toBeTruthy() + expect(subscriptions.find(s => s._id === adminSubscription._id)).toBeFalsy() + }) +}) diff --git a/tests/notifications.e2e.spec.ts b/tests/notifications.e2e.spec.ts new file mode 100644 index 0000000..1560ca2 --- /dev/null +++ b/tests/notifications.e2e.spec.ts @@ -0,0 +1,39 @@ +import { expect } from '@playwright/test' +import { test } from './fixtures/login.ts' +import { axiosAuth, clean, devBaseURL, axios } from './support/axios.ts' + +const axPush = axios({ headers: { 'x-secret-key': 'SECRET_EVENTS' }, baseURL: devBaseURL }) + +test.describe('Notifications UI', () => { + test.beforeEach(clean) + + test('shows empty state when no notifications', async ({ page, goToWithAuth }) => { + await goToWithAuth('/events/embed/notifications', 'test-user1') + await expect(page.getByText('No notification')).toBeVisible() + }) + + test('shows notifications after events are sent with subscription', async ({ page, goToWithAuth }) => { + // create a subscription for test-user1 + const user1 = await axiosAuth('test-user1') + await user1.post('/api/subscriptions', { + topic: { key: 'topic1' }, + sender: { type: 'user', id: 'test-user1', name: 'User 1' }, + outputs: ['devices'] + }) + + // send an event matching the subscription + await axPush.post('/api/events', [{ + date: new Date().toISOString(), + topic: { key: 'topic1' }, + title: 'Test notification title', + sender: { type: 'user', id: 'test-user1', name: 'User 1' } + }]) + + // wait a bit for notification processing + await new Promise(resolve => setTimeout(resolve, 2000)) + + await goToWithAuth('/events/embed/notifications', 'test-user1') + await expect(page.getByText('Test notification title')).toBeVisible() + await expect(page.getByText('1 notification')).toBeVisible() + }) +}) diff --git a/tests/notifications.unit.spec.ts b/tests/notifications.unit.spec.ts new file mode 100644 index 0000000..180a0ad --- /dev/null +++ b/tests/notifications.unit.spec.ts @@ -0,0 +1,127 @@ +import { test, expect } from '@playwright/test' +import { prepareSubscriptionNotification } from '../api/src/notifications/operations.ts' + +const makeEvent = (overrides: any = {}) => ({ + _id: 'evt1', + title: 'Event Title', + topic: { key: 'topic1' }, + date: '2024-01-01', + visibility: 'private' as const, + ...overrides +}) + +const makeSubscription = (overrides: any = {}) => ({ + _id: 'sub1', + origin: 'https://example.com', + topic: { key: 'topic1' }, + recipient: { id: 'test-user1', name: 'User 1' }, + outputs: ['devices' as const], + visibility: 'private' as const, + created: { date: '2024-01-01' }, + updated: { date: '2024-01-01' }, + ...overrides +}) + +test.describe('prepareSubscriptionNotification', () => { + test('basic notification structure', () => { + const event = makeEvent() + const sub = makeSubscription() + const notif = prepareSubscriptionNotification(event, sub, {}, 'fr', 'notif1') + expect(notif._id).toBe('notif1') + expect(notif.eventId).toBe('evt1') + expect(notif.recipient).toEqual({ id: 'test-user1', name: 'User 1' }) + expect(notif.origin).toBe('https://example.com') + }) + + test('uses subscription icon', () => { + const notif = prepareSubscriptionNotification( + makeEvent(), makeSubscription({ icon: 'https://example.com/icon.png' }), + { notificationIcon: 'https://default.com/icon.png' }, 'fr', 'n1' + ) + expect(notif.icon).toBe('https://example.com/icon.png') + }) + + test('falls back to defaults.notificationIcon', () => { + const notif = prepareSubscriptionNotification( + makeEvent(), makeSubscription(), + { notificationIcon: 'https://default.com/icon.png' }, 'fr', 'n1' + ) + expect(notif.icon).toBe('https://default.com/icon.png') + }) + + test('falls back to origin-based icon', () => { + const notif = prepareSubscriptionNotification( + makeEvent(), makeSubscription(), + {}, 'fr', 'n1' + ) + expect(notif.icon).toBe('https://example.com/events/logo-192x192.png') + }) + + test('expands URL template', () => { + const event = makeEvent({ urlParams: { id: '123', type: 'dataset' } }) + const sub = makeSubscription({ urlTemplate: '/resources/{type}/{id}' }) + const notif = prepareSubscriptionNotification(event, sub, {}, 'fr', 'n1') + expect(notif.url).toBe('https://example.com/resources/dataset/123') + }) + + test('relative URL gets origin prepended', () => { + const sub = makeSubscription({ urlTemplate: '/page' }) + const notif = prepareSubscriptionNotification(makeEvent(), sub, {}, 'fr', 'n1') + expect(notif.url).toBe('https://example.com/page') + }) + + test('falls back topic title from subscription', () => { + const event = makeEvent({ topic: { key: 'k' } }) + const sub = makeSubscription({ topic: { key: 'k', title: 'Sub Topic' } }) + const notif = prepareSubscriptionNotification(event, sub, {}, 'fr', 'n1') + expect(notif.topic.title).toBe('Sub Topic') + }) + + test('uses topic title as fallback for notification title', () => { + const event = makeEvent({ title: { fr: '', en: '' }, topic: { key: 'k' } }) + const sub = makeSubscription({ topic: { key: 'k', title: 'Topic Title' } }) + const notif = prepareSubscriptionNotification(event, sub, {}, 'fr', 'n1') + expect(notif.title).toBe('Topic Title') + }) + + test('applies micro-template to body', () => { + const event = makeEvent({ body: 'Hello from {hostname}' }) + const sub = makeSubscription() + const notif = prepareSubscriptionNotification(event, sub, {}, 'fr', 'n1') + expect(notif.body).toBe('Hello from example.com') + }) + + test('applies micro-template to htmlBody', () => { + const event = makeEvent({ htmlBody: '

{origin}

' }) + const sub = makeSubscription() + const notif = prepareSubscriptionNotification(event, sub, {}, 'fr', 'n1') + expect(notif.htmlBody).toBe('

https://example.com

') + }) + + test('inherits outputs from subscription when not on event', () => { + const event = makeEvent() + const sub = makeSubscription({ outputs: ['email'] }) + const notif = prepareSubscriptionNotification(event, sub, {}, 'fr', 'n1') + expect(notif.outputs).toEqual(['email']) + }) + + test('removes resource, originator, urlParams from notification', () => { + const event = makeEvent({ + resource: { type: 'dataset', id: 'd1' }, + originator: { user: { id: 'u1' } }, + urlParams: { x: '1' } + }) + const sub = makeSubscription() + const notif = prepareSubscriptionNotification(event, sub, {}, 'fr', 'n1') + expect((notif as any).resource).toBeUndefined() + expect((notif as any).originator).toBeUndefined() + expect((notif as any).urlParams).toBeUndefined() + }) + + test('localizes event with subscription locale', () => { + const event = makeEvent({ title: { fr: 'Titre', en: 'Title' } }) + const sub = makeSubscription({ locale: 'en' }) + const notif = prepareSubscriptionNotification(event, sub, {}, 'fr', 'n1') + expect(notif.title).toBe('Title') + }) +}) diff --git a/tests/push.unit.spec.ts b/tests/push.unit.spec.ts new file mode 100644 index 0000000..c4b32ba --- /dev/null +++ b/tests/push.unit.spec.ts @@ -0,0 +1,42 @@ +import { test, expect } from '@playwright/test' +import { equalDeviceRegistrations } from '../api/src/push/operations.ts' + +test.describe('equalDeviceRegistrations', () => { + test('returns false for null first arg', () => { + expect(equalDeviceRegistrations(null, 'abc')).toBe(false) + }) + + test('returns false for null second arg', () => { + expect(equalDeviceRegistrations('abc', null)).toBe(false) + }) + + test('returns false for both null', () => { + expect(equalDeviceRegistrations(null, null)).toBe(false) + }) + + test('returns true for equal strings', () => { + expect(equalDeviceRegistrations('abc', 'abc')).toBe(true) + }) + + test('returns false for different strings', () => { + expect(equalDeviceRegistrations('abc', 'def')).toBe(false) + }) + + test('returns true for objects with same endpoint', () => { + expect(equalDeviceRegistrations( + { endpoint: 'https://push.example.com/1' }, + { endpoint: 'https://push.example.com/1' } + )).toBe(true) + }) + + test('returns false for objects with different endpoints', () => { + expect(equalDeviceRegistrations( + { endpoint: 'https://push.example.com/1' }, + { endpoint: 'https://push.example.com/2' } + )).toBe(false) + }) + + test('returns false for mixed string and object', () => { + expect(equalDeviceRegistrations('abc', { endpoint: 'abc' })).toBe(false) + }) +}) diff --git a/tests/seed.spec.ts b/tests/seed.spec.ts new file mode 100644 index 0000000..4e835b9 --- /dev/null +++ b/tests/seed.spec.ts @@ -0,0 +1,7 @@ +import { test } from '@playwright/test' + +test.describe('Test group', () => { + test('seed', async ({ page }) => { + // generate code here. + }) +}) diff --git a/tests/shared.unit.spec.ts b/tests/shared.unit.spec.ts new file mode 100644 index 0000000..c20afa4 --- /dev/null +++ b/tests/shared.unit.spec.ts @@ -0,0 +1,33 @@ +import { test, expect } from '@playwright/test' +import { backoffMinutes } from '../api/src/shared/operations.ts' + +test.describe('backoffMinutes', () => { + test('returns 1 for 1 error', () => { + expect(backoffMinutes(1)).toBe(1) + }) + + test('returns 6 for 2 errors', () => { + // Math.ceil(2^2.5) = Math.ceil(5.656) = 6 + expect(backoffMinutes(2)).toBe(6) + }) + + test('returns 16 for 3 errors', () => { + // Math.ceil(3^2.5) = Math.ceil(15.588) = 16 + expect(backoffMinutes(3)).toBe(16) + }) + + test('returns 32 for 4 errors', () => { + // Math.ceil(4^2.5) = Math.ceil(32) = 32 + expect(backoffMinutes(4)).toBe(32) + }) + + test('grows super-linearly', () => { + const b3 = backoffMinutes(3) + const b5 = backoffMinutes(5) + const b9 = backoffMinutes(9) + expect(b5).toBeGreaterThan(b3) + expect(b9).toBeGreaterThan(b5) + // ratio should increase + expect(b9 / b5).toBeGreaterThan(b5 / b3) + }) +}) diff --git a/tests/state-setup.ts b/tests/state-setup.ts new file mode 100644 index 0000000..7481f08 --- /dev/null +++ b/tests/state-setup.ts @@ -0,0 +1,17 @@ +import assert from 'node:assert/strict' +import { spawn } from 'child_process' +import { axiosBuilder } from '@data-fair/lib-node/axios.js' +import { test as setup } from '@playwright/test' + +const anonymousAx = axiosBuilder() + +setup('Stateful tests setup', async () => { + await assert.doesNotReject(anonymousAx.get(`http://localhost:${process.env.DEV_API_PORT}/api/ping`), + `Dev web server seems to be unavailable. +If you are agent do not try to start it. Instead check for a startup failure at the end of dev/logs/dev-api.log and report this problem to your user if there is no fixable startup failure in the log.`) + await assert.doesNotReject(anonymousAx.get(`http://${process.env.DEV_HOST}:${process.env.NGINX_PORT}/simple-directory`), + 'Simple Directory server seems to be unavailable. If you are agent do not try to fix this, instead report this problem to your user.') + + const tail = spawn('tail', ['-n', '0', '-f', 'dev/logs/dev-api.log'], { stdio: 'inherit', detached: true }) + process.env.TAIL_PID = tail.pid?.toString() +}) diff --git a/tests/state-teardown.ts b/tests/state-teardown.ts new file mode 100644 index 0000000..d9af52c --- /dev/null +++ b/tests/state-teardown.ts @@ -0,0 +1,7 @@ +import { test as teardown } from '@playwright/test' + +teardown('Stateful tests teardown', async () => { + if (process.env.TAIL_PID) { + try { process.kill(-Number(process.env.TAIL_PID)) } catch (e) { /* ignore */ } + } +}) diff --git a/test-it/02-subscriptions.ts b/tests/subscriptions.api.spec.ts similarity index 53% rename from test-it/02-subscriptions.ts rename to tests/subscriptions.api.spec.ts index da28cad..b08ef3e 100644 --- a/test-it/02-subscriptions.ts +++ b/tests/subscriptions.api.spec.ts @@ -1,211 +1,207 @@ -import { strict as assert } from 'node:assert' -import { it, describe, before, beforeEach, after } from 'node:test' +import { test, expect } from '@playwright/test' import WebSocket from 'ws' -import { axios, axiosAuth, clean, startApiServer, stopApiServer } from './utils/index.ts' +import { axios, axiosAuth, clean, devBaseURL } from './support/axios.ts' -const axPush = axios({ params: { key: 'SECRET_EVENTS' }, baseURL: 'http://localhost:8082/events' }) -const user1 = await axiosAuth('user1@test.com') -const user2 = await axiosAuth('user2@test.com') -const admin1 = await axiosAuth('admin1@test.com') +const axPush = axios({ params: { key: 'SECRET_EVENTS' }, baseURL: devBaseURL }) +const user1 = await axiosAuth('test-user1') +const user2 = await axiosAuth('test-user2') +const admin1 = await axiosAuth('test1-admin1') -describe('subscriptions', () => { - before(startApiServer) - beforeEach(clean) - after(stopApiServer) +test.describe('subscriptions', () => { + test.beforeEach(clean) - it('should reject wrong recipient', async () => { - await assert.rejects(user1.post('/api/subscriptions', { + test('should reject wrong recipient', async () => { + await expect(user1.post('/api/subscriptions', { topic: { key: 'topic1' }, recipient: { id: 'anotheruser' }, - sender: { type: 'user', id: 'user1' } - }), { status: 403 }) + sender: { type: 'user', id: 'test-user1' } + })).rejects.toMatchObject({ status: 403 }) }) - it('should send a public notification to any subscribed user', async () => { + test('should send a public notification to any subscribed user', async () => { const subscription = { topic: { key: 'topic1' }, - sender: { type: 'user', id: 'user1' }, + sender: { type: 'user', id: 'test-user1' }, visibility: 'public' } await admin1.post('/api/subscriptions', subscription) - await assert.rejects(admin1.post('/api/subscriptions', subscription), { status: 409 }) + await expect(admin1.post('/api/subscriptions', subscription)).rejects.toMatchObject({ status: 409 }) await user1.post('/api/subscriptions', subscription) await user2.post('/api/subscriptions', subscription) let res = await admin1.get('/api/subscriptions') - assert.equal(res.data.count, 1) - assert.equal(res.data.results[0].origin, 'http://localhost:5600') + expect(res.data.count).toBe(1) + expect(res.data.results[0].origin).toBe(`http://${process.env.DEV_HOST}:${process.env.NGINX_PORT}`) res = await axPush.post('/api/events', [{ date: new Date().toISOString(), topic: { key: 'topic1' }, title: 'a notification', body: 'a notification from host {hostname}', - sender: { type: 'user', id: 'user1', name: 'User 1' }, + sender: { type: 'user', id: 'test-user1', name: 'User 1' }, visibility: 'public' }]) res = await admin1.get('/api/notifications') - assert.equal(res.data.count, 1) - assert.equal(res.data.results[0].body, 'a notification from host localhost') + expect(res.data.count).toBe(1) + expect(res.data.results[0].body).toBe(`a notification from host ${process.env.DEV_HOST}`) res = await user1.get('/api/notifications') - assert.equal(res.data.count, 1) + expect(res.data.count).toBe(1) res = await user2.get('/api/notifications') - assert.equal(res.data.count, 1) + expect(res.data.count).toBe(1) }) - it('should send a private notification only to member of sender organization', async () => { + test('should send a private notification only to member of sender organization', async () => { const subscription = { topic: { key: 'topic1' }, - sender: { type: 'organization', id: 'orga1' } + sender: { type: 'organization', id: 'test1' } } let res = await admin1.post('/api/subscriptions', subscription) - assert.equal(res.data.visibility, 'private') + expect(res.data.visibility).toBe('private') res = await user1.post('/api/subscriptions', subscription) - assert.equal(res.data.visibility, 'private') + expect(res.data.visibility).toBe('private') res = await user2.post('/api/subscriptions', subscription) - assert.equal(res.data.visibility, 'public') + expect(res.data.visibility).toBe('public') res = await axPush.post('/api/events', [{ date: new Date().toISOString(), topic: { key: 'topic1' }, title: 'a notification', - sender: { type: 'organization', id: 'orga1', name: 'Orga 1' } + sender: { type: 'organization', id: 'test1', name: 'Test Organization 1' } }]) res = await admin1.get('/api/notifications') - assert.equal(res.data.count, 1) + expect(res.data.count).toBe(1) res = await user1.get('/api/notifications') - assert.equal(res.data.count, 1) + expect(res.data.count).toBe(1) res = await user2.get('/api/notifications') - assert.equal(res.data.count, 0) + expect(res.data.count).toBe(0) }) - it('should send a private notification only to member of sender organization with certain role', async () => { + test('should send a private notification only to member of sender organization with certain role', async () => { const subscription = { topic: { key: 'topic1' }, - sender: { type: 'organization', id: 'orga1', role: 'admin' } + sender: { type: 'organization', id: 'test1', role: 'admin' } } let res = await admin1.post('/api/subscriptions', subscription) - assert.equal(res.data.visibility, 'private') + expect(res.data.visibility).toBe('private') res = await user1.post('/api/subscriptions', subscription) - assert.equal(res.data.visibility, 'public') + expect(res.data.visibility).toBe('public') res = await axPush.post('/api/events', [{ date: new Date().toISOString(), topic: { key: 'topic1' }, title: 'a notification', - sender: { type: 'organization', id: 'orga1', role: 'admin', name: 'Orga 1' } + sender: { type: 'organization', id: 'test1', role: 'admin', name: 'Test Organization 1' } }]) res = await admin1.get('/api/notifications') - assert.equal(res.data.count, 1) + expect(res.data.count).toBe(1) res = await user1.get('/api/notifications') - assert.equal(res.data.count, 0) + expect(res.data.count).toBe(0) }) - it('should not send a global private notification to member of a department in sender organization', async () => { + test('should not send a global private notification to member of a department in sender organization', async () => { const subscription = { topic: { key: 'topic1' }, - sender: { type: 'organization', id: 'orga2' } + sender: { type: 'organization', id: 'test2' } } let res = await user2.post('/api/subscriptions', subscription) - // user2 is in a department, he only as access to public notification on the root of the org - assert.equal(res.data.visibility, 'public') + expect(res.data.visibility).toBe('public') res = await axPush.post('/api/events', [{ date: new Date().toISOString(), topic: { key: 'topic1' }, title: 'a notification', - sender: { type: 'organization', id: 'orga2', name: 'Orga 2' } + sender: { type: 'organization', id: 'test2', name: 'Test Organization 2' } }]) res = await user2.get('/api/notifications') - assert.equal(res.data.count, 0) + expect(res.data.count).toBe(0) }) - it('should send a notification to any department', async () => { + test('should send a notification to any department', async () => { let res = await user1.post('/api/subscriptions', { topic: { key: 'topic1' }, - sender: { type: 'organization', id: 'orga2', department: 'dep1', name: 'Orga 2' } + sender: { type: 'organization', id: 'test2', department: 'dep1', name: 'Test Organization 2' } }) - assert.equal(res.data.visibility, 'private') + expect(res.data.visibility).toBe('private') res = await user2.post('/api/subscriptions', { topic: { key: 'topic1' }, - sender: { type: 'organization', id: 'orga2', department: 'dep2', name: 'Orga 2' } + sender: { type: 'organization', id: 'test2', department: 'dep2', name: 'Test Organization 2' } }) - assert.equal(res.data.visibility, 'private') + expect(res.data.visibility).toBe('private') res = await axPush.post('/api/events', [{ date: new Date().toISOString(), topic: { key: 'topic1' }, title: 'a notification', - sender: { type: 'organization', id: 'orga2', department: '*', name: 'Orga 2' } + sender: { type: 'organization', id: 'test2', department: '*', name: 'Test Organization 2' } }]) res = await user2.get('/api/notifications') - assert.equal(res.data.count, 1) + expect(res.data.count).toBe(1) res = await user1.get('/api/notifications') - assert.equal(res.data.count, 1) + expect(res.data.count).toBe(1) }) - it('should send a private department notification to member of right department in sender organization', async () => { + test('should send a private department notification to member of right department in sender organization', async () => { let res = await user1.post('/api/subscriptions', { topic: { key: 'topic1' }, - sender: { type: 'organization', id: 'orga2', department: 'dep2' } + sender: { type: 'organization', id: 'test2', department: 'dep2' } }) - assert.equal(res.data.visibility, 'public') + expect(res.data.visibility).toBe('public') res = await user2.post('/api/subscriptions', { topic: { key: 'topic1' }, - sender: { type: 'organization', id: 'orga2', department: 'dep2' } + sender: { type: 'organization', id: 'test2', department: 'dep2' } }) - assert.equal(res.data.visibility, 'private') + expect(res.data.visibility).toBe('private') res = await axPush.post('/api/events', [{ date: new Date().toISOString(), topic: { key: 'topic1' }, title: 'a notification', - sender: { type: 'organization', id: 'orga2', department: 'dep2', name: 'Orga 2' } + sender: { type: 'organization', id: 'test2', department: 'dep2', name: 'Test Organization 2' } }]) res = await user2.get('/api/notifications') - assert.equal(res.data.count, 1) + expect(res.data.count).toBe(1) res = await user1.get('/api/notifications') - assert.equal(res.data.count, 0) + expect(res.data.count).toBe(0) }) - it('should send a notification with subscribedOnly option', async () => { + test('should send a notification with subscribedOnly option', async () => { const notif = { topic: { key: 'topic1' }, title: 'a notification', body: 'a notification from host {hostname}', - sender: { type: 'user', id: 'user1', name: 'User 1' }, + sender: { type: 'user', id: 'test-user1', name: 'User 1' }, visibility: 'public', - recipient: { id: 'admin1' } + recipient: { id: 'test1-admin1' } } let res = await axPush.post('/api/notifications', notif, { params: { subscribedOnly: 'true' } }) res = await admin1.get('/api/notifications') - assert.equal(res.data.count, 0) + expect(res.data.count).toBe(0) const subscription = { topic: { key: 'topic1' }, - sender: { type: 'user', id: 'user1' }, + sender: { type: 'user', id: 'test-user1' }, visibility: 'public' } await admin1.post('/api/subscriptions', subscription) res = await axPush.post('/api/notifications', notif, { params: { subscribedOnly: 'true' } }) res = await admin1.get('/api/notifications') - assert.equal(res.data.count, 1) - assert.equal(res.data.results[0].body, 'a notification from host localhost') + expect(res.data.count).toBe(1) + expect(res.data.results[0].body).toBe(`a notification from host ${process.env.DEV_HOST}`) }) - it('should send a global public notification to any subscribed user', async () => { + test('should send a global public notification to any subscribed user', async () => { const subscription = { topic: { key: 'global-topic1' }, visibility: 'public' } await admin1.post('/api/subscriptions', subscription) - await assert.rejects(admin1.post('/api/subscriptions', subscription), { status: 409 }) + await expect(admin1.post('/api/subscriptions', subscription)).rejects.toMatchObject({ status: 409 }) await user1.post('/api/subscriptions', subscription) await user2.post('/api/subscriptions', subscription) let res = await admin1.get('/api/subscriptions') - assert.equal(res.data.count, 1) - assert.equal(res.data.results[0].origin, 'http://localhost:5600') + expect(res.data.count).toBe(1) + expect(res.data.results[0].origin).toBe(`http://${process.env.DEV_HOST}:${process.env.NGINX_PORT}`) res = await axPush.post('/api/events', [{ date: new Date().toISOString(), @@ -215,29 +211,29 @@ describe('subscriptions', () => { visibility: 'public' }]) res = await admin1.get('/api/notifications') - assert.equal(res.data.count, 1) - assert.equal(res.data.results[0].body, 'a global notification from host localhost') + expect(res.data.count).toBe(1) + expect(res.data.results[0].body).toBe(`a global notification from host ${process.env.DEV_HOST}`) res = await user1.get('/api/notifications') - assert.equal(res.data.count, 1) + expect(res.data.count).toBe(1) res = await user2.get('/api/notifications') - assert.equal(res.data.count, 1) + expect(res.data.count).toBe(1) }) - it('should send notificationss of de-duplicated events', async () => { + test('should send notifications of de-duplicated events', async () => { // user1 is subscribed in 2 different manners let res = await user1.post('/api/subscriptions', { topic: { key: 'topic1' }, - sender: { type: 'organization', id: 'orga1' } + sender: { type: 'organization', id: 'test1' } }) res = await user1.post('/api/subscriptions', { topic: { key: 'topic2' }, - sender: { type: 'organization', id: 'orga1' } + sender: { type: 'organization', id: 'test1' } }) // admin is subscribed in the second manner only res = await admin1.post('/api/subscriptions', { topic: { key: 'topic2' }, - sender: { type: 'organization', id: 'orga1' } + sender: { type: 'organization', id: 'test1' } }) res = await axPush.post('/api/events', [{ @@ -245,20 +241,20 @@ describe('subscriptions', () => { date: new Date().toISOString(), topic: { key: 'topic1' }, title: 'notif 1', - sender: { type: 'organization', id: 'orga1', name: 'Orga 1' } + sender: { type: 'organization', id: 'test1', name: 'Test Organization 1' } }]) res = await axPush.post('/api/events', [{ _id: 'test', date: new Date().toISOString(), topic: { key: 'topic2' }, title: 'notif 2', - sender: { type: 'organization', id: 'orga1', name: 'Orga 1' } + sender: { type: 'organization', id: 'test1', name: 'Test Organization 1' } }]) res = await admin1.get('/api/notifications') - assert.equal(res.data.count, 1) + expect(res.data.count).toBe(1) // no duplicate created res = await user1.get('/api/notifications') - assert.equal(res.data.count, 1) + expect(res.data.count).toBe(1) // another notification sent straight to the user res = await axPush.post('/api/notifications', { @@ -266,10 +262,10 @@ describe('subscriptions', () => { date: new Date().toISOString(), topic: { key: 'topic2' }, title: 'notif 2', - recipient: { id: 'user1' } + recipient: { id: 'test-user1' } }) res = await user1.get('/api/notifications') - assert.equal(res.data.count, 2) + expect(res.data.count).toBe(2) // a duplicate not saved res = await axPush.post('/api/notifications', { @@ -277,15 +273,15 @@ describe('subscriptions', () => { date: new Date().toISOString(), topic: { key: 'topic2' }, title: 'notif 2', - recipient: { id: 'user1' } + recipient: { id: 'test-user1' } }) res = await user1.get('/api/notifications') - assert.equal(res.data.count, 2) + expect(res.data.count).toBe(2) }) - it('should deliver direct notifications via WS', async () => { - const cookies = user1.cookieJar.getCookiesSync('http://localhost:5600') - const ws = new WebSocket('ws://localhost:8082', { headers: { Cookie: cookies.map(String).join('; ') } }) + test('should deliver direct notifications via WS', async () => { + const cookies = user1.cookieJar.getCookiesSync(`http://${process.env.DEV_HOST}:${process.env.NGINX_PORT}`) + const ws = new WebSocket(`ws://localhost:${process.env.DEV_API_PORT}`, { headers: { Cookie: cookies.map(String).join('; ') } }) const messages: any[] = [] await new Promise((resolve, reject) => { @@ -294,53 +290,53 @@ describe('subscriptions', () => { if (msg.type === 'subscribe-confirm') resolve() if (msg.type === 'message') messages.push(msg) }) - ws.on('open', () => ws.send(JSON.stringify({ type: 'subscribe', channel: 'user:user1:notifications' }))) + ws.on('open', () => ws.send(JSON.stringify({ type: 'subscribe', channel: 'user:test-user1:notifications' }))) ws.on('error', reject) }) await axPush.post('/api/notifications', { topic: { key: 'topic1' }, - sender: { type: 'user', id: 'user1', name: 'User 1' }, + sender: { type: 'user', id: 'test-user1', name: 'User 1' }, title: 'notif direct 1', - recipient: { id: 'user1' } + recipient: { id: 'test-user1' } }) await new Promise(resolve => setTimeout(resolve, 200)) - assert.equal(messages.length, 1) - assert.equal(messages[0].data.title, 'notif direct 1') + expect(messages.length).toBe(1) + expect(messages[0].data.title).toBe('notif direct 1') await axPush.post('/api/notifications', { topic: { key: 'topic1' }, - sender: { type: 'user', id: 'user1', name: 'User 1' }, + sender: { type: 'user', id: 'test-user1', name: 'User 1' }, title: 'notif direct 2', - recipient: { id: 'user1' } + recipient: { id: 'test-user1' } }) await new Promise(resolve => setTimeout(resolve, 200)) - assert.equal(messages.length, 2) - assert.equal(messages[1].data.title, 'notif direct 2') + expect(messages.length).toBe(2) + expect(messages[1].data.title).toBe('notif direct 2') // a duplicate should not produce a WS message await axPush.post('/api/notifications', { eventId: 'ws-dedup', topic: { key: 'topic1' }, - sender: { type: 'user', id: 'user1', name: 'User 1' }, + sender: { type: 'user', id: 'test-user1', name: 'User 1' }, title: 'notif dedup', - recipient: { id: 'user1' } + recipient: { id: 'test-user1' } }) await new Promise(resolve => setTimeout(resolve, 200)) - assert.equal(messages.length, 3) + expect(messages.length).toBe(3) await axPush.post('/api/notifications', { eventId: 'ws-dedup', topic: { key: 'topic1' }, - sender: { type: 'user', id: 'user1', name: 'User 1' }, + sender: { type: 'user', id: 'test-user1', name: 'User 1' }, title: 'notif dedup', - recipient: { id: 'user1' } + recipient: { id: 'test-user1' } }) await new Promise(resolve => setTimeout(resolve, 200)) - assert.equal(messages.length, 3) + expect(messages.length).toBe(3) const res = await user1.get('/api/notifications') - assert.equal(res.data.count, 3) + expect(res.data.count).toBe(3) ws.close() }) diff --git a/tests/subscriptions.e2e.spec.ts b/tests/subscriptions.e2e.spec.ts new file mode 100644 index 0000000..3850d98 --- /dev/null +++ b/tests/subscriptions.e2e.spec.ts @@ -0,0 +1,26 @@ +import { expect } from '@playwright/test' +import { test } from './fixtures/login.ts' +import { axiosAuth, clean } from './support/axios.ts' + +test.describe('Subscriptions UI', () => { + test.beforeEach(clean) + + test('shows empty state when no subscriptions', async ({ page, goToWithAuth }) => { + await goToWithAuth('/events/embed/subscriptions', 'test-user1') + await expect(page.getByText('No subscription')).toBeVisible() + }) + + test('shows subscriptions after creating one via API', async ({ page, goToWithAuth }) => { + const user1 = await axiosAuth('test-user1') + await user1.post('/api/subscriptions', { + topic: { key: 'topic1' }, + title: 'My test subscription', + sender: { type: 'user', id: 'test-user1', name: 'User 1' }, + outputs: ['devices'] + }) + + await goToWithAuth('/events/embed/subscriptions', 'test-user1') + await expect(page.getByText('1 subscription')).toBeVisible() + await expect(page.getByText('My test subscription')).toBeVisible() + }) +}) diff --git a/tests/subscriptions.unit.spec.ts b/tests/subscriptions.unit.spec.ts new file mode 100644 index 0000000..5527689 --- /dev/null +++ b/tests/subscriptions.unit.spec.ts @@ -0,0 +1,79 @@ +import { test, expect } from '@playwright/test' +import { canSubscribePrivate } from '../api/src/subscriptions/operations.ts' + +const makeUser = (overrides: any = {}) => ({ + id: 'test-user1', + email: 'test-user1@test.com', + name: 'User 1', + organizations: [], + ...overrides +}) + +test.describe('canSubscribePrivate', () => { + test('admin mode always returns true', () => { + const user = makeUser({ adminMode: 1 }) + expect(canSubscribePrivate({ type: 'organization', id: 'any' }, user)).toBe(true) + }) + + test('returns false when no sender', () => { + expect(canSubscribePrivate(undefined, makeUser())).toBe(false) + }) + + test('user sender matches own id', () => { + expect(canSubscribePrivate({ type: 'user', id: 'test-user1' }, makeUser())).toBe(true) + }) + + test('user sender does not match other id', () => { + expect(canSubscribePrivate({ type: 'user', id: 'other' }, makeUser())).toBe(false) + }) + + test('org sender when user is member', () => { + const user = makeUser({ + organizations: [{ id: 'org1', name: 'Org', role: 'user' }] + }) + expect(canSubscribePrivate({ type: 'organization', id: 'org1' }, user)).toBe(true) + }) + + test('org sender when user is not member', () => { + const user = makeUser({ organizations: [] }) + expect(canSubscribePrivate({ type: 'organization', id: 'org1' }, user)).toBe(false) + }) + + test('org sender with department user belongs to', () => { + const user = makeUser({ + organizations: [ + { id: 'org1', name: 'Org', role: 'user' }, + { id: 'org1', name: 'Org', role: 'user', department: 'dep1' } + ] + }) + expect(canSubscribePrivate({ type: 'organization', id: 'org1', department: 'dep1' }, user)).toBe(true) + }) + + test('org sender with department falls back to org membership', () => { + const user = makeUser({ + organizations: [{ id: 'org1', name: 'Org', role: 'user' }] + }) + expect(canSubscribePrivate({ type: 'organization', id: 'org1', department: 'dep1' }, user)).toBe(true) + }) + + test('org sender with role user does not have', () => { + const user = makeUser({ + organizations: [{ id: 'org1', name: 'Org', role: 'user' }] + }) + expect(canSubscribePrivate({ type: 'organization', id: 'org1', role: 'admin' }, user)).toBe(false) + }) + + test('org sender with role user has', () => { + const user = makeUser({ + organizations: [{ id: 'org1', name: 'Org', role: 'admin' }] + }) + expect(canSubscribePrivate({ type: 'organization', id: 'org1', role: 'admin' }, user)).toBe(true) + }) + + test('org admin can subscribe to any role', () => { + const user = makeUser({ + organizations: [{ id: 'org1', name: 'Org', role: 'admin' }] + }) + expect(canSubscribePrivate({ type: 'organization', id: 'org1', role: 'contrib' }, user)).toBe(true) + }) +}) diff --git a/tests/support/axios.ts b/tests/support/axios.ts new file mode 100644 index 0000000..e90204e --- /dev/null +++ b/tests/support/axios.ts @@ -0,0 +1,26 @@ +import { axiosBuilder } from '@data-fair/lib-node/axios.js' +import { axiosAuth as _axiosAuth } from '@data-fair/lib-node/axios-auth.js' + +const devHost = process.env.DEV_HOST || 'localhost' +const nginxPort = process.env.NGINX_PORT || '5600' +const devApiPort = process.env.DEV_API_PORT || '5600' + +const directoryUrl = `http://${devHost}:${nginxPort}/simple-directory` + +export const baseURL = `http://${devHost}:${nginxPort}/events` +export const devBaseURL = `http://localhost:${devApiPort}` + +const axiosOpts = { baseURL } + +export const axios = (opts = {}) => axiosBuilder({ ...axiosOpts, ...opts }) + +const anonymousAx = axios() + +export const axiosAuth = (user: string, opts?: { adminMode?: boolean }) => { + const password = user === 'superadmin' ? 'superpasswd' : 'passwd' + return _axiosAuth({ email: user + '@test.com', password, adminMode: opts?.adminMode, axiosOpts, directoryUrl }) +} + +export const clean = async () => { + await anonymousAx.delete(`http://localhost:${devApiPort}/api/test-env`) +} diff --git a/tests/webhooks.api.spec.ts b/tests/webhooks.api.spec.ts new file mode 100644 index 0000000..ad46d36 --- /dev/null +++ b/tests/webhooks.api.spec.ts @@ -0,0 +1,199 @@ +import { test, expect } from '@playwright/test' +import { createServer } from 'node:http' +import { axios, axiosAuth, clean, devBaseURL } from './support/axios.ts' + +const axPush = axios({ params: { key: 'SECRET_EVENTS' }, baseURL: devBaseURL }) +const axDev = axios({ baseURL: devBaseURL }) +const admin1 = await axiosAuth('test1-admin1') + +// helper to post event matching a webhook subscription owned by test1-admin1/test1 +const postMatchingEvent = (title: string) => axPush.post('/api/events', [{ + date: new Date().toISOString(), + topic: { key: 'topic1' }, + title, + sender: { type: 'organization', id: 'test1', name: 'Test Organization 1' }, + visibility: 'public' +}]) + +test.describe('webhooks', () => { + test.beforeEach(clean) + + test('should create a webhook when event matches a webhook subscription', async () => { + await admin1.post('/api/webhook-subscriptions', { + title: 'Test webhook sub', + topic: { key: 'topic1' }, + sender: { type: 'organization', id: 'test1' }, + url: 'http://localhost:19876/hook' + }) + + await postMatchingEvent('a notification') + + const res = await admin1.get('/api/webhooks') + expect(res.data.count).toBe(1) + expect(res.data.results[0].status).toBe('waiting') + expect(res.data.results[0].notification.title).toBe('a notification') + expect(res.data.results[0].nbAttempts).toBe(0) + }) + + test('should deliver a webhook to a target URL', async () => { + const received: any[] = [] + const hookServer = createServer((req, res) => { + const chunks: Buffer[] = [] + req.on('data', (chunk) => { chunks.push(chunk) }) + req.on('end', () => { + received.push({ headers: req.headers, body: JSON.parse(Buffer.concat(chunks).toString()) }) + res.writeHead(200) + res.end() + }) + }) + await new Promise(resolve => hookServer.listen(19876, resolve)) + + try { + await admin1.post('/api/webhook-subscriptions', { + title: 'Test delivery', + topic: { key: 'topic1' }, + sender: { type: 'organization', id: 'test1' }, + url: 'http://localhost:19876/hook', + header: { key: 'X-Secret', value: 'mysecret' } + }) + + await postMatchingEvent('webhook delivery test') + + // wait for the webhook worker to process (polls every 4s) + await new Promise(resolve => setTimeout(resolve, 8000)) + + expect(received.length).toBe(1) + expect(received[0].body.title).toBe('webhook delivery test') + expect(received[0].headers['x-secret']).toBe('mysecret') + + const res = await admin1.get('/api/webhooks') + expect(res.data.results[0].status).toBe('ok') + expect(res.data.results[0].nbAttempts).toBe(1) + } finally { + hookServer.close() + } + }) + + test('should retry failed webhooks with backoff', async () => { + let callCount = 0 + const hookServer = createServer((req, res) => { + callCount++ + req.on('data', () => {}) + req.on('end', () => { + res.writeHead(500) + res.end('Internal Server Error') + }) + }) + await new Promise(resolve => hookServer.listen(19877, resolve)) + + try { + await admin1.post('/api/webhook-subscriptions', { + title: 'Retry test', + topic: { key: 'topic1' }, + sender: { type: 'organization', id: 'test1' }, + url: 'http://localhost:19877/hook' + }) + + await postMatchingEvent('retry test') + + // wait for the first attempt + await new Promise(resolve => setTimeout(resolve, 8000)) + + expect(callCount).toBe(1) + + const res = await admin1.get('/api/webhooks') + expect(res.data.results[0].status).toBe('error') + expect(res.data.results[0].nbAttempts).toBe(1) + expect(res.data.results[0].lastAttempt).toBeTruthy() + expect(res.data.results[0].nextAttempt).toBeTruthy() + } finally { + hookServer.close() + } + }) + + test('should stop retrying after 10 failed attempts', async () => { + // create a real webhook subscription and a failing server + const hookServer = createServer((req, res) => { + req.on('data', () => {}) + req.on('end', () => { + res.writeHead(500) + res.end('fail') + }) + }) + await new Promise(resolve => hookServer.listen(19878, resolve)) + + try { + const sub = (await admin1.post('/api/webhook-subscriptions', { + title: 'Max retry test', + topic: { key: 'topic1' }, + sender: { type: 'organization', id: 'test1' }, + url: 'http://localhost:19878/hook' + })).data + + // insert a webhook with 9 previous attempts so the next failure is the 10th + await axDev.post('/api/test-env/webhooks', { + _id: 'test-max-retries', + sender: { type: 'organization', id: 'test1' }, + owner: sub.owner, + subscription: { _id: sub._id, title: sub.title }, + notification: { + title: 'max retry test', + topic: { key: 'topic1' }, + date: new Date().toISOString() + }, + status: 'error', + nbAttempts: 9, + nextAttempt: new Date(Date.now() - 1000).toISOString() + }) + + await new Promise(resolve => setTimeout(resolve, 8000)) + + const webhook = (await axDev.get('/api/test-env/webhooks/test-max-retries')).data + expect(webhook?.status).toBe('error') + expect(webhook?.nbAttempts).toBe(10) + // no more retries scheduled + expect(webhook?.nextAttempt).toBeFalsy() + } finally { + hookServer.close() + } + }) + + test('should cancel a webhook', async () => { + await admin1.post('/api/webhook-subscriptions', { + title: 'Cancel test', + topic: { key: 'topic1' }, + sender: { type: 'organization', id: 'test1' }, + url: 'http://localhost:19879/hook' + }) + + await postMatchingEvent('cancel test') + + const list = await admin1.get('/api/webhooks') + expect(list.data.count).toBe(1) + const webhookId = list.data.results[0]._id + + const res = await admin1.post(`/api/webhooks/${webhookId}/_cancel`) + expect(res.data.status).toBe('cancelled') + }) + + test('should retry a webhook on demand', async () => { + await admin1.post('/api/webhook-subscriptions', { + title: 'Manual retry test', + topic: { key: 'topic1' }, + sender: { type: 'organization', id: 'test1' }, + url: 'http://localhost:19880/hook' + }) + + await postMatchingEvent('manual retry') + + // wait for the worker to try (will fail because no server is listening) + await new Promise(resolve => setTimeout(resolve, 8000)) + + const list = await admin1.get('/api/webhooks') + expect(list.data.results[0].status).toBe('error') + + const res = await admin1.post(`/api/webhooks/${list.data.results[0]._id}/_retry`) + expect(res.data.status).toBe('waiting') + expect(res.data.nbAttempts).toBe(0) + }) +}) diff --git a/ui/components.d.ts b/ui/components.d.ts index 3ade766..22596cf 100644 --- a/ui/components.d.ts +++ b/ui/components.d.ts @@ -1,7 +1,11 @@ /* eslint-disable */ // @ts-nocheck +// biome-ignore lint: disable +// oxlint-disable +// ------ // Generated by unplugin-vue-components // Read more: https://github.com/vuejs/core/pull/3399 + export {} /* prettier-ignore */ diff --git a/ui/dts/auto-imports.d.ts b/ui/dts/auto-imports.d.ts index 7260e75..a6f3ef4 100644 --- a/ui/dts/auto-imports.d.ts +++ b/ui/dts/auto-imports.d.ts @@ -6,131 +6,137 @@ // biome-ignore lint: disable export {} declare global { - const $apiPath: typeof import('~/context')['$apiPath'] - const $cspNonce: typeof import('~/context')['$cspNonce'] - const $fetch: typeof import('~/context')['$fetch'] - const $sitePath: typeof import('~/context')['$sitePath'] - const $uiConfig: typeof import('~/context')['$uiConfig'] - const EffectScope: typeof import('vue')['EffectScope'] - const computed: typeof import('vue')['computed'] - const computedDeepDiff: typeof import('@data-fair/lib-vue/deep-diff.js')['computedDeepDiff'] - const createApp: typeof import('vue')['createApp'] - const customRef: typeof import('vue')['customRef'] - const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] - const defineComponent: typeof import('vue')['defineComponent'] - const dfDateMatchFilter: typeof import('@data-fair/lib-vuetify/date-match-filter.vue')['default'] - const dfDateRangePicker: typeof import('@data-fair/lib-vuetify/date-range-picker.vue')['default'] - const dfLangSwitcher: typeof import('@data-fair/lib-vuetify/lang-switcher.vue')['default'] - const dfNavigationRight: typeof import('@data-fair/lib-vuetify/navigation-right.vue')['default'] - const dfOwnerAvatar: typeof import('@data-fair/lib-vuetify/owner-avatar.vue')['default'] - const dfOwnerPick: typeof import('@data-fair/lib-vuetify/owner-pick.vue')['default'] - const dfPersonalMenu: typeof import('@data-fair/lib-vuetify/personal-menu.vue')['default'] - const dfThemeSwitcher: typeof import('@data-fair/lib-vuetify/theme-switcher.vue')['default'] - const dfTutorialAlert: typeof import('@data-fair/lib-vuetify/tutorial-alert.vue')['default'] - const dfUiNotif: typeof import('@data-fair/lib-vuetify/ui-notif.vue')['default'] - const dfUiNotifAlert: typeof import('@data-fair/lib-vuetify/ui-notif-alert.vue')['default'] - const dfUserAvatar: typeof import('@data-fair/lib-vuetify/ui-user-avatar.vue')['default'] - const effectScope: typeof import('vue')['effectScope'] - const equalDeviceRegistrations: typeof import('../src/utils/registrations')['equalDeviceRegistrations'] - const getCurrentInstance: typeof import('vue')['getCurrentInstance'] - const getCurrentScope: typeof import('vue')['getCurrentScope'] - const h: typeof import('vue')['h'] - const inject: typeof import('vue')['inject'] - const isProxy: typeof import('vue')['isProxy'] - const isReactive: typeof import('vue')['isReactive'] - const isReadonly: typeof import('vue')['isReadonly'] - const isRef: typeof import('vue')['isRef'] - const markRaw: typeof import('vue')['markRaw'] - const mdiAlertCircle: typeof import('@mdi/js')['mdiAlertCircle'] - const mdiBell: typeof import('@mdi/js')['mdiBell'] + const $apiPath: typeof import('~/context').$apiPath + const $cspNonce: typeof import('~/context').$cspNonce + const $fetch: typeof import('~/context').$fetch + const $sitePath: typeof import('~/context').$sitePath + const $uiConfig: typeof import('~/context').$uiConfig + const EffectScope: typeof import('vue').EffectScope + const computed: typeof import('vue').computed + const computedDeepDiff: typeof import('@data-fair/lib-vue/deep-diff.js').computedDeepDiff + const createApp: typeof import('vue').createApp + const customRef: typeof import('vue').customRef + const defineAsyncComponent: typeof import('vue').defineAsyncComponent + const defineComponent: typeof import('vue').defineComponent + const dfDateMatchFilter: typeof import('@data-fair/lib-vuetify/date-match-filter.vue').default + const dfDateRangePicker: typeof import('@data-fair/lib-vuetify/date-range-picker.vue').default + const dfLangSwitcher: typeof import('@data-fair/lib-vuetify/lang-switcher.vue').default + const dfNavigationRight: typeof import('@data-fair/lib-vuetify/navigation-right.vue').default + const dfOwnerAvatar: typeof import('@data-fair/lib-vuetify/owner-avatar.vue').default + const dfOwnerPick: typeof import('@data-fair/lib-vuetify/owner-pick.vue').default + const dfPersonalMenu: typeof import('@data-fair/lib-vuetify/personal-menu.vue').default + const dfThemeSwitcher: typeof import('@data-fair/lib-vuetify/theme-switcher.vue').default + const dfTutorialAlert: typeof import('@data-fair/lib-vuetify/tutorial-alert.vue').default + const dfUiNotif: typeof import('@data-fair/lib-vuetify/ui-notif.vue').default + const dfUiNotifAlert: typeof import('@data-fair/lib-vuetify/ui-notif-alert.vue').default + const dfUserAvatar: typeof import('@data-fair/lib-vuetify/ui-user-avatar.vue').default + const effectScope: typeof import('vue').effectScope + const equalDeviceRegistrations: typeof import('../src/utils/registrations').equalDeviceRegistrations + const formatBytes: typeof import('@data-fair/lib-vue/format/bytes.js').formatBytes + const getCurrentInstance: typeof import('vue').getCurrentInstance + const getCurrentScope: typeof import('vue').getCurrentScope + const getCurrentWatcher: typeof import('vue').getCurrentWatcher + const h: typeof import('vue').h + const inject: typeof import('vue').inject + const isProxy: typeof import('vue').isProxy + const isReactive: typeof import('vue').isReactive + const isReadonly: typeof import('vue').isReadonly + const isRef: typeof import('vue').isRef + const isShallow: typeof import('vue').isShallow + const markRaw: typeof import('vue').markRaw + const mdiAlertCircle: typeof import('@mdi/js').mdiAlertCircle + const mdiBell: typeof import('@mdi/js').mdiBell const mdiCalendarRange: typeof import('@mdi/js')['mdiCalendarRange'] - const mdiCancel: typeof import('@mdi/js')['mdiCancel'] - const mdiCellphone: typeof import('@mdi/js')['mdiCellphone'] - const mdiCheckCircle: typeof import('@mdi/js')['mdiCheckCircle'] + const mdiCancel: typeof import('@mdi/js').mdiCancel + const mdiCellphone: typeof import('@mdi/js').mdiCellphone + const mdiCheckCircle: typeof import('@mdi/js').mdiCheckCircle const mdiDatabase: typeof import('@mdi/js')['mdiDatabase'] - const mdiDelete: typeof import('@mdi/js')['mdiDelete'] - const mdiDevices: typeof import('@mdi/js')['mdiDevices'] - const mdiDotsVertical: typeof import('@mdi/js')['mdiDotsVertical'] - const mdiEmail: typeof import('@mdi/js')['mdiEmail'] + const mdiDelete: typeof import('@mdi/js').mdiDelete + const mdiDevices: typeof import('@mdi/js').mdiDevices + const mdiDotsVertical: typeof import('@mdi/js').mdiDotsVertical + const mdiEmail: typeof import('@mdi/js').mdiEmail const mdiImageSizeSelectSmall: typeof import('@mdi/js')['mdiImageSizeSelectSmall'] - const mdiRefresh: typeof import('@mdi/js')['mdiRefresh'] - const mdiRss: typeof import('@mdi/js')['mdiRss'] - const mdiSend: typeof import('@mdi/js')['mdiSend'] - const mdiWeb: typeof import('@mdi/js')['mdiWeb'] - const nextTick: typeof import('vue')['nextTick'] - const onActivated: typeof import('vue')['onActivated'] - const onBeforeMount: typeof import('vue')['onBeforeMount'] - const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave'] - const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate'] - const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] - const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] - const onDeactivated: typeof import('vue')['onDeactivated'] - const onErrorCaptured: typeof import('vue')['onErrorCaptured'] - const onMounted: typeof import('vue')['onMounted'] - const onRenderTracked: typeof import('vue')['onRenderTracked'] - const onRenderTriggered: typeof import('vue')['onRenderTriggered'] - const onScopeDispose: typeof import('vue')['onScopeDispose'] - const onServerPrefetch: typeof import('vue')['onServerPrefetch'] - const onUnmounted: typeof import('vue')['onUnmounted'] - const onUpdated: typeof import('vue')['onUpdated'] - const onWatcherCleanup: typeof import('vue')['onWatcherCleanup'] - const parseSender: typeof import('../src/utils/sender-utils')['parseSender'] - const provide: typeof import('vue')['provide'] - const reactive: typeof import('vue')['reactive'] - const readonly: typeof import('vue')['readonly'] - const ref: typeof import('vue')['ref'] - const resolveComponent: typeof import('vue')['resolveComponent'] + const mdiRefresh: typeof import('@mdi/js').mdiRefresh + const mdiRss: typeof import('@mdi/js').mdiRss + const mdiSend: typeof import('@mdi/js').mdiSend + const mdiWeb: typeof import('@mdi/js').mdiWeb + const nextTick: typeof import('vue').nextTick + const onActivated: typeof import('vue').onActivated + const onBeforeMount: typeof import('vue').onBeforeMount + const onBeforeRouteLeave: typeof import('vue-router').onBeforeRouteLeave + const onBeforeRouteUpdate: typeof import('vue-router').onBeforeRouteUpdate + const onBeforeUnmount: typeof import('vue').onBeforeUnmount + const onBeforeUpdate: typeof import('vue').onBeforeUpdate + const onDeactivated: typeof import('vue').onDeactivated + const onErrorCaptured: typeof import('vue').onErrorCaptured + const onMounted: typeof import('vue').onMounted + const onRenderTracked: typeof import('vue').onRenderTracked + const onRenderTriggered: typeof import('vue').onRenderTriggered + const onScopeDispose: typeof import('vue').onScopeDispose + const onServerPrefetch: typeof import('vue').onServerPrefetch + const onUnmounted: typeof import('vue').onUnmounted + const onUpdated: typeof import('vue').onUpdated + const onWatcherCleanup: typeof import('vue').onWatcherCleanup + const parseSender: typeof import('../src/utils/sender-utils').parseSender + const provide: typeof import('vue').provide + const reactive: typeof import('vue').reactive + const readonly: typeof import('vue').readonly + const ref: typeof import('vue').ref + const resolveComponent: typeof import('vue').resolveComponent const sendUiNotif: typeof import('@data-fair/lib-vue/ui-notif.js')['sendUiNotif'] - const serializeSender: typeof import('../src/utils/sender-utils')['serializeSender'] - const shallowReactive: typeof import('vue')['shallowReactive'] - const shallowReadonly: typeof import('vue')['shallowReadonly'] - const shallowRef: typeof import('vue')['shallowRef'] + const serializeSender: typeof import('../src/utils/sender-utils').serializeSender + const shallowReactive: typeof import('vue').shallowReactive + const shallowReadonly: typeof import('vue').shallowReadonly + const shallowRef: typeof import('vue').shallowRef const subscribeWebhook: typeof import('../src/components/subscribe-webhook.vue')['default'] - const toRaw: typeof import('vue')['toRaw'] - const toRef: typeof import('vue')['toRef'] - const toRefs: typeof import('vue')['toRefs'] - const toValue: typeof import('vue')['toValue'] - const triggerRef: typeof import('vue')['triggerRef'] - const unref: typeof import('vue')['unref'] - const urlBase64ToUint8Array: typeof import('../src/utils/registrations')['urlBase64ToUint8Array'] - const useAsyncAction: typeof import('@data-fair/lib-vue/async-action.js')['useAsyncAction'] - const useAttrs: typeof import('vue')['useAttrs'] - const useBooleanSearchParam: typeof import('@data-fair/lib-vue/reactive-search-params.js')['useBooleanSearchParam'] - const useConceptFilters: typeof import('@data-fair/lib-vue/concept-filters.js')['useConceptFilters'] - const useCssModule: typeof import('vue')['useCssModule'] - const useCssVars: typeof import('vue')['useCssVars'] - const useFetch: typeof import('@data-fair/lib-vue/fetch.js')['useFetch'] - const useI18n: typeof import('vue-i18n')['useI18n'] - const useId: typeof import('vue')['useId'] - const useLink: typeof import('vue-router')['useLink'] - const useLocaleDayjs: typeof import('@data-fair/lib-vue/locale-dayjs.js')['useLocaleDayjs'] - const useModel: typeof import('vue')['useModel'] - const useNumberSearchParam: typeof import('@data-fair/lib-vue/reactive-search-params.js')['useNumberSearchParam'] - const useReactiveSearchParams: typeof import('@data-fair/lib-vue/reactive-search-params.js')['useReactiveSearchParams'] - const useRoute: typeof import('vue-router')['useRoute'] - const useRouter: typeof import('vue-router')['useRouter'] - const useSession: typeof import('@data-fair/lib-vue/session.js')['useSession'] - const useSessionAuthenticated: typeof import('@data-fair/lib-vue/session.js')['useSessionAuthenticated'] - const useSlots: typeof import('vue')['useSlots'] - const useStringSearchParam: typeof import('@data-fair/lib-vue/reactive-search-params.js')['useStringSearchParam'] - const useStringsArraySearchParam: typeof import('@data-fair/lib-vue/reactive-search-params.js')['useStringsArraySearchParam'] - const useSubscriptions: typeof import('../src/composables/use-subscriptions')['default'] - const useTemplateRef: typeof import('vue')['useTemplateRef'] - const useUiNotif: typeof import('@data-fair/lib-vue/ui-notif.js')['useUiNotif'] - const useWS: typeof import('@data-fair/lib-vue/ws.js')['useWS'] - const watch: typeof import('vue')['watch'] - const watchDeepDiff: typeof import('@data-fair/lib-vue/deep-diff.js')['watchDeepDiff'] - const watchEffect: typeof import('vue')['watchEffect'] - const watchPostEffect: typeof import('vue')['watchPostEffect'] - const watchSyncEffect: typeof import('vue')['watchSyncEffect'] - const withUiNotif: typeof import('@data-fair/lib-vue/ui-notif.js')['withUiNotif'] + const toRaw: typeof import('vue').toRaw + const toRef: typeof import('vue').toRef + const toRefs: typeof import('vue').toRefs + const toValue: typeof import('vue').toValue + const triggerRef: typeof import('vue').triggerRef + const unref: typeof import('vue').unref + const urlBase64ToUint8Array: typeof import('../src/utils/registrations').urlBase64ToUint8Array + const useAsyncAction: typeof import('@data-fair/lib-vue/async-action.js').useAsyncAction + const useAttrs: typeof import('vue').useAttrs + const useBooleanSearchParam: typeof import('@data-fair/lib-vue/reactive-search-params.js').useBooleanSearchParam + const useConceptFilters: typeof import('@data-fair/lib-vue/concept-filters.js').useConceptFilters + const useCssModule: typeof import('vue').useCssModule + const useCssVars: typeof import('vue').useCssVars + const useEditFetch: typeof import('@data-fair/lib-vue/edit-fetch.js').useEditFetch + const useFetch: typeof import('@data-fair/lib-vue/fetch.js').useFetch + const useI18n: typeof import('vue-i18n').useI18n + const useId: typeof import('vue').useId + const useLeaveGuard: typeof import('@data-fair/lib-vue/leave-guard.js').useLeaveGuard + const useLink: typeof import('vue-router').useLink + const useLocaleDayjs: typeof import('@data-fair/lib-vue/locale-dayjs.js').useLocaleDayjs + const useModel: typeof import('vue').useModel + const useNumberSearchParam: typeof import('@data-fair/lib-vue/reactive-search-params.js').useNumberSearchParam + const useReactiveSearchParams: typeof import('@data-fair/lib-vue/reactive-search-params.js').useReactiveSearchParams + const useRoute: typeof import('vue-router').useRoute + const useRouter: typeof import('vue-router').useRouter + const useSession: typeof import('@data-fair/lib-vue/session.js').useSession + const useSessionAuthenticated: typeof import('@data-fair/lib-vue/session.js').useSessionAuthenticated + const useSlots: typeof import('vue').useSlots + const useStringSearchParam: typeof import('@data-fair/lib-vue/reactive-search-params.js').useStringSearchParam + const useStringsArraySearchParam: typeof import('@data-fair/lib-vue/reactive-search-params.js').useStringsArraySearchParam + const useSubscriptions: typeof import('../src/composables/use-subscriptions').default + const useTemplateRef: typeof import('vue').useTemplateRef + const useUiNotif: typeof import('@data-fair/lib-vue/ui-notif.js').useUiNotif + const useWS: typeof import('@data-fair/lib-vue/ws.js').useWS + const watch: typeof import('vue').watch + const watchDeepDiff: typeof import('@data-fair/lib-vue/deep-diff.js').watchDeepDiff + const watchEffect: typeof import('vue').watchEffect + const watchPostEffect: typeof import('vue').watchPostEffect + const watchSyncEffect: typeof import('vue').watchSyncEffect + const withUiNotif: typeof import('@data-fair/lib-vue/ui-notif.js').withUiNotif } // for type re-export declare global { // @ts-ignore - export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue' + export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' import('vue') } + // for vue template auto import import { UnwrapRef } from 'vue' declare module 'vue' { @@ -162,14 +168,17 @@ declare module 'vue' { readonly dfUserAvatar: UnwrapRef readonly effectScope: UnwrapRef readonly equalDeviceRegistrations: UnwrapRef + readonly formatBytes: UnwrapRef readonly getCurrentInstance: UnwrapRef readonly getCurrentScope: UnwrapRef + readonly getCurrentWatcher: UnwrapRef readonly h: UnwrapRef readonly inject: UnwrapRef readonly isProxy: UnwrapRef readonly isReactive: UnwrapRef readonly isReadonly: UnwrapRef readonly isRef: UnwrapRef + readonly isShallow: UnwrapRef readonly markRaw: UnwrapRef readonly mdiAlertCircle: UnwrapRef readonly mdiBell: UnwrapRef @@ -224,9 +233,11 @@ declare module 'vue' { readonly useConceptFilters: UnwrapRef readonly useCssModule: UnwrapRef readonly useCssVars: UnwrapRef + readonly useEditFetch: UnwrapRef readonly useFetch: UnwrapRef readonly useI18n: UnwrapRef readonly useId: UnwrapRef + readonly useLeaveGuard: UnwrapRef readonly useLink: UnwrapRef readonly useLocaleDayjs: UnwrapRef readonly useModel: UnwrapRef @@ -250,4 +261,4 @@ declare module 'vue' { readonly watchSyncEffect: UnwrapRef readonly withUiNotif: UnwrapRef } -} +} \ No newline at end of file diff --git a/ui/package.json b/ui/package.json index 48c2aa0..6332c93 100644 --- a/ui/package.json +++ b/ui/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "NODE_CONFIG_DIR=../api/config/ vite --port 6220", + "dev": "mkdir -p ../dev/logs && NODE_CONFIG_DIR=../api/config/ vite --port $DEV_UI_PORT 2>&1 | ts '[%Y-%m-%d %H:%M:%S]' | tee ../dev/logs/dev-ui.log", "build": "vue-tsc -b && vite build", "preview": "vite preview", "check-types": "vue-tsc", @@ -12,29 +12,29 @@ "lint-fix": "eslint --fix ." }, "dependencies": { - "@data-fair/frame": "^0.18.4", - "@data-fair/lib-vue": "^1.27.1", - "@data-fair/lib-vuetify": "^1.13.4", - "@intlify/unplugin-vue-i18n": "^5.3.1", + "@data-fair/frame": "^0.17.7", + "@data-fair/lib-vue": "^1.26.0", + "@data-fair/lib-vuetify": "^2.0.0", + "@intlify/unplugin-vue-i18n": "^11.0.7", "@koumoul/v-iframe": "^2.4.5", "@mdi/js": "^7.4.47", "@types/config": "^3.3.5", - "@types/debug": "^4.1.13", - "@vitejs/plugin-vue": "^5.2.4", + "@types/debug": "^4.1.12", + "@vitejs/plugin-vue": "^6.0.5", "debug": "^4.4.3", "iframe-resizer": "^4.4.5", "ofetch": "^1.5.1", - "sass-embedded": "^1.98.0", + "reconnecting-websocket": "^4.4.0", + "sass-embedded": "^1.97.3", "truncate-middle": "^1.0.6", - "unplugin-auto-import": "^0.19.0", - "unplugin-vue-components": "^0.28.0", - "unplugin-vue-router": "^0.19.2", - "vite": "^5.4.21", + "unplugin-auto-import": "^21.0.0", + "unplugin-vue-components": "^31.0.0", + "vite": "^8.0.0", "vite-plugin-vuetify": "^2.1.3", - "vue": "^3.5.30", - "vue-i18n": "^10.0.8", - "vue-router": "^4.6.4", - "vue-tsc": "^2.2.12", - "vuetify": "^3.12.3" + "vue": "^3.5.28", + "vue-i18n": "^10.0.3", + "vue-router": "^5.0.3", + "vue-tsc": "^2.0.29", + "vuetify": "^4.0.2" } } diff --git a/ui/src/components/confirm-menu.vue b/ui/src/components/confirm-menu.vue index c6285a3..b8064a6 100644 --- a/ui/src/components/confirm-menu.vue +++ b/ui/src/components/confirm-menu.vue @@ -14,7 +14,6 @@ {{ title }} @@ -80,7 +79,7 @@ defineProps({ }, btnProps: { type: Object, - default: () => ({ color: 'warning', depressed: true }) + default: () => ({ color: 'warning', variant: 'flat' }) }, alert: { type: String as () => 'error' | 'warning', diff --git a/ui/src/components/device-card.vue b/ui/src/components/device-card.vue index ad1dc96..e5d4fc1 100644 --- a/ui/src/components/device-card.vue +++ b/ui/src/components/device-card.vue @@ -39,7 +39,7 @@ variant="text" @click="emit('test')" > - tester + Tester diff --git a/ui/src/components/subscribe-topic.vue b/ui/src/components/subscribe-topic.vue index 1745bfe..75a35bc 100644 --- a/ui/src/components/subscribe-topic.vue +++ b/ui/src/components/subscribe-topic.vue @@ -55,7 +55,7 @@ const { topic, icon, urlTemplate, - // eslint-disable-next-line vue/require-valid-default-prop + outputs = ['email', 'devices'], sender, noSender diff --git a/ui/src/components/subscribe-webhook.vue b/ui/src/components/subscribe-webhook.vue index 803d657..6635cee 100644 --- a/ui/src/components/subscribe-webhook.vue +++ b/ui/src/components/subscribe-webhook.vue @@ -7,7 +7,6 @@
diff --git a/ui/src/components/webhook-history.vue b/ui/src/components/webhook-history.vue index 833273e..15f7949 100644 --- a/ui/src/components/webhook-history.vue +++ b/ui/src/components/webhook-history.vue @@ -12,7 +12,7 @@ :loading="test.loading.value" @click="test.execute()" > - tester + Tester diff --git a/ui/src/components/webhook-subscription-form.vue b/ui/src/components/webhook-subscription-form.vue index 01c1574..4c375b2 100644 --- a/ui/src/components/webhook-subscription-form.vue +++ b/ui/src/components/webhook-subscription-form.vue @@ -1,7 +1,6 @@